File Coverage

blib/lib/Geo/Coordinates/OSGB.pm
Criterion Covered Total %
statement 264 264 100.0
branch 55 64 85.9
condition 11 15 73.3
subroutine 30 30 100.0
pod 5 5 100.0
total 365 378 96.5


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