File Coverage

blib/lib/Geo/Coordinates/OSGB.pm
Criterion Covered Total %
statement 248 248 100.0
branch 41 48 85.4
condition 11 15 73.3
subroutine 30 30 100.0
pod 5 5 100.0
total 335 346 96.8


line stmt bran cond sub pod time code
1             package Geo::Coordinates::OSGB;
2 8     8   255305 use base qw(Exporter);
  8         65  
  8         1003  
3 8     8   51 use strict;
  8         15  
  8         170  
4 8     8   37 use warnings;
  8         11  
  8         205  
5 8     8   42 use Carp;
  8         15  
  8         519  
6 8     8   2006 use File::Share ':all';
  8         51205  
  8         1436  
7 8     8   194 use 5.010; # at least Perl 5.10 please
  8         31  
8              
9             our $VERSION = '2.20';
10              
11             our %EXPORT_TAGS = (
12             all => [
13             qw(
14             ll_to_grid
15             grid_to_ll
16              
17             ll_to_grid_helmert
18             grid_to_ll_helmert
19              
20             get_ostn02_shift_pair
21             set_default_shape
22             )
23             ]
24             );
25              
26             our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } );
27              
28 8         1075 use constant ELLIPSOIDS => {
29             WGS84 => [ 6_378_137.000, 6_356_752.31424518, 298.257223563, 0.006694379990141316996137233540 ],
30             ETRS89 => [ 6_378_137.000, 6_356_752.314140, 298.257222101, 0.006694380022900787625359114703 ],
31             GRS80 => [ 6_378_137.000, 6_356_752.314140, 298.257222101, 0.006694380022900787625359114703 ],
32             OSGB36 => [ 6_377_563.396, 6_356_256.909, 299.3249612665, 0.0066705400741492318211148938735613129751683486352306 ],
33 8     8   56 };
  8         18  
34              
35             my $default_shape = 'WGS84';
36              
37             sub set_default_shape {
38 2     2 1 7 my $s = shift;
39 2 50       9 croak "Unknown shape: $s" if !exists ELLIPSOIDS->{$s};
40 2         7 $default_shape = $s;
41 2         5 return;
42             }
43              
44             # constants for OSGB mercator projection
45 8     8   56 use constant ORIGIN_LONGITUDE => -2 / 57.29577951308232087679815481410517;
  8         16  
  8         480  
46 8     8   57 use constant ORIGIN_LATITUDE => 49 / 57.29577951308232087679815481410517;
  8         21  
  8         441  
47 8     8   65 use constant ORIGIN_EASTING => 400_000;
  8         26  
  8         409  
48 8     8   51 use constant ORIGIN_NORTHING => -100_000;
  8         17  
  8         389  
49 8     8   46 use constant CONVERGENCE_FACTOR => 0.9996012717;
  8         19  
  8         403  
50              
51             # constants for small distances
52 8     8   49 use constant TENTH_MM => 0.0001;
  8         15  
  8         356  
53 8     8   44 use constant HUNDREDTH_MM => 0.00001;
  8         17  
  8         544  
54              
55             # OSTN data
56             my $ostn_ee_file = dist_file('Geo-Coordinates-OSGB', 'ostn_east_shift_82140');
57             my $ostn_nn_file = dist_file('Geo-Coordinates-OSGB', 'ostn_north_shift_-84180');
58              
59 8     8   46 use constant MIN_EE_SHIFT => 82140;
  8         16  
  8         372  
60 8     8   48 use constant MIN_NN_SHIFT => -84180;
  8         22  
  8         19141  
61              
62             sub _load_ostn_data {
63 16     16   126 my $name = shift;
64 16         1204 open my $fh, '< :raw :bytes', $name;
65 16         21612 my $count = read $fh, my $data, 1753902;
66 16         206 close $fh;
67 16         924766 return unpack "S<[$count]", $data; # Note the byte order modifiers....
68             }
69              
70             # Perl 5.08: I've use the byte order modifier "<" on the unpack command above
71             # because this means we are system independent and the binary data can be
72             # read of little endian and big endian machines. But this needs Perl 5.10 or
73             # better. If you must have perl 5.08, then you will need to get a copy of
74             # OSTN15 from the OSGB, modify "build/pack_ostn_data" to pack the data in your
75             # native format, and then modify the "unpack" above to match.
76              
77             my @EE_SHIFTS = _load_ostn_data($ostn_ee_file);
78             my @NN_SHIFTS = _load_ostn_data($ostn_nn_file);
79              
80             sub _llh_to_cartesian {
81 15     15   36 my ( $lat, $lon, $H, $shape ) = @_;
82              
83 15         22 my ( $a, $b, $f, $ee ) = @{ ELLIPSOIDS->{$shape} };
  15         36  
84              
85 15         26 my $phi = $lat / 57.29577951308232087679815481410517;
86 15         25 my $sp = sin $phi;
87 15         23 my $cp = cos $phi;
88 15         24 my $lam = $lon / 57.29577951308232087679815481410517;
89 15         22 my $sl = sin $lam;
90 15         23 my $cl = cos $lam;
91              
92 15         30 my $nu = $a / sqrt( 1 - $ee * $sp * $sp );
93              
94 15         26 my $x = ( $nu + $H ) * $cp * $cl;
95 15         27 my $y = ( $nu + $H ) * $cp * $sl;
96 15         34 my $z = ( ( 1 - $ee ) * $nu + $H ) * $sp;
97              
98 15         34 return ( $x, $y, $z );
99             }
100              
101             sub _cartesian_to_llh {
102 15     15   33 my ( $x, $y, $z, $shape ) = @_;
103              
104 15         37 my ( $a, $b, $f, $ee ) = @{ ELLIPSOIDS->{$shape} };
  15         40  
105              
106 15         28 my $p = sqrt($x*$x+$y*$y);
107 15         61 my $lam = atan2 $y, $x;
108 15         35 my $phi = atan2 $z, $p*(1-$ee);
109              
110 15         22 my ( $nu, $oldphi, $sp );
111 15         23 while (1) {
112 45         61 $sp = sin $phi;
113 45         63 $nu = $a / sqrt(1 - $ee*$sp*$sp);
114 45         52 $oldphi = $phi;
115 45         69 $phi = atan2 $z+$ee*$nu*$sp, $p;
116 45 100       90 last if abs($oldphi-$phi) < 1E-12;
117             }
118              
119 15         23 my $lat = $phi * 57.29577951308232087679815481410517;
120 15         21 my $lon = $lam * 57.29577951308232087679815481410517;
121 15         28 my $H = $p / cos($phi) - $nu;
122              
123 15         33 return ( $lat, $lon, $H );
124             }
125              
126             sub _small_Helmert_transform_for_OSGB {
127 15     15   31 my ($direction, $xa, $ya, $za) = @_;
128 15         28 my $tx = $direction * -446.448;
129 15         26 my $ty = $direction * +125.157;
130 15         21 my $tz = $direction * -542.060;
131 15         23 my $sp = $direction * 0.0000204894 + 1;
132 15         24 my $rx = ($direction * -0.1502/3600) / 57.29577951308232087679815481410517;
133 15         29 my $ry = ($direction * -0.2470/3600) / 57.29577951308232087679815481410517;
134 15         25 my $rz = ($direction * -0.8421/3600) / 57.29577951308232087679815481410517;
135 15         27 my $xb = $tx + $sp*$xa - $rz*$ya + $ry*$za;
136 15         26 my $yb = $ty + $rz*$xa + $sp*$ya - $rx*$za;
137 15         34 my $zb = $tz - $ry*$xa + $rx*$ya + $sp*$za;
138 15         30 return ($xb, $yb, $zb);
139             }
140              
141             sub _shift_ll_from_osgb36_to_wgs84 {
142 4     4   11 my ($lat, $lon) = @_;
143 4         9 my ($xa, $ya, $za) = _llh_to_cartesian($lat, $lon, 0, 'OSGB36' );
144 4         16 my ($xb, $yb, $zb) = _small_Helmert_transform_for_OSGB(-1,$xa, $ya, $za);
145 4         11 my ($latx, $lonx, $junk) = _cartesian_to_llh($xb, $yb, $zb, 'WGS84');
146 4         54 return ($latx, $lonx);
147             }
148              
149             sub _shift_ll_from_wgs84_to_osgb36 {
150 11     11   21 my ($lat, $lon) = @_;
151 11         38 my ($xa, $ya, $za) = _llh_to_cartesian($lat, $lon, 0, 'WGS84');
152 11         34 my ($xb, $yb, $zb) = _small_Helmert_transform_for_OSGB(+1,$xa, $ya, $za);
153 11         25 my ($latx, $lonx, $junk) = _cartesian_to_llh($xb, $yb, $zb, 'OSGB36');
154 11         29 return ($latx, $lonx);
155             }
156              
157             sub ll_to_grid {
158              
159 163     163 1 19249 my ( $lat, $lon, $options ) = @_;
160              
161             # we might have been passed a hash as the first argument
162 163 50 66     438 if (ref $lat && defined $lat->{lat} && defined $lat->{lon}) {
      66        
163 1         3 $options = $lat;
164 1         5 $lat = $options->{lat};
165 1         3 $lon = $options->{lon};
166             }
167              
168             # correct reversed arguments, this is always valid in OSGB area
169 163 100       478 if ($lat < $lon) {
170 1         3 ($lat, $lon) = ($lon, $lat)
171             }
172              
173 163 100       482 my $shape = exists $options->{shape} ? $options->{shape} : $default_shape;
174 163 50       416 croak "Unknown shape: $shape" if !exists ELLIPSOIDS->{$shape};
175              
176 163         463 my ($e,$n) = _project_onto_grid($lat, $lon, $shape);
177              
178 163         286 my @out;
179             # If we were using LL from OS maps, then we are done
180 163 100       351 if ($shape eq 'OSGB36') {
181 12         26 @out = map { sprintf '%.3f', $_ } ($e, $n);
  24         171  
182 12 100       91 return wantarray ? @out : "@out";
183             }
184              
185             # now shape is WGS84 etc so we must adjust
186 151         302 my ($dx, $dy) = _find_OSTN_shifts_at($e,$n);
187 151 100       319 if ($dx) {
188 144         333 @out = map { sprintf '%.3f', $_ } ($e + $dx, $n + $dy);
  288         2060  
189 144 100       813 return wantarray ? @out : "@out";
190             }
191              
192             # still here? Then do Helmert shift into OSGB36 and re-project
193 7         51 return ll_to_grid_helmert($lat, $lon)
194             }
195              
196             sub ll_to_grid_helmert {
197 11     11 1 25 my ($lat, $lon) = @_;
198 11         31 my @out = map { sprintf '%.0f', $_ } # round to metres
  22         71  
199             _project_onto_grid( _shift_ll_from_wgs84_to_osgb36($lat, $lon), 'OSGB36' );
200 11 100       86 return wantarray ? @out : "@out";
201             }
202              
203             sub _project_onto_grid {
204              
205 174     174   367 my ( $lat, $lon, $shape ) = @_;
206              
207 174         260 my ($a,$b,$f,$e2) = @{ ELLIPSOIDS->{$shape} };
  174         397  
208              
209 174         400 my $n = ($a-$b)/($a+$b);
210 174         308 my $af = $a * CONVERGENCE_FACTOR;
211              
212 174         264 my $phi = $lat / 57.29577951308232087679815481410517;
213 174         300 my $lam = $lon / 57.29577951308232087679815481410517;
214              
215 174         376 my $cp = cos $phi; my $sp = sin $phi;
  174         284  
216 174         269 my $sp2 = $sp*$sp;
217 174         274 my $tp = $sp/$cp; # cos phi cannot be zero in GB
218 174         224 my $tp2 = $tp*$tp;
219 174         284 my $tp4 = $tp2*$tp2;
220              
221 174         298 my $splat = 1 - $e2 * $sp2;
222 174         253 my $sqrtsplat = sqrt $splat;
223 174         283 my $nu = $af / $sqrtsplat;
224 174         304 my $rho = $af * (1 - $e2) / ($splat*$sqrtsplat);
225 174         269 my $eta2 = $nu/$rho - 1;
226              
227 174         270 my $p_plus = $phi + ORIGIN_LATITUDE;
228 174         261 my $p_minus = $phi - ORIGIN_LATITUDE;
229 174         832 my $M = $b * CONVERGENCE_FACTOR * (
230             (1 + $n * (1 + 5/4*$n*(1 + $n)))*$p_minus
231             - 3*$n*(1+$n*(1+7/8*$n)) * sin( $p_minus) * cos( $p_plus)
232             + (15/8*$n * ($n*(1+$n))) * sin(2*$p_minus) * cos(2*$p_plus)
233             - 35/24*$n**3 * sin(3*$p_minus) * cos(3*$p_plus)
234             );
235              
236 174         280 my $I = $M + ORIGIN_NORTHING;
237 174         304 my $II = $nu/2 * $sp * $cp;
238 174         436 my $III = $nu/24 * $sp * $cp**3 * (5-$tp2+9*$eta2);
239 174         390 my $IIIA = $nu/720* $sp * $cp**5 *(61-58*$tp2+$tp4);
240              
241 174         235 my $IV = $nu*$cp;
242 174         341 my $V = $nu/6 * $cp**3 * ($nu/$rho-$tp2);
243 174         428 my $VI = $nu/120 * $cp**5 * (5-18*$tp2+$tp4+14*$eta2-58*$tp2*$eta2);
244              
245 174         259 my $dl = $lam - ORIGIN_LONGITUDE;
246 174         344 my $north = $I + ( $II + ( $III + $IIIA * $dl * $dl ) * $dl * $dl ) * $dl * $dl;
247 174         314 my $east = ORIGIN_EASTING + ( $IV + ( $V + $VI * $dl * $dl ) * $dl * $dl ) * $dl;
248              
249 174         465 return ($east, $north);
250             }
251              
252             sub _find_OSTN_shifts_at {
253              
254 573     573   974 my ($easting, $northing) = @_;
255              
256 573 100       1127 return if $easting < 0;
257 568 100       1033 return if $easting > 700000;
258 567 100       1019 return if $northing < 0;
259 566 100       952 return if $northing > 1250000;
260              
261 565         916 my $east_km = int($easting / 1000);
262 565         839 my $north_km = int($northing / 1000);
263              
264 565         1144 my $lle = (MIN_EE_SHIFT + $EE_SHIFTS[$east_km + $north_km * 701])/1000;
265 565         919 my $lre = (MIN_EE_SHIFT + $EE_SHIFTS[$east_km + $north_km * 701 + 1])/1000;
266 565         922 my $ule = (MIN_EE_SHIFT + $EE_SHIFTS[$east_km + $north_km * 701 + 701])/1000;
267 565         922 my $ure = (MIN_EE_SHIFT + $EE_SHIFTS[$east_km + $north_km * 701 + 702])/1000;
268              
269 565         936 my $lln = (MIN_NN_SHIFT + $NN_SHIFTS[$east_km + $north_km * 701])/1000;
270 565         831 my $lrn = (MIN_NN_SHIFT + $NN_SHIFTS[$east_km + $north_km * 701 + 1])/1000;
271 565         913 my $uln = (MIN_NN_SHIFT + $NN_SHIFTS[$east_km + $north_km * 701 + 701])/1000;
272 565         856 my $urn = (MIN_NN_SHIFT + $NN_SHIFTS[$east_km + $north_km * 701 + 702])/1000;
273              
274 565         883 my $t = ($easting / 1000) - $east_km;
275 565         858 my $u = ($northing / 1000) - $north_km;
276              
277             return (
278 565         1874 (1-$t) * (1-$u) * $lle + $t * (1-$u) * $lre + (1-$t) * $u * $ule + $t * $u * $ure,
279             (1-$t) * (1-$u) * $lln + $t * (1-$u) * $lrn + (1-$t) * $u * $uln + $t * $u * $urn
280             );
281             }
282              
283             sub grid_to_ll {
284              
285 147     147 1 55768 my ($e, $n, $options) = @_;
286              
287 147 50 66     518 if (ref $e && defined $e->{e} && defined $e->{n}) {
      66        
288 1         4 $options = $e;
289 1         2 $e = $options->{e};
290 1         3 $n = $options->{n};
291             }
292              
293 147 50       465 my $shape = exists $options->{shape} ? $options->{shape} : $default_shape;
294              
295 147 50       446 croak "Unknown shape: $shape" if !exists ELLIPSOIDS->{$shape};
296              
297 147         449 my ($os_lat, $os_lon) = _reverse_project_onto_ellipsoid($e, $n, 'OSGB36');
298              
299             # if we want OS map LL we are done
300 147 100       412 if ($shape eq 'OSGB36') {
301 6         67 return ($os_lat, $os_lon)
302             }
303              
304             # If we want WGS84 LL, we must adjust to pseudo grid if we can
305 141         364 my ($dx, $dy) = _find_OSTN_shifts_at($e,$n);
306 141 50       342 if ($dx) {
307 141         231 my $in_ostn02_polygon = 1;
308 141         316 my ($x,$y) = ($e-$dx, $n-$dy);
309 141         293 my ($last_dx, $last_dy) = ($dx, $dy);
310             APPROX:
311 141         363 for (1..20) {
312 281         524 ($dx, $dy) = _find_OSTN_shifts_at($x,$y);
313              
314 281 100       588 if (!$dx) {
315             # we have been shifted off the edge
316 1         3 $in_ostn02_polygon = 0;
317             last APPROX
318 1         5 }
319              
320 280         529 ($x,$y) = ($e-$dx, $n-$dy);
321 280 100 100     1020 last APPROX if abs($dx-$last_dx) < TENTH_MM
322             && abs($dy-$last_dy) < TENTH_MM;
323 140         311 ($last_dx, $last_dy) = ($dx, $dy);
324             }
325 141 100       335 if ($in_ostn02_polygon ) {
326 140         361 return _reverse_project_onto_ellipsoid($e-$dx, $n-$dy, 'WGS84')
327             }
328             }
329              
330             # If we get here, we must use the Helmert approx
331 1         9 return _shift_ll_from_osgb36_to_wgs84($os_lat, $os_lon)
332             }
333              
334             sub grid_to_ll_helmert {
335 3     3 1 7 my ($e, $n) = @_;
336 3         8 my ($os_lat, $os_lon) = _reverse_project_onto_ellipsoid($e, $n, 'OSGB36');
337 3         8 return _shift_ll_from_osgb36_to_wgs84($os_lat, $os_lon)
338             }
339              
340             sub _reverse_project_onto_ellipsoid {
341              
342 290     290   562 my ( $easting, $northing, $shape ) = @_;
343              
344 290         421 my ( $a, $b, $f, $e2 ) = @{ ELLIPSOIDS->{$shape} };
  290         771  
345              
346 290         697 my $n = ( $a - $b ) / ( $a + $b );
347 290         478 my $af = $a * CONVERGENCE_FACTOR;
348              
349 290         495 my $dn = $northing - ORIGIN_NORTHING;
350 290         436 my $de = $easting - ORIGIN_EASTING;
351              
352 290         477 my $phi = ORIGIN_LATITUDE + $dn/$af;
353 290         397 my $lam = ORIGIN_LONGITUDE;
354              
355 290         463 my ($M, $p_plus, $p_minus);
356 290         414 while (1) {
357 1030         1273 $p_plus = $phi + ORIGIN_LATITUDE;
358 1030         1331 $p_minus = $phi - ORIGIN_LATITUDE;
359 1030         3857 $M = $b * CONVERGENCE_FACTOR * (
360             (1 + $n * (1 + 5/4*$n*(1 + $n)))*$p_minus
361             - 3*$n*(1+$n*(1+7/8*$n)) * sin( $p_minus) * cos( $p_plus)
362             + (15/8*$n * ($n*(1+$n))) * sin(2*$p_minus) * cos(2*$p_plus)
363             - 35/24*$n**3 * sin(3*$p_minus) * cos(3*$p_plus)
364             );
365 1030 100       2191 last if abs($dn-$M) < HUNDREDTH_MM;
366 740         1070 $phi = $phi + ($dn-$M)/$af;
367             }
368              
369 290         497 my $cp = cos $phi;
370 290         470 my $sp = sin $phi;
371 290         448 my $tp = $sp / $cp; # cos phi cannot be zero in GB
372              
373 290         500 my $splat = 1 - $e2 * $sp * $sp;
374 290         435 my $sqrtsplat = sqrt $splat;
375 290         426 my $nu = $af / $sqrtsplat;
376 290         512 my $rho = $af * (1 - $e2) / ( $splat * $sqrtsplat );
377 290         506 my $eta2 = $nu / $rho - 1;
378              
379 290         492 my $VII = $tp / (2 * $rho * $nu);
380 290         748 my $VIII = $tp / (24 * $rho * $nu**3) * (5 + $eta2 + ( 3 - 9 * $eta2 ) * $tp * $tp );
381 290         659 my $IX = $tp / (720 * $rho * $nu**5) * (61 + ( 90 + 45 * $tp * $tp ) * $tp * $tp );
382              
383 290         445 my $secp = 1/$cp;
384              
385 290         412 my $X = $secp / $nu;
386 290         632 my $XI = $secp / ( 6 * $nu**3 ) * ( $nu / $rho + 2 * $tp * $tp );
387 290         602 my $XII = $secp / ( 120 * $nu**5 ) * ( 5 + ( 28 + 24 * $tp * $tp ) * $tp * $tp );
388 290         695 my $XIIA = $secp / ( 5040 * $nu**7 ) * ( 61 + ( 662 + ( 1320 + 720 * $tp * $tp ) * $tp * $tp ) * $tp * $tp );
389              
390 290         603 $phi = $phi + ( -$VII + ( $VIII - $IX * $de * $de ) * $de * $de) * $de * $de;
391 290         536 $lam = $lam + ( $X + ( -$XI + ( $XII - $XIIA * $de * $de ) * $de * $de) * $de * $de) * $de;
392              
393             # now put into degrees & return
394 290         1241 return ($phi * 57.29577951308232087679815481410517,
395             $lam * 57.29577951308232087679815481410517);
396             }
397              
398             1;
399              
400             =pod
401              
402             =head1 NAME
403              
404             Geo::Coordinates::OSGB - Convert coordinates between Lat/Lon and the British National Grid
405              
406             An implementation of co-ordinate conversion for England, Wales, and Scotland
407             based on formulae and data published by the Ordnance Survey of Great Britain.
408              
409             =head1 VERSION
410              
411             2.20
412              
413             =for HTML
414            
415              
416             =head1 SYNOPSIS
417              
418             use Geo::Coordinates::OSGB qw(ll_to_grid grid_to_ll);
419              
420             ($easting,$northing) = ll_to_grid($lat,$lon);
421             ($lat,$lon) = grid_to_ll($easting,$northing);
422              
423             =head1 DESCRIPTION
424              
425             These modules convert accurately between OSGB national grid references and
426             coordinates given in latitude and longitude.
427              
428             The default "ellipsoid model" used for the conversions is the I
429             international standard WGS84. This means that you can take latitude and
430             longitude readings from your GPS receiver, or read them from Wikipedia, or
431             Google Earth, or your car's sat-nav, and use this module to convert them to
432             accurate British National grid references for use with one of the Ordnance
433             Survey's paper maps. And I, of course.
434              
435             The module is implemented purely in Perl, and should run on any platform with
436             Perl version 5.8 or better.
437              
438             In this description, the abbreviations `OS' and `OSGB' mean `the Ordnance
439             Survey of Great Britain': the British government agency that produces the
440             standard maps of England, Wales, and Scotland. Any mention of `sheets' or
441             `maps' refers to one or more of the map sheets defined in the accompanying maps
442             module.
443              
444             This code is written for the British national grid system. It is of no use
445             outside Britain. In fact it's only really useful in the areas covered by the
446             OS's main series of maps, which exclude the Channel Islands and Northern
447             Ireland.
448              
449             =head1 SUBROUTINES/METHODS
450              
451             The following functions can be exported from the
452             C module:
453              
454             grid_to_ll
455             ll_to_grid
456              
457             Neither of these is exported by default.
458              
459             =head2 Main subroutines
460              
461             =head3 C
462              
463             C translates a latitude and longitude pair into a grid
464             easting and northing pair.
465              
466             When called in a list context, C returns the easting and
467             northing as a list of two. When called in a scalar context, it returns
468             a single string with the numbers separated by a space.
469              
470             The arguments should be supplied as real numbers representing
471             decimal degrees, like this:
472              
473             my ($e,$n) = ll_to_grid(51.5, -2.1); # (393154.801, 177900.605)
474              
475             Following the normal mathematical convention, positive arguments mean North or
476             East, negative South or West.
477              
478             If you have data with degrees, minutes, and seconds, you can convert them
479             to decimals like this:
480              
481             my ($e,$n) = ll_to_grid(51+25/60, 0-5/60-2/3600);
482              
483             If you have trouble remembering the order of the arguments, or the returned
484             values, note that latitude comes before longitude in the alphabet too, as
485             easting comes before northing. However, since reasonable latitudes for the
486             OSGB are in the range 49 to 61, and reasonable longitudes in the range -9 to
487             +2, C accepts the arguments in either order; if your longitude is
488             larger than your latitude, then the values of the arguments will be silently
489             swapped.
490              
491             You can also supply the arguments as named keywords (but be sure to use
492             the curly braces so that you pass them as a reference):
493              
494             my ($e,$n) = ll_to_grid( { lat => 51.5, lon => -2.1 } );
495              
496             The easting and northing will be returned as the orthogonal distances in metres
497             from the `false point of origin' of the British Grid (which is a point some way
498             to the south-west of the Scilly Isles). The returned pair refers to a point on
499             the usual OSGB grid, which extends from the Scilly Isles in the south west to
500             the Shetlands in the north.
501              
502             my ($e,$n) = ll_to_grid(51.5, -2.1); # (393154.807, 177900.595)
503             my $s = ll_to_grid(51.5, -2.1); # "393154.807 177900.595"
504              
505             If the coordinates you supply are in the area covered by the OSTN
506             transformation data, then the results will be rounded to 3 decimal places,
507             which corresponds to the nearest millimetre. If they are outside the coverage
508             then the conversion is automagically done using a Helmert transformation
509             instead of the OSTN data. The results will be rounded to the nearest metre
510             in this case, although you probably should not rely on the results being more
511             accurate than about 5m.
512              
513             With the older OSTN02 dataset, coverage extended only to about 3km offshore,
514             but the current OSTN15 dataset extends coverage to the whole grid area
515             from (0,0) to (700000, 1250000), so you have be really far away to get
516             whole metres. Even points well away from land, like this one:
517              
518             # A point in the sea, to the north-west of Coll
519             my $s = ll_to_grid(56.75,-7);
520              
521             will get an accurate conversion. With OSTN02 that returned C<94471 773206>
522             but with OSTN15 you get C<94469.597 773209.464>. For your sake, I hope you are
523             never in a situation at sea off Coll where the 3 metres difference is important.
524              
525             The numbers returned may be negative if your latitude and longitude are
526             far enough south and west, but beware that the transformation is less
527             and less accurate or useful the further you get from the British Isles.
528              
529             If you want the result presented in a more traditional grid reference
530             format you should pass the results to one of the grid formatting
531             routines from L. Like this.
532              
533             my $s = ll_to_grid(51.5, -2.1); # "393154.807 177900.595"
534             $s = format_grid(ll_to_grid(51.5,-2.1)); # "ST 931 779"
535             $s = format_grid_GPS(ll_to_grid(51.5,-2.1)); # "ST 93154 77900"
536             $s = format_grid_map(ll_to_grid(51.5,-2.1)); # "ST 931 779 on A:173, B:156, C:157"
537              
538             C also takes an optional argument that sets the ellipsoid
539             model to use. This defaults to `WGS84', the name of the normal model
540             for working with normal GPS coordinates, but if you want to work with
541             the traditional latitude and longitude values printed around the edges of
542             OS maps before 2015 then you should add an optional shape parameter like this:
543              
544             my ($e, $n) = ll_to_grid(49,-2, {shape => 'OSGB36'});
545              
546             Incidentally, if you make this call above you will get back
547             C<(400000, -100000)> which are the coordinates of the `true point of origin'
548             of the British grid. You should get back an easting of 400000 for any
549             point with longitude 2W since this is the central meridian used for the
550             OSGB projection. However you will get a slightly different value unless
551             you specify C<< {shape => 'OSGB36'} >> because the WGS84 meridians are not
552             quite the same as OSGB36.
553              
554             =head3 C
555              
556             The routine C takes an easting and northing pair
557             representing the distance in metres from the `false point of origin' of
558             the OSGB grid and returns a pair of real numbers representing the
559             equivalent longitude and latitude coordinates in the WGS84 model.
560              
561             Following convention, positive results are North of the equator and East
562             of the prime meridian, negative numbers are South and West. The
563             fractional parts of the results represent decimal fractions of degrees.
564              
565             No special processing is done in scalar context because there is no
566             obvious assumption about how to round the results. You will just get
567             the length of the list returned, which is 2.
568              
569             The arguments must be an (easting, northing) pair representing the
570             absolute grid reference in metres from the point of origin. You can get
571             these from a traditional grid reference string by calling
572             C first.
573              
574             my ($lat, $lon) = grid_to_ll(parse_grid('SM 349 231'))
575              
576             An optional last argument defines the ellipsoid model to use just as it
577             does for C. This is only necessary is you are working
578             with an ellipsoid model other than WGS84. Pass the argument as a hash
579             ref with a `shape' key.
580              
581             my ($lat, $lon) = grid_to_ll(400000, 300000, {shape => 'OSGB36'});
582              
583             If you like named arguments then you can use a single hash ref for all
584             of them (this is strictly optional):
585              
586             my ($lat, $lon) = grid_to_ll({ e => 400000, n => 300000, shape => 'OSGB36'});
587              
588             The results returned will be floating point numbers with the default
589             Perl precision. Unless you are running with long double precision
590             floats you will get 13 decimal places for latitude and 14 places for
591             longitude; but this does not mean that the calculations are accurate to
592             that many places. The OS online conversion tools return decimal degrees
593             to only 6 places. A difference of 1 in the sixth decimal place
594             represents a distance on the ground of about 10 cm. This is probably a
595             good rule of thumb for the reliability of these calculations, but all
596             the available decimal places are returned so that you can choose the
597             rounding that is appropriate for your application. Here's one way to do
598             that:
599              
600             my ($lat, $lon) = map { sprintf "%.6f", $_ } grid_to_ll(431234, 312653);
601              
602              
603             =head2 Additional subroutines
604              
605             =head3 C
606              
607             The default ellipsoid shape used for conversion to and from latitude and
608             longitude is `WGS84' as used in the international GPS system. This
609             default is set every time that you load the module. If you want to
610             process or produce a large number latitude and longitude coordinates in
611             the British Ordnance Survey system (as printed round the edges of OS
612             Landranger and Explorer maps before 2015) you can use C<< set_default_shape('OSGB36'); >> to
613             set the default shape to OSGB36. This saves you having to add C<< {
614             shape => 'OSGB36' } >> to every call of C or C.
615              
616             You can use C<< set_default_shape('WGS84'); >> to set the default shape back
617             to WGS84 again when finished with OSGB36 coordinates.
618              
619             =head3 C
620              
621             You can use this function to do a conversion from WGS84 lat/lon
622             to the OS grid without using the whole OSTN data set. The algorithm
623             used is known as a Helmert transformation. This is the usual coordinate
624             conversion algorithm implemented in most consumer-level GPS devices, which
625             generally do not have enough memory space for the whole of OSTN. It
626             is based on parameters supplied by the OS; they suggest that in most of
627             the UK this conversion is accurate to within about 5m.
628              
629             my ($e, $n) = ll_to_grid_helmert(51.477811, -0.001475); # RO Greenwich
630              
631             The input must be decimal degrees in the WGS84 model, with latitude
632             first and longitude second. The results are rounded to the nearest
633             whole metre. They can be used with C in the same way as
634             the results from C.
635              
636             This function is called automatically by C if your
637             coordinates are WGS84 and lie outside the OSTN polygon.
638              
639             =head3 C
640              
641             You can use this function to do a slightly quicker conversion from OS grid
642             references to WGS84 latitude and longitude coordinates without using the
643             whole OSTN data set. The algorithm used is known as a Helmert
644             transformation. This is the usual coordinate conversion algorithm
645             implemented in most consumer-level GPS devices. It is based on
646             parameters supplied by the OS; they suggest that in most of the UK this
647             conversion is accurate to within about 5m.
648              
649             my ($lat, $lon) = grid_to_ll_helmert(538885, 177322);
650              
651             The input must be in metres from false point of origin (as produced by
652             C) and the results are in decimal degrees using the WGS84
653             model.
654              
655             The results are returned with the full Perl precision in the same way as
656             C so that you can choose an appropriate rounding for your
657             needs. Four or five decimal places is probably appropriate in most
658             cases. This represents somewhere between 1 and 10 m on the ground.
659              
660             This function is called automatically by C if the grid reference
661             you supply lies outside the OSTN polygon. (All such spots are far out to sea).
662             The results are only useful close to mainland Britain.
663              
664             =head3 Importing all the functions
665              
666             You can import all the functions defined in C with an C<:all> tag.
667              
668             use Geo::Coordinates::OSGB ':all';
669              
670             =head1 EXAMPLES
671              
672             use Geo::Coordinates::OSGB qw/ll_to_grid grid_to_ll/;
673              
674             # Latitude and longitude according to the WGS84 model
675             ($lat, $lon) = grid_to_ll($e, $n);
676              
677             # and to go the other way
678             ($e, $n) = ll_to_grid($lat,$lon);
679              
680             See the test files for more examples of usage.
681              
682             =head1 BUGS AND LIMITATIONS
683              
684             The formulae supplied by the OS and used for the conversion routines are
685             specifically designed to be close floating-point approximations rather
686             than exact mathematical equivalences. So after round-trips like these:
687              
688             ($lat1,$lon1) = grid_to_ll(ll_to_grid($lat0,$lon0));
689             ($e1,$n1) = ll_to_grid(grid_to_ll($e0,$n0));
690              
691             neither C<$lat1 == $lat0> nor C<$lon1 == $lon0> nor C<$e1 == $e0> nor
692             C<$n1 == $n0> exactly. However the differences should be very small.
693              
694             The OS formulae were designed to give an accuracy of about 1 mm of
695             error. This means that you can rely on the third decimal place for grid
696             references and about the seventh or eighth for latitude and longitude
697             (although the OS themselves only provide six decimal places in their
698             results).
699              
700             For all of England, Wales, Scotland, and the Isle of Man the error will be
701             tiny. All other areas, like Northern Ireland, the Channel Islands or Rockall,
702             and any areas of sea more than a few miles off shore, are outside the coverage
703             of OSTN, so the simpler, less accurate transformation is used. The OS state
704             that this is accurate to about 5m but that the parameters used are only valid
705             in the reasonably close vicinity of the British Isles.
706              
707             Not enough testing has been done. I am always grateful for the feedback
708             I get from users, but especially for problem reports that help me to
709             make this a better module.
710              
711             =head1 DIAGNOSTICS
712              
713             The only error message you will get from this module is about the
714             ellipsoid shape used for the transformation. If you try to set C<<
715             {shape => 'blah'} >> the module will croak with a message saying
716             C. The shape should be one of the shapes defined:
717             WGS84 or OSGB36.
718              
719             Should this software not do what you expect, then please first read this
720             documentation, secondly verify that you have installed it correctly and
721             that it passes all the installation tests on your set up, thirdly study
722             the source code to see what it's supposed to be doing, fourthly get in
723             touch to ask me about it.
724              
725             =head1 CONFIGURATION AND ENVIRONMENT
726              
727             There is no configuration required either of these modules or your
728             environment. It should work on any recent version of Perl, on any
729             platform.
730              
731             =head1 DEPENDENCIES
732              
733             Perl 5.10 or better.
734              
735             =head1 INCOMPATIBILITIES
736              
737             None known.
738              
739             =head1 LICENSE AND COPYRIGHT
740              
741             Copyright (C) 2002-2017 Toby Thurston
742              
743             OSTN transformation data included in this module is freely available
744             from the Ordnance Survey but remains Crown Copyright (C) 2002
745              
746             This program is free software; you can redistribute it and/or modify it
747             under the terms of the GNU General Public License as published by the
748             Free Software Foundation; either version 2 of the License, or (at your
749             option) any later version.
750              
751             This program is distributed in the hope that it will be useful, but
752             WITHOUT ANY WARRANTY; without even the implied warranty of
753             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
754             General Public License for more details.
755              
756             You should have received a copy of the GNU General Public License along
757             with this program; if not, write to the Free Software Foundation, Inc.,
758             51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
759              
760             =head1 AUTHOR
761              
762             Toby Thurston -- 29 Oct 2017
763              
764             toby@cpan.org
765              
766             =head1 SEE ALSO
767              
768             See L for routines to format grid references.
769              
770             The UK Ordnance Survey's explanations on their web pages.
771              
772             See L for a general approach (not based on the OSGB).
773              
774             =cut
775