Git Product home page Git Product logo

convertbng.jl's Introduction

Introduction

A Julia library for fast conversion between WGS84 longitude and latitude and British National Grid (epsg:27700) coordinates. Conversions use a standard 7-element Helmert transform with the addition of OSTN15 corrections for accuracy.

This is a derived work of original repositories written by Stephan Hügel, which include a Rust binary lonlat_bng and the corresponding Python utility library convertbng. As Python is relatively slow, convertbng utilises a Rust binary to do the heavy lifting.

As Julia is JIT compiled, all of the conversions can remain within the same language and still exhibit good performance - this package has been written exclusively in Julia and has only a single dependency.

Accuracy

Conversions which solely use Helmert transforms are only accurate to approximately 5 metres and are not suitable for calculations or conversions where accuracy is important. To correct for these distortions in the OSGB36 terrestrial reference frame, Ordnance Survey has developed a ‘rubber-sheet’ transformation model called OSTN15. This model is available as grids of northing and easting shifts between ETRS89 and OSGB36 covering the UK at a one km resolution. Precision easting and northing shifts for each point can be obtained by a bilinear interpolation. See here for more information.

This package is able to convert all forty ETRS89 lon,lat OS test coordinates to OSGB36 eastings, northings with a maximum error of less than 2 mm, and complete the reverse transformation at an accuracy of a single digit at 8 d.p (~1 mm).

Installation

This package requires Julia v1.0.5 as a minimum and it is recommended that Julia v1.3.1 onwards is used. As this package is currently unregistered, simply enter the following in Pkg mode:

Pkg> add https://github.com/joshuanunn/ConvertBNG.jl

Multi-Threading for High Performance

Multi-threading is used automatically when passing arrays of coordinates to the convert_bng and convert_lonlat functions. However, by default Julia only utilises a single thread so calculations will be limited to a single CPU core. If high performance is desired, the environment variable JULIA_NUM_THREADS should be set to the match the number of cores available. Note that this must be set before launching the Julia REPL.

For example, enter set JULIA_NUM_THREADS=4 into the Windows command prompt or export JULIA_NUM_THREADS=4 into the Linux terminal. These settings only last the length of the terminal session, so you should launch Julia from the same terminal afterwards. If you want this setting to remain persistent, change the user environment variable in the Windows control panel (User Accounts -> Change my environment variables) or add the above export command to your .bashrc file or equivalent in Linux. See the Benchmarks section below for timings.

If you already have the environment variable JULIA_NUM_THREADS set to > 1, but you only want to use this package in a single threaded mode, simply call the equivalent convert_bng_nothread or convert_lonlat_nothread functions with an array of coordinates.

Use

Following installation, import the package to make all functions available:

julia> using ConvertBNG

The primary functions available in this package are:

  • convert_bng Perform Lon, Lat to OSGB36 Eastings, Northings conversion, using OSTN15 data
  • convert_lonlat Convert OSGB36 Eastings, Northing coordinates to Lon, Lat using OSTN15 data

Coordinates can be passed to these functions as a single pair or as a 2-column array of any length. Transformed coordinates are returned as a tuple for a single point conversion or as a 2-column array for multiple point conversion. Note that lon, lat coordinates outside the UK bounding box will be transformed to (NaN NaN), which cannot be mapped.

Can be used to transform single points:

julia> e,n = convert_bng(-2.594679, 51.450521)
(358772.71, 172560.997)

julia> e
358772.71

julia> n
172560.997

Or an array of points:

julia> lats_lons = [-5.003508 56.79685; -3.211528 54.454222; -4.076231 53.068497]
3×2 Array{Float64,2}:
 -5.003508  56.79685
 -3.211528  54.454222
 -4.076231  53.068497

julia> convert_bng(lats_lons)
3×2 Array{Float64,2}:
 216676.483  771283.429
 321549.542  507211.985
 260985.952  354375.893
 
julia> convert_lonlat(convert_bng(lats_lons))
3×2 Array{Float64,2}:
 -5.003508    56.79685
 -3.211528    54.454222
 -4.076231    53.068497

In Detail

The above conversions can be broken into their component steps by using the following piecewise functions:

  • convert_bng is equivalent to:

    1. Perform Longitude, Latitude to ETRS89 conversion: easting, northing = convert_etrs89(longitude, latitude)
    2. Perform ETRS89 to OSGB36 conversion, using OSTN15 data: easting_corr, northing_corr = convert_etrs89_to_osgb36(easting, northing)
  • convert_lonlat is equivalent to:

    1. Convert OSGB36 coordinates to ETRS89, using OSTN15 data: x, y = convert_osgb36_to_etrs89(easting, northing)
    2. Convert ETRS89 coordinates to Lon, Lat: longitude, latitude = convert_etrs89_to_ll(x, y)

Using the Caister Water Tower example in the OS Transformations and OSGM15 User Guide - Annexe A, we can describe this location using the following coordinates:

  • ETRS89 longitude, latitude: (1.716073973, 52.658007833)
  • ETRS89 eastings, northings: (651307.003, 313255.686)
  • OSGB36 eastings, northings: (651409.804, 313177.450)

All of these coordinates can be interrelated using the following function calls, which give the corresponding return values:

julia> convert_bng(1.716073973, 52.658007833)             == (651409.804 313177.450)
julia> convert_etrs89(1.716073973, 52.658007833)          == (651307.003 313255.686)
julia> convert_etrs89_to_osgb36(651307.003, 313255.686)   == (651409.804 313177.450)

julia> convert_lonlat(651409.804, 313177.450)             == (1.71607397 52.65800783)
julia> convert_osgb36_to_etrs89(651409.804, 313177.450)   == (651307.003 313255.686)
julia> convert_etrs89_to_ll(651307.003, 313255.686)       == (1.71607397 52.65800783)

The difference between ETRS89 and OSGB36 is determined by extracting the raw OSTN15 shifts for the area of interest and using bilinear interpolation to derive a more accurate set of shifts. For this example the raw shifts are (102.787, -78.242, 44.236), which are subsequently refined to (102.801, -78.236, 44.228).

Benchmarks

Benchmarks were run on two different systems - a high performance Linux system with an 8-core i7-9700K CPU and a mainstream Windows 10 system with a 4-core i5-7500 CPU. The benchmark code is derived from that used in the lonlat_bng Rust library and involves getting the total time for converting 50 lots of 1 million random lon,lat pairs to BNG and vice versa. The code can be run in the Julia REPL: include("/path/to/package/benchmark/runbenchmarks.jl").

  • The benchmarks were run a number of times to ensure that the total times for each case were consistent and the best time from any run is quoted in the results tables below.
  • Julia versions v1.0.5, v1.3.1 and v1.4.0 were tested - all quoted times are those using Julia v1.4.0.
  • Note that Julia v1.0.5 times were upto 25% slower than the later veriosns and inconsistent - it is recommended to use later versions.
  • All Julia benchmarks were run with multi-threading, using 1, 4 or 8 CPU cores for comparison.
  • For context, the tables also include the equivalent benchmark times for the convertbng library which uses a Rust binary. This has been run in both exposed operational modes (Ctypes and Cython). These binaries are setup to fully utilise the CPU and so times are only shown for use of all cores.

Results for 8-Core i7-9700K CPU @ 3.60GHz

Function Threads/Cores Julia (s) Rust Ctypes (s) Rust Cython (s)
convert_bng 1 11.8 - -
convert_bng 4 3.1 - -
convert_bng 8 1.6 3.3 2.0
convert_latlon 1 19.9 - -
convert_latlon 4 5.2 - -
convert_latlon 8 2.7 6.3 3.2

Results for 4-Core i5-7500 CPU @ 3.40GHz

Function Threads/Cores Julia (s) Rust Ctypes (s) Rust Cython (s)
convert_bng 1 16.0 - -
convert_bng 4 4.4 8.5 5.5
convert_latlon 1 26.8 - -
convert_latlon 4 7.3 16.2 9.8

Tests

After installation, the unit tests can (and should) be run by entering the following at the Julia REPL:

julia> using Pkg; Pkg.test("ConvertBNG")

License

MIT

This software is a derived work of the Rust library lonlat_bng, released by Stephan Hügel under the MIT license.

This software makes use of OSTN15 data, which is © Copyright and database rights Ordnance Survey Limited 2016. All rights reserved. Provided under the BSD 2-clause license.

convertbng.jl's People

Contributors

anowacki avatar joshuanunn avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar

Forkers

anowacki

convertbng.jl's Issues

Merging with existing package?

Congratulations on an accurate and pure-Julia package!

In case you are thinking about registering the package, I hope you don't mind if I make an enquiry of you.

I wrote BritishNationalGrid.jl some time ago as I had a need to convert lots of BNG points (some with the square two-letter references) to WGS84, but didn't need great absolute accuracy. I was then persuaded to register the package. ('BNG.jl' wasn't at the time considered an acceptable name since the acronym may have multiple meanings and globally recognition of it is weak.) However, I'm not an expert in this and my package simply relied on Proj4 to do a job to about 1 m. You clearly are much more knowledgable about coordinate transformations with the BNG, have a need to perform lots of them quickly, and I suspect you are therefore much better placed to continue development of a package to do just that.

I think it would be a shame if the community couldn't indicate that one single package is the optimal way to convert between WGS84 and BNG coordinates, so I thought it might be helpful to make a suggestion.

There is currently no way (nor should there be) to remove BritishNationalGrid.jl from the General registry, and it will therefore be there for the foreseeable future. With that knowledge, I wonder if you would either like to contribute your code to BritishNationalGrid.jl, or perhaps take ownership of the package entirely? (Or I could add it to the JuliaGeo organisation, to which you could be added?) It simply seems like a good opportunity to retire the existing sub-optimal code.

Of course, you may have no interest in doing this—you might have no plans to register the package, or for lots of other good reasons want complete control of all aspects, all of which are completely understandable. But I thought I would ask, just in case.

Thanks again for this very welcome addition to the Julia ecosystem!

(Or—I wonder if really it should all go in Geodesy.jl?)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.