.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.43) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Geo::Coordinates::OSGB 3pm" .TH Geo::Coordinates::OSGB 3pm "2022-12-06" "perl v5.36.0" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Geo::Coordinates::OSGB \- Convert coordinates between Lat/Lon and the British National Grid .PP An implementation of co\-ordinate conversion for England, Wales, and Scotland based on formulae and data published by the Ordnance Survey of Great Britain. .SH "VERSION" .IX Header "VERSION" 2.20 .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use Geo::Coordinates::OSGB qw(ll_to_grid grid_to_ll); \& \& ($easting,$northing) = ll_to_grid($lat,$lon); \& ($lat,$lon) = grid_to_ll($easting,$northing); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" These modules convert accurately between \s-1OSGB\s0 national grid references and coordinates given in latitude and longitude. .PP The default \*(L"ellipsoid model\*(R" used for the conversions is the \fIde facto\fR international standard \s-1WGS84.\s0 This means that you can take latitude and longitude readings from your \s-1GPS\s0 receiver, or read them from Wikipedia, or Google Earth, or your car's sat-nav, and use this module to convert them to accurate British National grid references for use with one of the Ordnance Survey's paper maps. And \fIvice versa\fR, of course. .PP The module is implemented purely in Perl, and should run on any platform with Perl version 5.8 or better. .PP In this description, the abbreviations `\s-1OS\s0' and `\s-1OSGB\s0' mean `the Ordnance Survey of Great Britain': the British government agency that produces the standard maps of England, Wales, and Scotland. Any mention of `sheets' or `maps' refers to one or more of the map sheets defined in the accompanying maps module. .PP This code is written for the British national grid system. It is of no use outside Britain. In fact it's only really useful in the areas covered by the \&\s-1OS\s0's main series of maps, which exclude the Channel Islands and Northern Ireland. .SH "SUBROUTINES/METHODS" .IX Header "SUBROUTINES/METHODS" The following functions can be exported from the \&\f(CW\*(C`Geo::Coordinates::OSGB\*(C'\fR module: .PP .Vb 2 \& grid_to_ll \& ll_to_grid .Ve .PP Neither of these is exported by default. .SS "Main subroutines" .IX Subsection "Main subroutines" \fI\f(CI\*(C`ll_to_grid(lat, lon)\*(C'\fI\fR .IX Subsection "ll_to_grid(lat, lon)" .PP \&\f(CW\*(C`ll_to_grid\*(C'\fR translates a latitude and longitude pair into a grid easting and northing pair. .PP When called in a list context, \f(CW\*(C`ll_to_grid\*(C'\fR returns the easting and northing as a list of two. When called in a scalar context, it returns a single string with the numbers separated by a space. .PP The arguments should be supplied as real numbers representing decimal degrees, like this: .PP .Vb 1 \& my ($e,$n) = ll_to_grid(51.5, \-2.1); # (393154.801, 177900.605) .Ve .PP Following the normal mathematical convention, positive arguments mean North or East, negative South or West. .PP If you have data with degrees, minutes, and seconds, you can convert them to decimals like this: .PP .Vb 1 \& my ($e,$n) = ll_to_grid(51+25/60, 0\-5/60\-2/3600); .Ve .PP If you have trouble remembering the order of the arguments, or the returned values, note that latitude comes before longitude in the alphabet too, as easting comes before northing. However, since reasonable latitudes for the \&\s-1OSGB\s0 are in the range 49 to 61, and reasonable longitudes in the range \-9 to +2, \f(CW\*(C`ll_to_grid\*(C'\fR accepts the arguments in either order; if your longitude is larger than your latitude, then the values of the arguments will be silently swapped. .PP You can also supply the arguments as named keywords (but be sure to use the curly braces so that you pass them as a reference): .PP .Vb 1 \& my ($e,$n) = ll_to_grid( { lat => 51.5, lon => \-2.1 } ); .Ve .PP The easting and northing will be returned as the orthogonal distances in metres from the `false point of origin' of the British Grid (which is a point some way to the south-west of the Scilly Isles). The returned pair refers to a point on the usual \s-1OSGB\s0 grid, which extends from the Scilly Isles in the south west to the Shetlands in the north. .PP .Vb 2 \& my ($e,$n) = ll_to_grid(51.5, \-2.1); # (393154.807, 177900.595) \& my $s = ll_to_grid(51.5, \-2.1); # "393154.807 177900.595" .Ve .PP If the coordinates you supply are in the area covered by the \s-1OSTN\s0 transformation data, then the results will be rounded to 3 decimal places, which corresponds to the nearest millimetre. If they are outside the coverage then the conversion is automagically done using a Helmert transformation instead of the \s-1OSTN\s0 data. The results will be rounded to the nearest metre in this case, although you probably should not rely on the results being more accurate than about 5m. .PP With the older \s-1OSTN02\s0 dataset, coverage extended only to about 3km offshore, but the current \s-1OSTN15\s0 dataset extends coverage to the whole grid area from (0,0) to (700000, 1250000), so you have be really far away to get whole metres. Even points well away from land, like this one: .PP .Vb 2 \& # A point in the sea, to the north\-west of Coll \& my $s = ll_to_grid(56.75,\-7); .Ve .PP will get an accurate conversion. With \s-1OSTN02\s0 that returned \f(CW\*(C`94471 773206\*(C'\fR but with \s-1OSTN15\s0 you get \f(CW\*(C`94469.597 773209.464\*(C'\fR. For your sake, I hope you are never in a situation at sea off Coll where the 3 metres difference is important. .PP The numbers returned may be negative if your latitude and longitude are far enough south and west, but beware that the transformation is less and less accurate or useful the further you get from the British Isles. .PP If you want the result presented in a more traditional grid reference format you should pass the results to one of the grid formatting routines from Grid.pm. Like this. .PP .Vb 4 \& my $s = ll_to_grid(51.5, \-2.1); # "393154.807 177900.595" \& $s = format_grid(ll_to_grid(51.5,\-2.1)); # "ST 931 779" \& $s = format_grid_GPS(ll_to_grid(51.5,\-2.1)); # "ST 93154 77900" \& $s = format_grid_map(ll_to_grid(51.5,\-2.1)); # "ST 931 779 on A:173, B:156, C:157" .Ve .PP \&\f(CW\*(C`ll_to_grid()\*(C'\fR also takes an optional argument that sets the ellipsoid model to use. This defaults to `\s-1WGS84\s0', the name of the normal model for working with normal \s-1GPS\s0 coordinates, but if you want to work with the traditional latitude and longitude values printed around the edges of \&\s-1OS\s0 maps before 2015 then you should add an optional shape parameter like this: .PP .Vb 1 \& my ($e, $n) = ll_to_grid(49,\-2, {shape => \*(AqOSGB36\*(Aq}); .Ve .PP Incidentally, if you make this call above you will get back \&\f(CW\*(C`(400000, \-100000)\*(C'\fR which are the coordinates of the `true point of origin' of the British grid. You should get back an easting of 400000 for any point with longitude 2W since this is the central meridian used for the \&\s-1OSGB\s0 projection. However you will get a slightly different value unless you specify \f(CW\*(C`{shape => \*(AqOSGB36\*(Aq}\*(C'\fR because the \s-1WGS84\s0 meridians are not quite the same as \s-1OSGB36.\s0 .PP \fI\f(CI\*(C`grid_to_ll(e,n)\*(C'\fI\fR .IX Subsection "grid_to_ll(e,n)" .PP The routine \f(CW\*(C`grid_to_ll()\*(C'\fR takes an easting and northing pair representing the distance in metres from the `false point of origin' of the \s-1OSGB\s0 grid and returns a pair of real numbers representing the equivalent longitude and latitude coordinates in the \s-1WGS84\s0 model. .PP Following convention, positive results are North of the equator and East of the prime meridian, negative numbers are South and West. The fractional parts of the results represent decimal fractions of degrees. .PP No special processing is done in scalar context because there is no obvious assumption about how to round the results. You will just get the length of the list returned, which is 2. .PP The arguments must be an (easting, northing) pair representing the absolute grid reference in metres from the point of origin. You can get these from a traditional grid reference string by calling \&\f(CW\*(C`parse_grid()\*(C'\fR first. .PP .Vb 1 \& my ($lat, $lon) = grid_to_ll(parse_grid(\*(AqSM 349 231\*(Aq)) .Ve .PP An optional last argument defines the ellipsoid model to use just as it does for \f(CW\*(C`ll_to_grid()\*(C'\fR. This is only necessary is you are working with an ellipsoid model other than \s-1WGS84.\s0 Pass the argument as a hash ref with a `shape' key. .PP .Vb 1 \& my ($lat, $lon) = grid_to_ll(400000, 300000, {shape => \*(AqOSGB36\*(Aq}); .Ve .PP If you like named arguments then you can use a single hash ref for all of them (this is strictly optional): .PP .Vb 1 \& my ($lat, $lon) = grid_to_ll({ e => 400000, n => 300000, shape => \*(AqOSGB36\*(Aq}); .Ve .PP The results returned will be floating point numbers with the default Perl precision. Unless you are running with long double precision floats you will get 13 decimal places for latitude and 14 places for longitude; but this does not mean that the calculations are accurate to that many places. The \s-1OS\s0 online conversion tools return decimal degrees to only 6 places. A difference of 1 in the sixth decimal place represents a distance on the ground of about 10 cm. This is probably a good rule of thumb for the reliability of these calculations, but all the available decimal places are returned so that you can choose the rounding that is appropriate for your application. Here's one way to do that: .PP .Vb 1 \& my ($lat, $lon) = map { sprintf "%.6f", $_ } grid_to_ll(431234, 312653); .Ve .SS "Additional subroutines" .IX Subsection "Additional subroutines" \fI\f(CI\*(C`set_default_shape(shape)\*(C'\fI\fR .IX Subsection "set_default_shape(shape)" .PP The default ellipsoid shape used for conversion to and from latitude and longitude is `\s-1WGS84\s0' as used in the international \s-1GPS\s0 system. This default is set every time that you load the module. If you want to process or produce a large number latitude and longitude coordinates in the British Ordnance Survey system (as printed round the edges of \s-1OS\s0 Landranger and Explorer maps before 2015) you can use \f(CW\*(C`set_default_shape(\*(AqOSGB36\*(Aq);\*(C'\fR to set the default shape to \s-1OSGB36.\s0 This saves you having to add \f(CW\*(C`{ shape => \*(AqOSGB36\*(Aq }\*(C'\fR to every call of \f(CW\*(C`ll_to_grid\*(C'\fR or \f(CW\*(C`grid_to_ll\*(C'\fR. .PP You can use \f(CW\*(C`set_default_shape(\*(AqWGS84\*(Aq);\*(C'\fR to set the default shape back to \s-1WGS84\s0 again when finished with \s-1OSGB36\s0 coordinates. .PP \fI\f(CI\*(C`ll_to_grid_helmert(lat, lon)\*(C'\fI\fR .IX Subsection "ll_to_grid_helmert(lat, lon)" .PP You can use this function to do a conversion from \s-1WGS84\s0 lat/lon to the \s-1OS\s0 grid without using the whole \s-1OSTN\s0 data set. The algorithm used is known as a Helmert transformation. This is the usual coordinate conversion algorithm implemented in most consumer-level \s-1GPS\s0 devices, which generally do not have enough memory space for the whole of \s-1OSTN.\s0 It is based on parameters supplied by the \s-1OS\s0; they suggest that in most of the \s-1UK\s0 this conversion is accurate to within about 5m. .PP .Vb 1 \& my ($e, $n) = ll_to_grid_helmert(51.477811, \-0.001475); # RO Greenwich .Ve .PP The input must be decimal degrees in the \s-1WGS84\s0 model, with latitude first and longitude second. The results are rounded to the nearest whole metre. They can be used with \f(CW\*(C`format_grid\*(C'\fR in the same way as the results from \f(CW\*(C`ll_to_grid\*(C'\fR. .PP This function is called automatically by \f(CW\*(C`ll_to_grid\*(C'\fR if your coordinates are \s-1WGS84\s0 and lie outside the \s-1OSTN\s0 polygon. .PP \fI\f(CI\*(C`grid_to_ll_helmert(e,n)\*(C'\fI\fR .IX Subsection "grid_to_ll_helmert(e,n)" .PP You can use this function to do a slightly quicker conversion from \s-1OS\s0 grid references to \s-1WGS84\s0 latitude and longitude coordinates without using the whole \s-1OSTN\s0 data set. The algorithm used is known as a Helmert transformation. This is the usual coordinate conversion algorithm implemented in most consumer-level \s-1GPS\s0 devices. It is based on parameters supplied by the \s-1OS\s0; they suggest that in most of the \s-1UK\s0 this conversion is accurate to within about 5m. .PP .Vb 1 \& my ($lat, $lon) = grid_to_ll_helmert(538885, 177322); .Ve .PP The input must be in metres from false point of origin (as produced by \&\f(CW\*(C`parse_grid\*(C'\fR) and the results are in decimal degrees using the \s-1WGS84\s0 model. .PP The results are returned with the full Perl precision in the same way as \&\f(CW\*(C`grid_to_ll\*(C'\fR so that you can choose an appropriate rounding for your needs. Four or five decimal places is probably appropriate in most cases. This represents somewhere between 1 and 10 m on the ground. .PP This function is called automatically by \f(CW\*(C`grid_to_ll\*(C'\fR if the grid reference you supply lies outside the \s-1OSTN\s0 polygon. (All such spots are far out to sea). The results are only useful close to mainland Britain. .PP \fIImporting all the functions\fR .IX Subsection "Importing all the functions" .PP You can import all the functions defined in \f(CW\*(C`OSGB.pm\*(C'\fR with an \f(CW\*(C`:all\*(C'\fR tag. .PP .Vb 1 \& use Geo::Coordinates::OSGB \*(Aq:all\*(Aq; .Ve .SH "EXAMPLES" .IX Header "EXAMPLES" .Vb 1 \& use Geo::Coordinates::OSGB qw/ll_to_grid grid_to_ll/; \& \& # Latitude and longitude according to the WGS84 model \& ($lat, $lon) = grid_to_ll($e, $n); \& \& # and to go the other way \& ($e, $n) = ll_to_grid($lat,$lon); .Ve .PP See the test files for more examples of usage. .SH "BUGS AND LIMITATIONS" .IX Header "BUGS AND LIMITATIONS" The formulae supplied by the \s-1OS\s0 and used for the conversion routines are specifically designed to be close floating-point approximations rather than exact mathematical equivalences. So after round-trips like these: .PP .Vb 2 \& ($lat1,$lon1) = grid_to_ll(ll_to_grid($lat0,$lon0)); \& ($e1,$n1) = ll_to_grid(grid_to_ll($e0,$n0)); .Ve .PP neither \f(CW\*(C`$lat1 == $lat0\*(C'\fR nor \f(CW\*(C`$lon1 == $lon0\*(C'\fR nor \f(CW\*(C`$e1 == $e0\*(C'\fR nor \&\f(CW\*(C`$n1 == $n0\*(C'\fR exactly. However the differences should be very small. .PP The \s-1OS\s0 formulae were designed to give an accuracy of about 1 mm of error. This means that you can rely on the third decimal place for grid references and about the seventh or eighth for latitude and longitude (although the \s-1OS\s0 themselves only provide six decimal places in their results). .PP For all of England, Wales, Scotland, and the Isle of Man the error will be tiny. All other areas, like Northern Ireland, the Channel Islands or Rockall, and any areas of sea more than a few miles off shore, are outside the coverage of \s-1OSTN,\s0 so the simpler, less accurate transformation is used. The \s-1OS\s0 state that this is accurate to about 5m but that the parameters used are only valid in the reasonably close vicinity of the British Isles. .PP Not enough testing has been done. I am always grateful for the feedback I get from users, but especially for problem reports that help me to make this a better module. .SH "DIAGNOSTICS" .IX Header "DIAGNOSTICS" The only error message you will get from this module is about the ellipsoid shape used for the transformation. If you try to set \f(CW\*(C`{shape => \*(Aqblah\*(Aq}\*(C'\fR the module will croak with a message saying \&\f(CW\*(C`Unknown shape: blah\*(C'\fR. The shape should be one of the shapes defined: \&\s-1WGS84\s0 or \s-1OSGB36.\s0 .PP Should this software not do what you expect, then please first read this documentation, secondly verify that you have installed it correctly and that it passes all the installation tests on your set up, thirdly study the source code to see what it's supposed to be doing, fourthly get in touch to ask me about it. .SH "CONFIGURATION AND ENVIRONMENT" .IX Header "CONFIGURATION AND ENVIRONMENT" There is no configuration required either of these modules or your environment. It should work on any recent version of Perl, on any platform. .SH "DEPENDENCIES" .IX Header "DEPENDENCIES" Perl 5.10 or better. .SH "INCOMPATIBILITIES" .IX Header "INCOMPATIBILITIES" None known. .SH "LICENSE AND COPYRIGHT" .IX Header "LICENSE AND COPYRIGHT" Copyright (C) 2002\-2017 Toby Thurston .PP \&\s-1OSTN\s0 transformation data included in this module is freely available from the Ordnance Survey but remains Crown Copyright (C) 2002 .PP This program is free software; you can redistribute it and/or modify it under the terms of the \s-1GNU\s0 General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. .PP This program is distributed in the hope that it will be useful, but \&\s-1WITHOUT ANY WARRANTY\s0; without even the implied warranty of \&\s-1MERCHANTABILITY\s0 or \s-1FITNESS FOR A PARTICULAR PURPOSE.\s0 See the \s-1GNU\s0 General Public License for more details. .PP You should have received a copy of the \s-1GNU\s0 General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, \s-1MA 02110\-1301 USA.\s0 .SH "AUTHOR" .IX Header "AUTHOR" Toby Thurston \*(-- 29 Oct 2017 .PP toby@cpan.org .SH "SEE ALSO" .IX Header "SEE ALSO" See Geo::Coordinates::OSGB::Grid for routines to format grid references. .PP The \s-1UK\s0 Ordnance Survey's explanations on their web pages. .PP See Geo::Coordinates::Convert for a general approach (not based on the \s-1OSGB\s0).