line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Geo::Coordinates::OSGB::Grid; |
2
|
|
|
|
|
|
|
|
3
|
9
|
|
|
9
|
|
48051
|
use Geo::Coordinates::OSGB::Maps qw{%maps %name_for_map_series}; |
|
9
|
|
|
|
|
461
|
|
|
9
|
|
|
|
|
4360
|
|
4
|
9
|
|
|
9
|
|
11562
|
use Geo::Coordinates::OSGB qw{get_ostn02_shift_pair}; |
|
9
|
|
|
|
|
51
|
|
|
9
|
|
|
|
|
688
|
|
5
|
|
|
|
|
|
|
|
6
|
9
|
|
|
9
|
|
72
|
use base qw(Exporter); |
|
9
|
|
|
|
|
19
|
|
|
9
|
|
|
|
|
808
|
|
7
|
9
|
|
|
9
|
|
60
|
use strict; |
|
9
|
|
|
|
|
24
|
|
|
9
|
|
|
|
|
241
|
|
8
|
9
|
|
|
9
|
|
52
|
use warnings; |
|
9
|
|
|
|
|
21
|
|
|
9
|
|
|
|
|
289
|
|
9
|
9
|
|
|
9
|
|
49
|
use Carp; |
|
9
|
|
|
|
|
25
|
|
|
9
|
|
|
|
|
592
|
|
10
|
9
|
|
|
9
|
|
247
|
use 5.008; # At least Perl 5.08 please, be sure to change POD below if you update |
|
9
|
|
|
|
|
39
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our $VERSION = '2.18'; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our %EXPORT_TAGS = (all => [qw( |
15
|
|
|
|
|
|
|
parse_grid |
16
|
|
|
|
|
|
|
format_grid |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
parse_trad_grid |
19
|
|
|
|
|
|
|
parse_GPS_grid |
20
|
|
|
|
|
|
|
parse_landranger_grid |
21
|
|
|
|
|
|
|
parse_map_grid |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
format_grid_trad |
24
|
|
|
|
|
|
|
format_grid_GPS |
25
|
|
|
|
|
|
|
format_grid_landranger |
26
|
|
|
|
|
|
|
format_grid_map |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
random_grid |
29
|
|
|
|
|
|
|
)]); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } ); |
32
|
|
|
|
|
|
|
|
33
|
9
|
|
|
9
|
|
63
|
use constant GRID_SQ_LETTERS => 'VWXYZQRSTULMNOPFGHJKABCDE'; |
|
9
|
|
|
|
|
24
|
|
|
9
|
|
|
|
|
897
|
|
34
|
9
|
|
|
9
|
|
88
|
use constant GRID_SIZE => sqrt length GRID_SQ_LETTERS; |
|
9
|
|
|
|
|
26
|
|
|
9
|
|
|
|
|
400
|
|
35
|
9
|
|
|
9
|
|
55
|
use constant MINOR_GRID_SQ_SIZE => 100_000; |
|
9
|
|
|
|
|
22
|
|
|
9
|
|
|
|
|
442
|
|
36
|
9
|
|
|
9
|
|
54
|
use constant MAJOR_GRID_SQ_SIZE => GRID_SIZE * MINOR_GRID_SQ_SIZE; |
|
9
|
|
|
|
|
24
|
|
|
9
|
|
|
|
|
481
|
|
37
|
9
|
|
|
9
|
|
56
|
use constant MAJOR_GRID_SQ_EASTING_OFFSET => 2 * MAJOR_GRID_SQ_SIZE; |
|
9
|
|
|
|
|
21
|
|
|
9
|
|
|
|
|
455
|
|
38
|
9
|
|
|
9
|
|
56
|
use constant MAJOR_GRID_SQ_NORTHING_OFFSET => 1 * MAJOR_GRID_SQ_SIZE; |
|
9
|
|
|
|
|
25
|
|
|
9
|
|
|
|
|
475
|
|
39
|
9
|
|
|
9
|
|
53
|
use constant MAX_GRID_SIZE => MINOR_GRID_SQ_SIZE * length GRID_SQ_LETTERS; |
|
9
|
|
|
|
|
21
|
|
|
9
|
|
|
|
|
19103
|
|
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# Produce a random GR |
42
|
|
|
|
|
|
|
# A simple approach would pick 0 < E < 700000 and 0 < N < 1250000 but that way |
43
|
|
|
|
|
|
|
# many GRs produced would be in the sea, so pick a random map, and then find a |
44
|
|
|
|
|
|
|
# random GR within its bbox and finally check that the resulting pair is |
45
|
|
|
|
|
|
|
# inside the OSTN02 boundary and actually on some map. |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub random_grid { |
48
|
2
|
|
|
2
|
1
|
1619
|
my @preferred_sheets = @_; |
49
|
|
|
|
|
|
|
# assume plain sheet numbers are Landranger |
50
|
2
|
|
|
|
|
6
|
for my $s (@preferred_sheets) { |
51
|
8
|
100
|
|
|
|
38
|
if ($s =~ m{\A\d+\Z}xsmio) { |
52
|
7
|
|
|
|
|
19
|
$s =~ s/\A/A:/xsmio |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
} |
55
|
2
|
|
|
|
|
4
|
my @sheets; |
56
|
2
|
50
|
|
|
|
8
|
if (@preferred_sheets) { |
57
|
2
|
|
|
|
|
6
|
@sheets = grep { exists $maps{$_} } @preferred_sheets; |
|
8
|
|
|
|
|
22
|
|
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
else { |
60
|
0
|
|
|
|
|
0
|
@sheets = keys %maps; |
61
|
|
|
|
|
|
|
} |
62
|
2
|
|
|
|
|
6
|
my $margin = 5000; |
63
|
2
|
|
|
|
|
8
|
my ($map, $lle, $lln, $ure, $urn); |
64
|
2
|
|
|
|
|
0
|
my ($easting, $northing); |
65
|
2
|
|
|
|
|
3
|
while (1) { |
66
|
2
|
|
|
|
|
10
|
$map = $maps{$sheets[int rand @sheets]}; |
67
|
2
|
|
|
|
|
5
|
($lle, $lln) = @{$map->{bbox}->[0]}; |
|
2
|
|
|
|
|
8
|
|
68
|
2
|
|
|
|
|
5
|
($ure, $urn) = @{$map->{bbox}->[1]}; |
|
2
|
|
|
|
|
5
|
|
69
|
2
|
|
|
|
|
27
|
$easting = sprintf '%.3f', $lle + rand ($ure-$lle); |
70
|
2
|
|
|
|
|
12
|
$northing = sprintf '%.3f', $lln + rand ($urn-$lln); |
71
|
|
|
|
|
|
|
# check we are well inside OSTN02 and actually on the chosen map |
72
|
|
|
|
|
|
|
last if get_ostn02_shift_pair($easting, $northing) |
73
|
|
|
|
|
|
|
&& get_ostn02_shift_pair($easting, $northing+$margin) |
74
|
|
|
|
|
|
|
&& get_ostn02_shift_pair($easting, $northing-$margin) |
75
|
|
|
|
|
|
|
&& get_ostn02_shift_pair($easting+$margin, $northing) |
76
|
|
|
|
|
|
|
&& get_ostn02_shift_pair($easting-$margin, $northing) |
77
|
2
|
50
|
33
|
|
|
17
|
&& 0 != _winding_number($easting, $northing, $map->{polygon}); |
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
78
|
|
|
|
|
|
|
} |
79
|
2
|
|
|
|
|
12
|
return ($easting, $northing); |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub format_grid { |
83
|
226
|
|
|
226
|
1
|
43575
|
my ($easting, $northing, $options) = @_; |
84
|
|
|
|
|
|
|
|
85
|
226
|
100
|
|
|
|
775
|
my $form = exists $options->{form} ? uc $options->{form} : 'SS EEE NNN'; |
86
|
226
|
100
|
|
|
|
565
|
my $with_maps = exists $options->{maps} ? $options->{maps} : 0; |
87
|
226
|
100
|
|
|
|
1400
|
my $map_keys = exists $options->{series} ? uc $options->{series} : join '', sort keys %name_for_map_series; |
88
|
|
|
|
|
|
|
|
89
|
226
|
|
|
|
|
815
|
my $sq = _grid_to_sq($easting,$northing); |
90
|
226
|
50
|
|
|
|
665
|
if ( !$sq ) { |
91
|
0
|
|
|
|
|
0
|
croak "Too far off the grid: $easting $northing\n"; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
226
|
|
|
|
|
577
|
my ($e,$n) = map { int } map { $_ % MINOR_GRID_SQ_SIZE } ($easting, $northing); |
|
452
|
|
|
|
|
1004
|
|
|
452
|
|
|
|
|
1046
|
|
95
|
|
|
|
|
|
|
|
96
|
226
|
|
|
|
|
509
|
my @sheets = (); |
97
|
226
|
100
|
|
|
|
546
|
if ( $with_maps ) { |
98
|
59
|
|
|
|
|
246
|
while (my ($k,$m) = each %maps) { |
99
|
66847
|
100
|
|
|
|
165119
|
next if index($map_keys, substr($k,0,1)) == -1; |
100
|
61142
|
100
|
100
|
|
|
346667
|
if ($m->{bbox}->[0][0] <= $easting && $easting < $m->{bbox}->[1][0] |
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
101
|
|
|
|
|
|
|
&& $m->{bbox}->[0][1] <= $northing && $northing < $m->{bbox}->[1][1]) { |
102
|
209
|
|
|
|
|
776
|
my $w = _winding_number($easting, $northing, $m->{polygon}); |
103
|
209
|
100
|
|
|
|
566
|
if ($w != 0) { |
104
|
200
|
|
|
|
|
901
|
push @sheets, $k; |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
} |
108
|
59
|
|
|
|
|
366
|
@sheets = sort @sheets; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
# special cases |
112
|
226
|
100
|
|
|
|
939
|
if ( $form eq 'TRAD' ) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
113
|
2
|
|
|
|
|
4
|
$form = 'SS EEE NNN'; |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
elsif ( $form eq 'GPS' ) { |
116
|
48
|
|
|
|
|
154
|
$form = 'SS EEEEE NNNNN'; |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
elsif ( $form eq 'SS' ) { |
119
|
1
|
|
|
|
|
5
|
return $sq; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
225
|
50
|
|
|
|
2389
|
if ( my ($space_a, $e_spec, $space_b, $n_spec) = $form =~ m{ \A S{1,2}(\s*)(E{1,5})(\s*)(N{1,5}) \Z }iosxm ) { |
123
|
225
|
|
|
|
|
494
|
my $e_len = length $e_spec; |
124
|
225
|
|
|
|
|
371
|
my $n_len = length $n_spec; |
125
|
225
|
|
|
|
|
665
|
$e = int($e / 10**(5 - $e_len)); |
126
|
225
|
|
|
|
|
432
|
$n = int($n / 10**(5 - $n_len)); |
127
|
|
|
|
|
|
|
|
128
|
225
|
100
|
|
|
|
587
|
if ( wantarray ) { |
129
|
49
|
|
|
|
|
571
|
return ($sq, $e, $n, @sheets) |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
176
|
|
|
|
|
715
|
my $gr = sprintf '%s%s%0.*d%s%0.*d', $sq, $space_a, $e_len, $e, $space_b, $n_len, $n; |
133
|
176
|
|
|
|
|
993
|
$gr =~ s/\s+/ /g; |
134
|
|
|
|
|
|
|
|
135
|
176
|
100
|
|
|
|
477
|
if ( $with_maps ) { |
136
|
12
|
50
|
|
|
|
40
|
if ( @sheets ) { |
137
|
12
|
|
|
|
|
168
|
return sprintf '%s on %s', $gr, join ', ', @sheets; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
else { |
140
|
0
|
|
|
|
|
0
|
return sprintf '%s is not on any maps in series %s', $gr, $map_keys; |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
else { |
144
|
164
|
|
|
|
|
835
|
return $gr; |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
0
|
|
|
|
|
0
|
croak "Format $form was not recognized"; |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
sub format_grid_trad { |
152
|
7
|
|
|
7
|
1
|
425
|
my ($easting, $northing) = @_; |
153
|
7
|
|
|
|
|
29
|
return format_grid($easting, $northing, { form => 'SS EEE NNN' }) |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
sub format_grid_GPS { |
157
|
10
|
|
|
10
|
1
|
417
|
my ($easting, $northing) = @_; |
158
|
10
|
|
|
|
|
37
|
return format_grid($easting, $northing, { form => 'SS EEEEE NNNNN' }) |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
sub format_grid_map { |
162
|
12
|
|
|
12
|
1
|
40
|
my ($easting, $northing, $options) = @_; |
163
|
12
|
100
|
|
|
|
34
|
if ( defined $options ) { |
164
|
2
|
|
|
|
|
7
|
$options->{maps} = 1 |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
else { |
167
|
10
|
|
|
|
|
33
|
$options = { maps => 1 } |
168
|
|
|
|
|
|
|
} |
169
|
12
|
|
|
|
|
42
|
return format_grid($easting, $northing, $options) |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub format_grid_landranger { |
173
|
4
|
|
|
4
|
1
|
326
|
my ($easting,$northing) = @_; |
174
|
|
|
|
|
|
|
|
175
|
4
|
|
|
|
|
25
|
my ($sq, $e, $n, @sheets) = format_grid($easting, $northing, { form => 'SS EEE NNN', maps => 1, series => 'A' }); |
176
|
|
|
|
|
|
|
|
177
|
4
|
|
|
|
|
17
|
for my $s (@sheets) { |
178
|
6
|
|
|
|
|
30
|
$s =~ s/\AA://xsm; |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
4
|
100
|
|
|
|
33
|
return ($sq, $e, $n, @sheets) if wantarray; |
182
|
|
|
|
|
|
|
|
183
|
1
|
50
|
|
|
|
5
|
if (!@sheets ) { return sprintf '%s %03d %03d is not on any Landranger sheet', $sq, $e, $n } |
|
0
|
|
|
|
|
0
|
|
184
|
1
|
50
|
|
|
|
4
|
if ( @sheets==1 ) { return sprintf '%s %03d %03d on Landranger sheet %s' , $sq, $e, $n, $sheets[0] } |
|
1
|
|
|
|
|
11
|
|
185
|
0
|
0
|
|
|
|
0
|
if ( @sheets==2 ) { return sprintf '%s %03d %03d on Landranger sheets %s and %s', $sq, $e, $n, @sheets } |
|
0
|
|
|
|
|
0
|
|
186
|
|
|
|
|
|
|
|
187
|
0
|
|
|
|
|
0
|
my $phrase = join ', ', @sheets[0..($#sheets-1)], "and $sheets[-1]"; |
188
|
0
|
|
|
|
|
0
|
return sprintf '%s %03d %03d on Landranger sheets %s', $sq, $e, $n, $phrase; |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub _grid_to_sq { |
193
|
226
|
|
|
226
|
|
502
|
my ($e, $n) = @_; |
194
|
|
|
|
|
|
|
|
195
|
226
|
|
|
|
|
499
|
$e += MAJOR_GRID_SQ_EASTING_OFFSET; |
196
|
226
|
|
|
|
|
407
|
$n += MAJOR_GRID_SQ_NORTHING_OFFSET; |
197
|
226
|
50
|
33
|
|
|
1865
|
return if !(0 <= $e && $e < MAX_GRID_SIZE && 0 <= $n && $n < MAX_GRID_SIZE); |
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
198
|
|
|
|
|
|
|
|
199
|
226
|
|
|
|
|
704
|
my $major_index = int $e / MAJOR_GRID_SQ_SIZE + GRID_SIZE * int $n / MAJOR_GRID_SQ_SIZE; |
200
|
226
|
|
|
|
|
423
|
$e = $e % MAJOR_GRID_SQ_SIZE; |
201
|
226
|
|
|
|
|
381
|
$n = $n % MAJOR_GRID_SQ_SIZE; |
202
|
226
|
|
|
|
|
487
|
my $minor_index = int $e / MINOR_GRID_SQ_SIZE + GRID_SIZE * int $n / MINOR_GRID_SQ_SIZE; |
203
|
|
|
|
|
|
|
return |
204
|
226
|
|
|
|
|
778
|
substr(GRID_SQ_LETTERS, $major_index, 1) . |
205
|
|
|
|
|
|
|
substr(GRID_SQ_LETTERS, $minor_index, 1); |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub _get_grid_square_offsets { |
209
|
117
|
|
|
117
|
|
244
|
my $s = shift; |
210
|
117
|
50
|
|
|
|
399
|
return if length $s < 2; |
211
|
|
|
|
|
|
|
|
212
|
117
|
|
|
|
|
437
|
my $a = index GRID_SQ_LETTERS, uc substr $s, 0, 1; |
213
|
117
|
100
|
|
|
|
365
|
return if 0 > $a; |
214
|
|
|
|
|
|
|
|
215
|
109
|
|
|
|
|
277
|
my $b = index GRID_SQ_LETTERS, uc substr $s, 1, 1; |
216
|
109
|
100
|
|
|
|
420
|
return if 0 > $b; |
217
|
|
|
|
|
|
|
|
218
|
55
|
|
|
|
|
171
|
my ($X, $Y) = ($a % GRID_SIZE, int $a / GRID_SIZE); |
219
|
55
|
|
|
|
|
125
|
my ($x, $y) = ($b % GRID_SIZE, int $b / GRID_SIZE); |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
return ( |
222
|
55
|
|
|
|
|
239
|
MAJOR_GRID_SQ_SIZE * $X - MAJOR_GRID_SQ_EASTING_OFFSET + MINOR_GRID_SQ_SIZE * $x, |
223
|
|
|
|
|
|
|
MAJOR_GRID_SQ_SIZE * $Y - MAJOR_GRID_SQ_NORTHING_OFFSET + MINOR_GRID_SQ_SIZE * $y |
224
|
|
|
|
|
|
|
); |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
sub _get_eastnorthings { |
228
|
112
|
|
|
112
|
|
279
|
my $s = shift; |
229
|
112
|
|
|
|
|
223
|
my $numbers = $s; |
230
|
112
|
|
|
|
|
304
|
$numbers =~ tr/0-9//cd; # avoid using "r" here as it requires perl >= 5.14 |
231
|
112
|
|
|
|
|
253
|
my $len = length $numbers; |
232
|
112
|
50
|
|
|
|
394
|
croak 'No easting or northing found' if $len == 0; |
233
|
112
|
50
|
|
|
|
339
|
croak "Easting and northing have different lengths in $s" if $len % 2; |
234
|
112
|
50
|
|
|
|
306
|
croak "Too many digits in $s" if $len > 10; |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
# this trick lets us pad with zeros on the right |
237
|
112
|
|
|
|
|
638
|
my $e = reverse sprintf '%05d', scalar reverse substr $numbers, 0, $len/2; |
238
|
112
|
|
|
|
|
535
|
my $n = reverse sprintf '%05d', scalar reverse substr $numbers, $len/2; |
239
|
224
|
|
|
|
|
375
|
return ($e, $n) |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
sub parse_grid { |
243
|
|
|
|
|
|
|
|
244
|
117
|
100
|
|
117
|
1
|
6862
|
my $options = 'HASH' eq ref $_[-1] ? pop @_ : { }; |
245
|
|
|
|
|
|
|
|
246
|
117
|
100
|
|
|
|
379
|
my $figs = exists $options->{figs} ? $options->{figs} : 3; |
247
|
|
|
|
|
|
|
|
248
|
117
|
|
|
|
|
221
|
my @out; |
249
|
|
|
|
|
|
|
|
250
|
117
|
100
|
|
|
|
776
|
my $s = @_ < 3 ? "@_" : sprintf '%s %0.*d %0.*d', $_[0], $figs, $_[1], $figs, $_[2]; |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
# normal case : TQ 123 456 etc |
253
|
117
|
100
|
|
|
|
399
|
if ( my ($E, $N) = _get_grid_square_offsets($s) ) { |
254
|
55
|
100
|
|
|
|
219
|
my ($e, $n) = length $s > 2 ? _get_eastnorthings(substr $s, 2) : (0,0); |
255
|
55
|
|
|
|
|
219
|
@out = ($E+$e, $N+$n); |
256
|
55
|
100
|
|
|
|
487
|
return wantarray ? @out : "@out"; |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
# sheet id instead of grid sq |
260
|
62
|
|
|
|
|
421
|
my $sheet_ref_pattern = qr{\A([A-Z]:)?([0-9NEWSOL/]+?)(\.[a-z]+)?(?:[ -/.]([ 0-9]+))?\Z}; |
261
|
62
|
|
|
|
|
679
|
my ($prefix, $sheet, $suffix, $numbers) = $s =~ m/$sheet_ref_pattern/sioxm; |
262
|
|
|
|
|
|
|
|
263
|
62
|
50
|
|
|
|
219
|
if (defined $sheet) { |
264
|
|
|
|
|
|
|
|
265
|
62
|
100
|
|
|
|
191
|
if ( ! defined $prefix ) { |
266
|
8
|
|
|
|
|
20
|
$prefix = 'A:'; |
267
|
|
|
|
|
|
|
} |
268
|
62
|
100
|
|
|
|
198
|
if ( ! defined $suffix ) { |
269
|
61
|
|
|
|
|
114
|
$suffix = q{}; |
270
|
|
|
|
|
|
|
} |
271
|
62
|
|
|
|
|
156
|
$sheet = $prefix . $sheet . $suffix; |
272
|
|
|
|
|
|
|
|
273
|
62
|
50
|
|
|
|
238
|
if (exists $maps{$sheet}) { |
274
|
62
|
|
|
|
|
127
|
my ($E, $N) = @{$maps{$sheet}->{bbox}[0]}; # NB we need the bbox corner so that it is left and below all points on the map |
|
62
|
|
|
|
|
227
|
|
275
|
|
|
|
|
|
|
|
276
|
62
|
100
|
|
|
|
201
|
if (defined $numbers) { |
277
|
59
|
|
|
|
|
201
|
my ($e, $n) = _get_eastnorthings($numbers); |
278
|
59
|
|
|
|
|
191
|
$E = $E + ($e-$E) % MINOR_GRID_SQ_SIZE; |
279
|
59
|
|
|
|
|
140
|
$N = $N + ($n-$N) % MINOR_GRID_SQ_SIZE; |
280
|
|
|
|
|
|
|
|
281
|
59
|
|
|
|
|
218
|
my $w = _winding_number($E, $N, $maps{$sheet}->{polygon}); |
282
|
59
|
50
|
|
|
|
209
|
if ($w == 0) { |
283
|
0
|
|
|
|
|
0
|
croak sprintf 'Grid reference %s = (%d, %d) is not on sheet %s', scalar format_grid($E,$N), $e, $n, $sheet; |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
} |
286
|
62
|
100
|
|
|
|
532
|
return wantarray ? ($E, $N) : "$E $N"; |
287
|
|
|
|
|
|
|
} |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
# just a pair of numbers |
291
|
0
|
0
|
|
|
|
0
|
if ( @out = $s =~ m{\A (\d+(?:\.\d+)?) \s+ (\d+(?:\.\d+)?) \Z}xsmio ) { # eee nnn |
292
|
0
|
0
|
|
|
|
0
|
return wantarray ? @out : "@out"; |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
|
295
|
0
|
|
|
|
|
0
|
croak "Failed to parse a grid reference from $s"; |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
*parse_trad_grid = \&parse_grid; |
299
|
|
|
|
|
|
|
*parse_GPS_grid = \&parse_grid; |
300
|
|
|
|
|
|
|
*parse_landranger_grid = \&parse_grid; |
301
|
|
|
|
|
|
|
*parse_map_grid = \&parse_grid; |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
# is $pt left of $a--$b? |
304
|
|
|
|
|
|
|
sub _is_left { |
305
|
540
|
|
|
540
|
|
1055
|
my ($x, $y, $a, $b) = @_; |
306
|
540
|
|
|
|
|
2662
|
return ( ($b->[0] - $a->[0]) * ($y - $a->[1]) - ($x - $a->[0]) * ($b->[1] - $a->[1]) ); |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
# adapted from http://geomalgorithms.com/a03-_inclusion.html |
310
|
|
|
|
|
|
|
sub _winding_number { |
311
|
270
|
|
|
270
|
|
602
|
my ($x, $y, $poly) = @_; |
312
|
270
|
|
|
|
|
471
|
my $w = 0; |
313
|
270
|
|
|
|
|
820
|
for (my $i=0; $i < $#$poly; $i++ ) { |
314
|
4194
|
100
|
|
|
|
8802
|
if ( $poly->[$i][1] <= $y ) { |
315
|
1838
|
100
|
100
|
|
|
6662
|
if ($poly->[$i+1][1] > $y && _is_left($x, $y, $poly->[$i], $poly->[$i+1]) > 0 ) { |
316
|
267
|
|
|
|
|
715
|
$w++; |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
else { |
320
|
2356
|
100
|
100
|
|
|
8282
|
if ($poly->[$i+1][1] <= $y && _is_left($x, $y, $poly->[$i], $poly->[$i+1]) < 0 ) { |
321
|
6
|
|
|
|
|
18
|
$w--; |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
} |
325
|
270
|
|
|
|
|
605
|
return $w; |
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
1; |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
__END__ |