.\" Automatically generated by Pod::Man 4.09 (Pod::Simple 3.35) .\" .\" 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 .. .if !\nF .nr F 0 .if \nF>0 \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} .\} .\" ======================================================================== .\" .IX Title "Geo::Coordinates::OSGB::Grid 3pm" .TH Geo::Coordinates::OSGB::Grid 3pm "2018-07-23" "perl v5.26.2" "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::Grid \- Format and parse British National Grid references .SH "VERSION" .IX Header "VERSION" 2.20 .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use Geo::Coordinates::OSGB::Grid qw/parse_grid format_grid/; \& \& my ($e,$n) = parse_grid(\*(AqTQ 23451 09893\*(Aq); \& my $gr = format_grid($e, $n); # "TQ 234 098" .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module provides useful functions for parsing and formatting \s-1OSGB\s0 grid references. Some detailed background is given in \f(CW\*(C`background.pod\*(C'\fR and on the \s-1OS\s0 web site. .SH "SUBROUTINES AND METHODS" .IX Header "SUBROUTINES AND METHODS" .ie n .SS """format_grid(e, n)""" .el .SS "\f(CWformat_grid(e, n)\fP" .IX Subsection "format_grid(e, n)" Formats an (easting, northing) pair into traditional `full national grid reference' with two letters and two sets of three numbers, like this `\s-1SU 387 147\s0'. .PP .Vb 1 \& $gridref = format_grid(438710.908, 114792.248); # SU 387 147 .Ve .PP If you want the individual components call it in a list context. .PP .Vb 1 \& ($sq, $e, $n) = format_grid(438710.908, 114792.248); # (\*(AqSU\*(Aq, 387, 147) .Ve .PP Note that rather than being rounded, the easting and northing are \&\fBtruncated\fR to hectometres (as the \s-1OS\s0 system demands), so the grid reference refers to the lower left corner of the relevant 100m square. The system is described below the legend on all \s-1OS\s0 Landranger maps. .PP The format grid routine takes an optional third argument to control the form of grid reference returned. This should be a hash reference with one or more of the keys shown below (with the default values). .PP .Vb 1 \& format_grid(e, n, {form => \*(AqSS EEE NNN\*(Aq, maps => 0, series => \*(AqABCHJ\*(Aq}) .Ve .PP \fIOptions for \f(CI\*(C`format_grid\*(C'\fI\fR .IX Subsection "Options for format_grid" .IP "form" 4 .IX Item "form" Controls the format of the grid reference. With \f(CW\*(C`$e, $n\*(C'\fR set as above: .Sp .Vb 8 \& Format produces Format produces \& \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \& \*(AqSS\*(Aq SU \& \*(AqSSEN\*(Aq SU31 \*(AqSS E N\*(Aq SU 3 1 \& \*(AqSSEENN\*(Aq SU3814 \*(AqSS EE NN\*(Aq SU 38 14 \& \*(AqSSEEENNN\*(Aq SU387147 \*(AqSS EEE NNN\*(Aq SU 387 147 \& \*(AqSSEEEENNNN\*(Aq SU38711479 \*(AqSS EEEE NNNN\*(Aq SU 3871 1479 \& \*(AqSSEEEEENNNNN\*(Aq SU3871014792 \*(AqSS EEEEE NNNNN\*(Aq SU 38710 14792 .Ve .Sp You can't leave out the \s-1SS,\s0 you can't have N before E, and there must be the same number of Es and Ns. .Sp There are two other special formats: .Sp .Vb 2 \& form => \*(AqTRAD\*(Aq is equivalent to form => \*(AqSS EEE NNN\*(Aq \& form => \*(AqGPS\*(Aq is equivalent to form => \*(AqSS EEEEE NNNNN\*(Aq .Ve .Sp In a list context, this option means that the individual components are returned appropriately truncated as shown. So with \f(CW\*(C`SS EEE NNN\*(C'\fR you get back \f(CW\*(C`(\*(AqSU\*(Aq, 387, 147)\*(C'\fR and \fBnot\fR \f(CW\*(C`(\*(AqSU\*(Aq, 387.10908, 147.92248)\*(C'\fR. The format can be given as upper case or lower case or a mixture. If you want just the local easting and northing without the grid square, get the individual parts in a list context and format them yourself: .Sp .Vb 2 \& my $gr = sprintf(\*(AqGrid ref %2$s %3$s on Sheet %4$s\*(Aq, format_grid_landranger($e, $n)) \& # returns: Grid ref 387 147 on Sheet 196 .Ve .IP "maps" 4 .IX Item "maps" Controls whether to include a list of map sheets after the grid reference. Set it to 1 (or any true value) to include the list, and to 0 (or any false value) to leave it out. The default is \f(CW\*(C`maps => 0\*(C'\fR. .Sp In a scalar context you get back a string like this: .Sp .Vb 1 \& SU 387 147 on A:196, B:OL22E, C:180 .Ve .Sp In a list context you get back a list like this: .Sp .Vb 1 \& (\*(AqSU\*(Aq, 387, 147, A:196, B:OL22E, C:180) .Ve .IP "series" 4 .IX Item "series" This option is only used when \f(CW\*(C`maps\*(C'\fR is true. It controls which series of maps to include in the list of sheets. Currently the series included are: .Sp \&\f(CW\*(C`A\*(C'\fR : \s-1OS\s0 Landranger 1:50000 maps .Sp \&\f(CW\*(C`B\*(C'\fR : \s-1OS\s0 Explorer 1:25000 maps (some of these are designated as `Outdoor Leisure' maps) .Sp \&\f(CW\*(C`C\*(C'\fR : \s-1OS\s0 Seventh Series One-Inch 1:63360 maps .Sp \&\f(CW\*(C`H\*(C'\fR : Harvey British Mountain maps \- mainly at 1:40000 .Sp \&\f(CW\*(C`J\*(C'\fR : Harvey Super Walker maps \- mainly at 1:25000 .Sp so if you only want Explorer maps use: \f(CW\*(C`series => \*(AqB\*(Aq\*(C'\fR, and if you want only Explorers and Landrangers use: \f(CW\*(C`series => \*(AqAB\*(Aq\*(C'\fR, and so on. .Sp Note that the numbers returned for the Harvey maps have been invented for the purposes of this module. They do not appear on the maps themselves; instead the maps have titles. You can use the numbers returned as an index to the data in Geo::Coordinates::OSGB::Maps to find the appropriate title. .ie n .SS """format_grid_trad(e,n)""" .el .SS "\f(CWformat_grid_trad(e,n)\fP" .IX Subsection "format_grid_trad(e,n)" Equivalent to \f(CW\*(C`format_grid(e,n, { form => \*(Aqtrad\*(Aq })\*(C'\fR. .ie n .SS """format_grid_GPS(e,n)""" .el .SS "\f(CWformat_grid_GPS(e,n)\fP" .IX Subsection "format_grid_GPS(e,n)" Equivalent to \f(CW\*(C`format_grid(e,n, { form => \*(Aqgps\*(Aq })\*(C'\fR. .ie n .SS """format_grid_map(e,n)""" .el .SS "\f(CWformat_grid_map(e,n)\fP" .IX Subsection "format_grid_map(e,n)" Equivalent to \f(CW\*(C`format_grid(e,n, { maps => 1 })\*(C'\fR. .ie n .SS """format_grid_landranger(e,n)""" .el .SS "\f(CWformat_grid_landranger(e,n)\fP" .IX Subsection "format_grid_landranger(e,n)" Equivalent to .PP .Vb 1 \& format_grid(e,n,{ form => \*(Aqss eee nnn\*(Aq, maps => 1, series => \*(AqA\*(Aq }) .Ve .PP except that the leading \*(L"A:\*(R" will be stripped from any sheet names returned, and you get a slightly fancier set of phrases in a scalar context depending on how many map numbers are in the list of sheets. .ie n .SS """parse_grid""" .el .SS "\f(CWparse_grid\fP" .IX Subsection "parse_grid" The \f(CW\*(C`parse_grid\*(C'\fR routine extracts an (easting, northing) pair from a string or a list of arguments representing a grid reference. The pair returned are in units of metres from the false origin of the grid, so that you can pass them to \f(CW\*(C`format_grid\*(C'\fR or \f(CW\*(C`grid_to_ll\*(C'\fR. .PP The arguments should be in one of the following forms .IP "\(bu" 4 A single string representing a grid reference .Sp .Vb 4 \& String \-> interpreted as \& \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \& parse_grid("TA 123 678") \-> (512300, 467800) \& parse_grid("TA 12345 67890") \-> (512345, 467890) .Ve .Sp The spaces are optional in all cases. You can also refer to a 100km square as \f(CW\*(C`TA\*(C'\fR which will return \f(CW\*(C`(500000,400000)\*(C'\fR, a 10km square as \&\f(CW\*(C`TA16\*(C'\fR which will return \f(CW\*(C`(510000, 460000)\*(C'\fR, or to a kilometre square as \f(CW\*(C`TA1267\*(C'\fR which gives \f(CW\*(C`(512000, 467000)\*(C'\fR. For completeness you can also use \f(CW\*(C`TA 1234 6789\*(C'\fR to refer to a decametre square \f(CW\*(C`(512340, 467890)\*(C'\fR but you might struggle to find a use for that one. .IP "\(bu" 4 A list representing a grid reference .Sp .Vb 7 \& List \-> interpreted as \& \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \& parse_grid(\*(AqTA\*(Aq, 0, 0) \-> (500000, 400000) \& parse_grid(\*(AqTA\*(Aq, 123, 678) \-> (512300, 467800) \& parse_grid(\*(AqTA\*(Aq, 12345, 67890) \-> (512345, 467890) \& parse_grid(\*(AqTA\*(Aq, \*(Aq123 678\*(Aq) \-> (512300, 467800) \& parse_grid(\*(AqTA\*(Aq, \*(Aq12345 67890\*(Aq) \-> (512345, 467890) .Ve .Sp If you are processing grid references from some external data source beware that if you use a list with bare numbers you may lose any leading zeros for grid references close to the \s-1SW\s0 corner of a grid square. This can lead to some ambiguity. Either make the numbers into strings to preserve the leading digits or supply a hash of options as a fourth argument with the `figs' option to define how many figures are supposed to be in each easting and northing. Like this: .Sp .Vb 4 \& List \-> interpreted as \& \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \& parse_grid(\*(AqTA\*(Aq, 123, 8) \-> (512300, 400800) \& parse_grid(\*(AqTA\*(Aq, 123, 8, { figs => 5 }) \-> (500123, 400008) .Ve .Sp The default setting of figs is 3, which assumes you are using hectometres as in a traditional grid reference. .IP "\(bu" 4 A string or list representing a map sheet and a grid reference on that sheet .Sp .Vb 7 \& Map input \-> interpreted as \& \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \& parse_grid(\*(AqA:164/352194\*(Aq) \-> (435200, 219400) \& parse_grid(\*(AqB:OL43E/914701\*(Aq) \-> (391400, 570100) \& parse_grid(\*(AqB:OL43E 914 701\*(Aq) \-> (391400, 570100) \& parse_grid(\*(AqB:OL43E\*(Aq,\*(Aq914701\*(Aq) \-> (391400, 570100) \& parse_grid(\*(AqB:OL43E\*(Aq,914,701) \-> (391400, 570100) .Ve .Sp Again spaces are optional, but you need some non-digit between the map identifier and the grid reference. There are also some constraints: the map identifier must be one defined in Geo::Coordinates::OSGB::Maps; and the following grid reference must actually be on the given sheet. Note also that you need to supply a specific sheet for a map that has more than one. The given example would fail if the map was given as `B:OL43', since that map has two sheets: `B:OL43E' and `B:OL43W'. .Sp If you give the identifier as just a number, it's assumed that you wanted a Landranger map; .Sp .Vb 2 \& parse_grid(\*(Aq176/224711\*(Aq) \-> (522400, 171100) \& parse_grid(164,513,62) \-> (451300, 206200) .Ve .Sp \&\f(CW\*(C`parse_grid\*(C'\fR will croak of you pass it a sheet identifier that is not defined in Geo::Coordinates::OSGB::Maps. It will also croak if the supplied easting and northing are not actually on the sheet. .Sp If you just want the corner of a particular map, just pass the sheet name: .Sp .Vb 2 \& parse_grid(\*(AqA:82\*(Aq) \-> (195000, 530000) \& parse_grid(161) \-> (309000, 205000) .Ve .Sp Again, it's assumed that you want a Landranger map. The grid reference returned is the \s-1SW\s0 corner of the particular sheet. This is usually obvious, but less so for some of the oddly shaped 1:25000 sheets, or Harvey's maps. What you actually get is the first point defined in the maps polygon, as defined in Maps. If in doubt you should work directly with the data in Geo::Coordinates::OSGB::Maps. .ie n .SS """parse_trad_grid(grid_ref)""" .el .SS "\f(CWparse_trad_grid(grid_ref)\fP" .IX Subsection "parse_trad_grid(grid_ref)" This is included only for backward compatibility. It is now just a synonym for \f(CW\*(C`parse_grid\*(C'\fR. .ie n .SS """parse_GPS_grid(grid_ref)""" .el .SS "\f(CWparse_GPS_grid(grid_ref)\fP" .IX Subsection "parse_GPS_grid(grid_ref)" This is included only for backward compatibility. It is now just a synonym for \f(CW\*(C`parse_grid\*(C'\fR. .ie n .SS """parse_landranger_grid(sheet, e, n)""" .el .SS "\f(CWparse_landranger_grid(sheet, e, n)\fP" .IX Subsection "parse_landranger_grid(sheet, e, n)" This is included only for backward compatibility. It is now just a synonym for \f(CW\*(C`parse_grid\*(C'\fR. .ie n .SS """parse_map_grid(sheet, e, n)""" .el .SS "\f(CWparse_map_grid(sheet, e, n)\fP" .IX Subsection "parse_map_grid(sheet, e, n)" This is included only for backward compatibility. It is now just a synonym for \f(CW\*(C`parse_grid\*(C'\fR. .ie n .SS """random_grid([sheet1, sheet2, ...])""" .el .SS "\f(CWrandom_grid([sheet1, sheet2, ...])\fP" .IX Subsection "random_grid([sheet1, sheet2, ...])" Takes an optional list of map sheet identifiers, and returns a random easting and northing for some place covered by one of the maps. There's no guarantee that the point will not be in the sea, but it will be within the bounding box of one of the maps. .IP "\(bu" 4 If you omit the list of sheets, then one of map sheets defined in Geo::Coordinates::OSGB::Maps will be picked at random. .IP "\(bu" 4 As a convenience whole numbers in the range 1..204 will be interpreted as Landranger sheets, as if you had written \f(CW\*(C`A:1\*(C'\fR, \f(CW\*(C`A:2\*(C'\fR, etc. .IP "\(bu" 4 Any sheet identifiers in the list that are not defined in Geo::Coordinates::OSGB::Maps will be (silently) ignored. .IP "\(bu" 4 The easting and northing are returned as meters from the grid origin, so that they are suitable for input to the \f(CW\*(C`format_grid\*(C'\fR routines. .SH "EXAMPLES" .IX Header "EXAMPLES" .Vb 4 \& use Geo::Coordinates::OSGB::Grid \& qw/parse_grid \& format_grid \& format_grid_landranger/; \& \& # Get full coordinates in metres from GR \& my ($e,$n) = parse_grid(\*(AqTQ 23451 09893\*(Aq); \& \& # Reading and writing grid references \& # Format full easting and northing into traditional formats \& my $gr1 = format_grid($e, $n); # "TQ 234 098" \& my $gr2 = format_grid($e, $n, { form => \*(AqSSEEENNN\*(Aq } ); # "TQ234098" \& my $gr3 = format_grid($e, $n, { form => \*(AqSSEEEEENNNNN\*(Aq} ); # "TQ 23451 09893" \& my $gr4 = format_grid($e, $n, { form => \*(Aqgps\*(Aq} ); # "TQ 23451 09893" \& my $gr5 = format_grid_landranger($e, $n);# "TQ 234 098 on Landranger sheet 198" \& \& # or call in list context to get the individual parts \& my ($sq, $ee, $nn) = format_grid($e, $n); # (\*(AqTQ\*(Aq, 234, 98) \& \& # parse routines to convert from these formats to full e,n \& ($e,$n) = parse_grid(\*(AqTQ 234 098\*(Aq); \& ($e,$n) = parse_grid(\*(AqTQ234098\*(Aq); # spaces optional \& ($e,$n) = parse_grid(\*(AqTQ\*(Aq,234,98); # or even as a list \& ($e,$n) = parse_grid(\*(AqTQ 23451 09893\*(Aq); # as above.. \& \& # You can also get grid refs from individual maps. \& # Sheet between 1..204; gre & grn must be 3 or 5 digits long \& ($e,$n) = parse_grid(176,123,994); \& # put leading zeros in quotes \& ($e,$n) = parse_grid(196,636,\*(Aq024\*(Aq); .Ve .PP For more examples of parsing and formatting look at the test files. .SH "BUGS AND LIMITATIONS" .IX Header "BUGS AND LIMITATIONS" The useful area of these routines is confined to the British Isles, not including Ireland or the Channel Islands. But very little range checking is done, so you can generate pseudo grid references for points that are some way outside this useful area. For example we have St Peter Port in Guernsey at \&\f(CW\*(C`XD 611 506\*(C'\fR and Rockall at \f(CW\*(C`MC 035 165\*(C'\fR. The working area runs from square \&\f(CW\*(C`AA\*(C'\fR in the far north west to \f(CW\*(C`ZZ\*(C'\fR in the far south east. In \s-1WGS84\s0 terms the corners run from 64.75N 32.33W (Iceland) to 65.8N 22.65E (Norway) to 44.5N 11.8E (Venice) to 44N 19.5W (the Western Approaches). This is something of a geodesy toy rather than a useful function. .SH "DIAGNOSTICS" .IX Header "DIAGNOSTICS" .ie n .SS "Messages from ""format_grid""" .el .SS "Messages from \f(CWformat_grid\fP" .IX Subsection "Messages from format_grid" In case of error \f(CW\*(C`format_grid\*(C'\fR will die with a message. Possible messages are: .IP "\(bu" 4 Format ... was not recognized .Sp The format code you supplied with \f(CW\*(C`{ form => ... }\*(C'\fR did not match any of the expected patterns. .IP "\(bu" 4 Too far off the grid: ... .Sp The (easting, northing) pair you supplied are too far away from the \s-1OS\s0 grid to be formatted with a valid grid square letter combination. .ie n .SS "Messages from ""parse_grid""" .el .SS "Messages from \f(CWparse_grid\fP" .IX Subsection "Messages from parse_grid" In case of error \f(CW\*(C`parse_grid\*(C'\fR will die with one of the following messages: .IP "\(bu" 4 No easting or northing found .Sp This means you passed something more than a 2\-letter grid square but there were no numbers found in the latter part of the string. .IP "\(bu" 4 Easting and northing have different lengths in ... .Sp The easting and northing you supply must have same length to avoid ambiguity. .IP "\(bu" 4 Too many digits in ... .Sp You have supplied more than 10 digits. .IP "\(bu" 4 Grid reference .... is not on sheet ... .Sp You can get this if you pass a map sheet identifier and a short grid ref, but the grid ref is not actually on that particular sheet. .IP "\(bu" 4 Failed to parse a grid reference from ... .Sp This is the catch all message issued if none of the patterns matches your input. .PP If you get an unexpected result from any of these subroutines, please generate a test case to reproduce your result and 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 better than 5.10, 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-1OSTN02\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 \*(-- 30 Jul 2017 .PP toby@cpan.org .SH "SEE ALSO" .IX Header "SEE ALSO" See Geo::Coordinates::OSGB.