line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Geo::Coordinates::OSGB::Grid; |
2
|
|
|
|
|
|
|
|
3
|
7
|
|
|
7
|
|
72019
|
use Geo::Coordinates::OSGB::Maps qw{%maps %name_for_map_series}; |
|
7
|
|
|
|
|
699
|
|
|
7
|
|
|
|
|
1561
|
|
4
|
|
|
|
|
|
|
|
5
|
7
|
|
|
7
|
|
86
|
use base qw(Exporter); |
|
7
|
|
|
|
|
16
|
|
|
7
|
|
|
|
|
940
|
|
6
|
7
|
|
|
7
|
|
52
|
use strict; |
|
7
|
|
|
|
|
22
|
|
|
7
|
|
|
|
|
183
|
|
7
|
7
|
|
|
7
|
|
44
|
use warnings; |
|
7
|
|
|
|
|
21
|
|
|
7
|
|
|
|
|
248
|
|
8
|
7
|
|
|
7
|
|
39
|
use Carp; |
|
7
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
504
|
|
9
|
7
|
|
|
7
|
|
196
|
use 5.010; # At least Perl 5.10 please, be sure to change POD below if you update |
|
7
|
|
|
|
|
30
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
our $VERSION = '2.20'; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
our %EXPORT_TAGS = (all => [qw( |
14
|
|
|
|
|
|
|
parse_grid |
15
|
|
|
|
|
|
|
format_grid |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
parse_trad_grid |
18
|
|
|
|
|
|
|
parse_GPS_grid |
19
|
|
|
|
|
|
|
parse_landranger_grid |
20
|
|
|
|
|
|
|
parse_map_grid |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
format_grid_trad |
23
|
|
|
|
|
|
|
format_grid_GPS |
24
|
|
|
|
|
|
|
format_grid_landranger |
25
|
|
|
|
|
|
|
format_grid_map |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
random_grid |
28
|
|
|
|
|
|
|
)]); |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } ); |
31
|
|
|
|
|
|
|
|
32
|
7
|
|
|
7
|
|
42
|
use constant GRID_SQ_LETTERS => 'VWXYZQRSTULMNOPFGHJKABCDE'; |
|
7
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
577
|
|
33
|
7
|
|
|
7
|
|
43
|
use constant GRID_SIZE => sqrt length GRID_SQ_LETTERS; |
|
7
|
|
|
|
|
19
|
|
|
7
|
|
|
|
|
312
|
|
34
|
7
|
|
|
7
|
|
38
|
use constant MINOR_GRID_SQ_SIZE => 100_000; |
|
7
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
315
|
|
35
|
7
|
|
|
7
|
|
40
|
use constant MAJOR_GRID_SQ_SIZE => GRID_SIZE * MINOR_GRID_SQ_SIZE; |
|
7
|
|
|
|
|
16
|
|
|
7
|
|
|
|
|
298
|
|
36
|
7
|
|
|
7
|
|
33
|
use constant MAJOR_GRID_SQ_EASTING_OFFSET => 2 * MAJOR_GRID_SQ_SIZE; |
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
318
|
|
37
|
7
|
|
|
7
|
|
44
|
use constant MAJOR_GRID_SQ_NORTHING_OFFSET => 1 * MAJOR_GRID_SQ_SIZE; |
|
7
|
|
|
|
|
17
|
|
|
7
|
|
|
|
|
368
|
|
38
|
7
|
|
|
7
|
|
40
|
use constant MAX_GRID_SIZE => MINOR_GRID_SQ_SIZE * length GRID_SQ_LETTERS; |
|
7
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
14541
|
|
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# Produce a random GR |
41
|
|
|
|
|
|
|
# A simple approach would pick 0 < E < 700000 and 0 < N < 1250000 but that way |
42
|
|
|
|
|
|
|
# many GRs produced would be in the sea, so pick a random map, and then find a |
43
|
|
|
|
|
|
|
# random GR within its bbox and finally check that the resulting pair is |
44
|
|
|
|
|
|
|
# and actually on the map. |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub random_grid { |
47
|
2
|
|
|
2
|
1
|
1204
|
my @preferred_sheets = @_; |
48
|
|
|
|
|
|
|
# assume plain sheet numbers are Landranger |
49
|
2
|
|
|
|
|
7
|
for my $s (@preferred_sheets) { |
50
|
8
|
100
|
|
|
|
43
|
if ($s =~ m{\A\d+\Z}xsmio) { |
51
|
7
|
|
|
|
|
28
|
$s =~ s/\A/A:/xsmio |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
} |
54
|
2
|
|
|
|
|
4
|
my @sheets; |
55
|
2
|
50
|
|
|
|
7
|
if (@preferred_sheets) { |
56
|
2
|
|
|
|
|
7
|
@sheets = grep { exists $maps{$_} } @preferred_sheets; |
|
8
|
|
|
|
|
27
|
|
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
else { |
59
|
0
|
|
|
|
|
0
|
@sheets = keys %maps; |
60
|
|
|
|
|
|
|
} |
61
|
2
|
|
|
|
|
7
|
my ($map, $lle, $lln, $ure, $urn); |
62
|
2
|
|
|
|
|
0
|
my ($easting, $northing); |
63
|
2
|
|
|
|
|
5
|
while (1) { |
64
|
2
|
|
|
|
|
9
|
$map = $maps{$sheets[int rand @sheets]}; |
65
|
2
|
|
|
|
|
5
|
($lle, $lln) = @{$map->{bbox}->[0]}; |
|
2
|
|
|
|
|
9
|
|
66
|
2
|
|
|
|
|
5
|
($ure, $urn) = @{$map->{bbox}->[1]}; |
|
2
|
|
|
|
|
7
|
|
67
|
2
|
|
|
|
|
27
|
$easting = sprintf '%.3f', $lle + rand ($ure-$lle); |
68
|
2
|
|
|
|
|
12
|
$northing = sprintf '%.3f', $lln + rand ($urn-$lln); |
69
|
|
|
|
|
|
|
# check we are actually on the chosen map |
70
|
2
|
50
|
|
|
|
7
|
last if _winding_number($easting, $northing, $map->{polygon}); |
71
|
|
|
|
|
|
|
} |
72
|
2
|
|
|
|
|
11
|
return ($easting, $northing); |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub format_grid { |
76
|
213
|
|
|
213
|
1
|
30397
|
my ($easting, $northing, $options) = @_; |
77
|
|
|
|
|
|
|
|
78
|
213
|
100
|
|
|
|
689
|
my $form = exists $options->{form} ? uc $options->{form} : 'SS EEE NNN'; |
79
|
213
|
100
|
|
|
|
514
|
my $with_maps = exists $options->{maps} ? $options->{maps} : 0; |
80
|
213
|
100
|
|
|
|
1259
|
my $map_keys = exists $options->{series} ? uc $options->{series} : join '', sort keys %name_for_map_series; |
81
|
|
|
|
|
|
|
|
82
|
213
|
|
|
|
|
671
|
my $sq = _grid_to_sq($easting,$northing); |
83
|
213
|
50
|
|
|
|
510
|
if ( !$sq ) { |
84
|
0
|
|
|
|
|
0
|
croak "Too far off the grid: $easting $northing\n"; |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
|
87
|
213
|
|
|
|
|
474
|
my ($e,$n) = map { int } map { $_ % MINOR_GRID_SQ_SIZE } ($easting, $northing); |
|
426
|
|
|
|
|
787
|
|
|
426
|
|
|
|
|
938
|
|
88
|
|
|
|
|
|
|
|
89
|
213
|
|
|
|
|
435
|
my @sheets = (); |
90
|
213
|
100
|
|
|
|
450
|
if ( $with_maps ) { |
91
|
59
|
|
|
|
|
271
|
while (my ($k,$m) = each %maps) { |
92
|
66847
|
100
|
|
|
|
136651
|
next if index($map_keys, substr($k,0,1)) == -1; |
93
|
61142
|
100
|
100
|
|
|
309631
|
if ($m->{bbox}->[0][0] <= $easting && $easting < $m->{bbox}->[1][0] |
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
94
|
|
|
|
|
|
|
&& $m->{bbox}->[0][1] <= $northing && $northing < $m->{bbox}->[1][1]) { |
95
|
208
|
|
|
|
|
635
|
my $w = _winding_number($easting, $northing, $m->{polygon}); |
96
|
208
|
100
|
|
|
|
460
|
if ($w != 0) { |
97
|
199
|
|
|
|
|
781
|
push @sheets, $k; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
} |
101
|
59
|
|
|
|
|
403
|
@sheets = sort @sheets; |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# special cases |
105
|
213
|
100
|
|
|
|
736
|
if ( $form eq 'TRAD' ) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
106
|
2
|
|
|
|
|
7
|
$form = 'SS EEE NNN'; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
elsif ( $form eq 'GPS' ) { |
109
|
48
|
|
|
|
|
162
|
$form = 'SS EEEEE NNNNN'; |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
elsif ( $form eq 'SS' ) { |
112
|
1
|
|
|
|
|
5
|
return $sq; |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
|
115
|
212
|
50
|
|
|
|
2346
|
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 ) { |
116
|
212
|
|
|
|
|
440
|
my $e_len = length $e_spec; |
117
|
212
|
|
|
|
|
332
|
my $n_len = length $n_spec; |
118
|
212
|
|
|
|
|
666
|
$e = int($e / 10**(5 - $e_len)); |
119
|
212
|
|
|
|
|
423
|
$n = int($n / 10**(5 - $n_len)); |
120
|
|
|
|
|
|
|
|
121
|
212
|
100
|
|
|
|
456
|
if ( wantarray ) { |
122
|
49
|
|
|
|
|
516
|
return ($sq, $e, $n, @sheets) |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
163
|
|
|
|
|
694
|
my $gr = sprintf '%s%s%0.*d%s%0.*d', $sq, $space_a, $e_len, $e, $space_b, $n_len, $n; |
126
|
163
|
|
|
|
|
955
|
$gr =~ s/\s+/ /g; |
127
|
|
|
|
|
|
|
|
128
|
163
|
100
|
|
|
|
386
|
if ( $with_maps ) { |
129
|
12
|
50
|
|
|
|
39
|
if ( @sheets ) { |
130
|
12
|
|
|
|
|
204
|
return sprintf '%s on %s', $gr, join ', ', @sheets; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
else { |
133
|
0
|
|
|
|
|
0
|
return sprintf '%s is not on any maps in series %s', $gr, $map_keys; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
else { |
137
|
151
|
|
|
|
|
750
|
return $gr; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
0
|
|
|
|
|
0
|
croak "Format $form was not recognized"; |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
sub format_grid_trad { |
145
|
7
|
|
|
7
|
1
|
447
|
my ($easting, $northing) = @_; |
146
|
7
|
|
|
|
|
24
|
return format_grid($easting, $northing, { form => 'SS EEE NNN' }) |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub format_grid_GPS { |
150
|
10
|
|
|
10
|
1
|
504
|
my ($easting, $northing) = @_; |
151
|
10
|
|
|
|
|
46
|
return format_grid($easting, $northing, { form => 'SS EEEEE NNNNN' }) |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
sub format_grid_map { |
155
|
12
|
|
|
12
|
1
|
37
|
my ($easting, $northing, $options) = @_; |
156
|
12
|
100
|
|
|
|
33
|
if ( defined $options ) { |
157
|
2
|
|
|
|
|
7
|
$options->{maps} = 1 |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
else { |
160
|
10
|
|
|
|
|
28
|
$options = { maps => 1 } |
161
|
|
|
|
|
|
|
} |
162
|
12
|
|
|
|
|
41
|
return format_grid($easting, $northing, $options) |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
sub format_grid_landranger { |
166
|
4
|
|
|
4
|
1
|
386
|
my ($easting,$northing) = @_; |
167
|
|
|
|
|
|
|
|
168
|
4
|
|
|
|
|
29
|
my ($sq, $e, $n, @sheets) = format_grid($easting, $northing, { form => 'SS EEE NNN', maps => 1, series => 'A' }); |
169
|
|
|
|
|
|
|
|
170
|
4
|
|
|
|
|
20
|
for my $s (@sheets) { |
171
|
6
|
|
|
|
|
28
|
$s =~ s/\AA://xsm; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
4
|
100
|
|
|
|
39
|
return ($sq, $e, $n, @sheets) if wantarray; |
175
|
|
|
|
|
|
|
|
176
|
1
|
50
|
|
|
|
19
|
if (!@sheets ) { return sprintf '%s %03d %03d is not on any Landranger sheet', $sq, $e, $n } |
|
0
|
|
|
|
|
0
|
|
177
|
1
|
50
|
|
|
|
8
|
if ( @sheets==1 ) { return sprintf '%s %03d %03d on Landranger sheet %s' , $sq, $e, $n, $sheets[0] } |
|
1
|
|
|
|
|
15
|
|
178
|
0
|
0
|
|
|
|
0
|
if ( @sheets==2 ) { return sprintf '%s %03d %03d on Landranger sheets %s and %s', $sq, $e, $n, @sheets } |
|
0
|
|
|
|
|
0
|
|
179
|
|
|
|
|
|
|
|
180
|
0
|
|
|
|
|
0
|
my $phrase = join ', ', @sheets[0..($#sheets-1)], "and $sheets[-1]"; |
181
|
0
|
|
|
|
|
0
|
return sprintf '%s %03d %03d on Landranger sheets %s', $sq, $e, $n, $phrase; |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
sub _grid_to_sq { |
186
|
213
|
|
|
213
|
|
450
|
my ($e, $n) = @_; |
187
|
|
|
|
|
|
|
|
188
|
213
|
|
|
|
|
453
|
$e += MAJOR_GRID_SQ_EASTING_OFFSET; |
189
|
213
|
|
|
|
|
332
|
$n += MAJOR_GRID_SQ_NORTHING_OFFSET; |
190
|
213
|
50
|
33
|
|
|
1451
|
return if !(0 <= $e && $e < MAX_GRID_SIZE && 0 <= $n && $n < MAX_GRID_SIZE); |
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
191
|
|
|
|
|
|
|
|
192
|
213
|
|
|
|
|
625
|
my $major_index = int $e / MAJOR_GRID_SQ_SIZE + GRID_SIZE * int $n / MAJOR_GRID_SQ_SIZE; |
193
|
213
|
|
|
|
|
393
|
$e = $e % MAJOR_GRID_SQ_SIZE; |
194
|
213
|
|
|
|
|
293
|
$n = $n % MAJOR_GRID_SQ_SIZE; |
195
|
213
|
|
|
|
|
450
|
my $minor_index = int $e / MINOR_GRID_SQ_SIZE + GRID_SIZE * int $n / MINOR_GRID_SQ_SIZE; |
196
|
|
|
|
|
|
|
return |
197
|
213
|
|
|
|
|
730
|
substr(GRID_SQ_LETTERS, $major_index, 1) . |
198
|
|
|
|
|
|
|
substr(GRID_SQ_LETTERS, $minor_index, 1); |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
sub _get_grid_square_offsets { |
202
|
117
|
|
|
117
|
|
256
|
my $s = shift; |
203
|
117
|
50
|
|
|
|
377
|
return if length $s < 2; |
204
|
|
|
|
|
|
|
|
205
|
117
|
|
|
|
|
406
|
my $a = index GRID_SQ_LETTERS, uc substr $s, 0, 1; |
206
|
117
|
100
|
|
|
|
310
|
return if 0 > $a; |
207
|
|
|
|
|
|
|
|
208
|
109
|
|
|
|
|
249
|
my $b = index GRID_SQ_LETTERS, uc substr $s, 1, 1; |
209
|
109
|
100
|
|
|
|
387
|
return if 0 > $b; |
210
|
|
|
|
|
|
|
|
211
|
55
|
|
|
|
|
201
|
my ($X, $Y) = ($a % GRID_SIZE, int $a / GRID_SIZE); |
212
|
55
|
|
|
|
|
152
|
my ($x, $y) = ($b % GRID_SIZE, int $b / GRID_SIZE); |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
return ( |
215
|
55
|
|
|
|
|
238
|
MAJOR_GRID_SQ_SIZE * $X - MAJOR_GRID_SQ_EASTING_OFFSET + MINOR_GRID_SQ_SIZE * $x, |
216
|
|
|
|
|
|
|
MAJOR_GRID_SQ_SIZE * $Y - MAJOR_GRID_SQ_NORTHING_OFFSET + MINOR_GRID_SQ_SIZE * $y |
217
|
|
|
|
|
|
|
); |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
sub _get_eastnorthings { |
221
|
112
|
|
|
112
|
|
292
|
my $s = shift; |
222
|
112
|
|
|
|
|
220
|
my $numbers = $s; |
223
|
112
|
|
|
|
|
321
|
$numbers =~ tr/0-9//cd; # avoid using "r" here as it requires perl >= 5.14 |
224
|
112
|
|
|
|
|
231
|
my $len = length $numbers; |
225
|
112
|
50
|
|
|
|
295
|
croak 'No easting or northing found' if $len == 0; |
226
|
112
|
50
|
|
|
|
331
|
croak "Easting and northing have different lengths in $s" if $len % 2; |
227
|
112
|
50
|
|
|
|
277
|
croak "Too many digits in $s" if $len > 10; |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
# this trick lets us pad with zeros on the right |
230
|
112
|
|
|
|
|
684
|
my $e = reverse sprintf '%05d', scalar reverse substr $numbers, 0, $len/2; |
231
|
112
|
|
|
|
|
773
|
my $n = reverse sprintf '%05d', scalar reverse substr $numbers, $len/2; |
232
|
224
|
|
|
|
|
391
|
return ($e, $n) |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
sub parse_grid { |
236
|
|
|
|
|
|
|
|
237
|
117
|
100
|
|
117
|
1
|
6379
|
my $options = 'HASH' eq ref $_[-1] ? pop @_ : { }; |
238
|
|
|
|
|
|
|
|
239
|
117
|
100
|
|
|
|
363
|
my $figs = exists $options->{figs} ? $options->{figs} : 3; |
240
|
|
|
|
|
|
|
|
241
|
117
|
|
|
|
|
224
|
my @out; |
242
|
|
|
|
|
|
|
|
243
|
117
|
100
|
|
|
|
754
|
my $s = @_ < 3 ? "@_" : sprintf '%s %0.*d %0.*d', $_[0], $figs, $_[1], $figs, $_[2]; |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# normal case : TQ 123 456 etc |
246
|
117
|
100
|
|
|
|
382
|
if ( my ($E, $N) = _get_grid_square_offsets($s) ) { |
247
|
55
|
100
|
|
|
|
238
|
my ($e, $n) = length $s > 2 ? _get_eastnorthings(substr $s, 2) : (0,0); |
248
|
55
|
|
|
|
|
229
|
@out = ($E+$e, $N+$n); |
249
|
55
|
100
|
|
|
|
521
|
return wantarray ? @out : "@out"; |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
# sheet id instead of grid sq |
253
|
62
|
|
|
|
|
405
|
my $sheet_ref_pattern = qr{\A([A-Z]:)?([0-9NEWSOL/]+?)(\.[a-z]+)?(?:[ -/.]([ 0-9]+))?\Z}; |
254
|
62
|
|
|
|
|
698
|
my ($prefix, $sheet, $suffix, $numbers) = $s =~ m/$sheet_ref_pattern/sioxm; |
255
|
|
|
|
|
|
|
|
256
|
62
|
50
|
|
|
|
206
|
if (defined $sheet) { |
257
|
|
|
|
|
|
|
|
258
|
62
|
100
|
|
|
|
180
|
if ( ! defined $prefix ) { |
259
|
8
|
|
|
|
|
16
|
$prefix = 'A:'; |
260
|
|
|
|
|
|
|
} |
261
|
62
|
100
|
|
|
|
175
|
if ( ! defined $suffix ) { |
262
|
61
|
|
|
|
|
115
|
$suffix = q{}; |
263
|
|
|
|
|
|
|
} |
264
|
62
|
|
|
|
|
151
|
$sheet = $prefix . $sheet . $suffix; |
265
|
|
|
|
|
|
|
|
266
|
62
|
50
|
|
|
|
224
|
if (exists $maps{$sheet}) { |
267
|
62
|
|
|
|
|
137
|
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
|
|
|
|
|
225
|
|
268
|
|
|
|
|
|
|
|
269
|
62
|
100
|
|
|
|
158
|
if (defined $numbers) { |
270
|
59
|
|
|
|
|
162
|
my ($e, $n) = _get_eastnorthings($numbers); |
271
|
59
|
|
|
|
|
183
|
$E = $E + ($e-$E) % MINOR_GRID_SQ_SIZE; |
272
|
59
|
|
|
|
|
142
|
$N = $N + ($n-$N) % MINOR_GRID_SQ_SIZE; |
273
|
|
|
|
|
|
|
|
274
|
59
|
|
|
|
|
189
|
my $w = _winding_number($E, $N, $maps{$sheet}->{polygon}); |
275
|
59
|
50
|
|
|
|
165
|
if ($w == 0) { |
276
|
0
|
|
|
|
|
0
|
croak sprintf 'Grid reference %s = (%d, %d) is not on sheet %s', scalar format_grid($E,$N), $e, $n, $sheet; |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
} |
279
|
62
|
100
|
|
|
|
559
|
return wantarray ? ($E, $N) : "$E $N"; |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
# just a pair of numbers |
284
|
0
|
0
|
|
|
|
0
|
if ( @out = $s =~ m{\A (\d+(?:\.\d+)?) \s+ (\d+(?:\.\d+)?) \Z}xsmio ) { # eee nnn |
285
|
0
|
0
|
|
|
|
0
|
return wantarray ? @out : "@out"; |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
0
|
|
|
|
|
0
|
croak "Failed to parse a grid reference from $s"; |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
*parse_trad_grid = \&parse_grid; |
292
|
|
|
|
|
|
|
*parse_GPS_grid = \&parse_grid; |
293
|
|
|
|
|
|
|
*parse_landranger_grid = \&parse_grid; |
294
|
|
|
|
|
|
|
*parse_map_grid = \&parse_grid; |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
# is $pt left of $a--$b? |
297
|
|
|
|
|
|
|
sub _is_left { |
298
|
538
|
|
|
538
|
|
1017
|
my ($x, $y, $a, $b) = @_; |
299
|
538
|
|
|
|
|
2228
|
return ( ($b->[0] - $a->[0]) * ($y - $a->[1]) - ($x - $a->[0]) * ($b->[1] - $a->[1]) ); |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
# adapted from http://geomalgorithms.com/a03-_inclusion.html |
303
|
|
|
|
|
|
|
sub _winding_number { |
304
|
269
|
|
|
269
|
|
555
|
my ($x, $y, $poly) = @_; |
305
|
269
|
|
|
|
|
431
|
my $w = 0; |
306
|
269
|
|
|
|
|
712
|
for (my $i=0; $i < $#$poly; $i++ ) { |
307
|
4186
|
100
|
|
|
|
6633
|
if ( $poly->[$i][1] <= $y ) { |
308
|
1829
|
100
|
100
|
|
|
5089
|
if ($poly->[$i+1][1] > $y && _is_left($x, $y, $poly->[$i], $poly->[$i+1]) > 0 ) { |
309
|
266
|
|
|
|
|
1194
|
$w++; |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
} |
312
|
|
|
|
|
|
|
else { |
313
|
2357
|
100
|
100
|
|
|
5806
|
if ($poly->[$i+1][1] <= $y && _is_left($x, $y, $poly->[$i], $poly->[$i+1]) < 0 ) { |
314
|
6
|
|
|
|
|
17
|
$w--; |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
} |
318
|
269
|
|
|
|
|
518
|
return $w; |
319
|
|
|
|
|
|
|
} |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
1; |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
__END__ |