An attempt at speeding up the conversion between decimal longitude and latitude and British National Grid (epsg:27700) coordinates, using an external Rust binary and Python FFI.
Python is relatively slow; this type of conversion is usually carried out in bulk, so an order-of-magnitude improvement could save precious minutes
Add the following to your Cargo.toml
(You'll have to look up the latest version on crates.io)
lonlat_bng = "0.1.9"
Full library documentation is available here
Note that lon
, lat
coordinates outside the UK bounding box will be transformed to (9999, 9999)
, which cannot be mapped.
The native functions exposed by the library are:
lonlat_bng::convert_bng(&f32, &f32) -> (i32, i32)
lonlat_bng::convert_lonlat(&i21 &i32) -> (f32, f32)
lonlat_bng::convert_to_bng_threaded_vec(Vec<(&f32, &f32)>) -> Vec<(i32, i32)>
lonlat_bng::convert_to_lonlat_threaded_vec(Vec<(&i32, &i32)>) -> Vec<(f32, f32)>
The FFI C-compatible functions exposed by the library are:
convert_to_bng_threaded(Array, Array) -> Array
convert_to_lonlat_threaded(Array, Array) -> Array
And for freeing the memory allocated by the above
drop_int_array(Array) -> Null
drop_float_array(Array) -> Null
The Array
s must contain 32-bit Float
s and 32-bit Int
s, respectively. For examples, see the Array
struct and tests in lib.rs, and the _BNG_FFIArray
class in convertbng.
If your FFI library implements convert_to_bng_threaded
, it must also implement drop_int_array
, and if it implements convert_to_lonlat_threaded
, it must implement drop_float_array
. Failing to do so will result in memory leaks.
Running cargo build --release
will build an artefact called liblonlat_bng.dylib
on OSX, and liblonlat_bng.a
on *nix
systems. Note that you'll have to generate liblonlat_bng.so
for *nix
hosts using the following steps:
ar -x target/release/liblonlat_bng.a
gcc -shared *.o -o target/release/liblonlat_bng.so -lrt
convert_bng
is available from PyPI for OSX and *nix:
pip install convertbng
More information is available in its repository
An IPython (sorry, Jupyter) notebook with some benchmarks is here
cargo build --release
from the repo rootipython notebook
, and open rust_BNG
.Test machine:
- Late-2012 27" iMac
- 3.4 GHz Intel Core i7
- 4 cores (8 logical CPUs)
- 16GB 1600 MHz DDR3 RAM
| Coordinates | Method | Time (ms) | Speedup | |:---------------|:------:|:---------:|--------:| | 100k (50 runs) | Python | 547 | N/A | | |Rust| 73.9 |7.4x | | | Pyproj | 57.2 | 9.5x | | 1mm (50 runs) | Python | 5560 | N/A | | |Rust| 624 |8.9x | | | Pyproj | 510 | 10.9x | | 10mm (50 runs) | Python | 54500 | N/A | | |Rust| 6360 |8.6x | | | Pyproj | 4710 | 11.5 |
Using multithreading gives excellent performance (Pyproj – which is a compiled Cython binary – is now only ~20% faster than Rust, on average). Not bad, considering the relative youth of Rust as a language (let alone this library), and the maturity of the PROJ.4 project.
The Helmert transform used is accurate to within 7 metres on average, so this library is not suitable for calculations used in e.g. surveying. If higher accuracy is required, please use a product which incorporates the OSTN02 calculations, which adjust for local variation within the Terrestrial Reference Frame. See here for more information.
MIT