File Coverage

blib/lib/URI/geo.pm
Criterion Covered Total %
statement 104 111 93.6
branch 38 54 70.3
condition 9 24 37.5
subroutine 26 26 100.0
pod 8 8 100.0
total 185 223 82.9


line stmt bran cond sub pod time code
1             package URI::geo;
2              
3 3     3   12581 use warnings;
  3         7  
  3         179  
4 3     3   15 use strict;
  3         5  
  3         78  
5              
6 3     3   11 use Carp;
  3         5  
  3         267  
7 3     3   1544 use URI::Split qw( uri_split uri_join );
  3         8  
  3         267  
8              
9 3     3   24 use base qw( URI );
  3         5  
  3         3107  
10              
11             our $VERSION = '5.34';
12              
13 24     24   85 sub _MINIMUM_LATITUDE { return -90 }
14 26     26   110 sub _MAXIMUM_LATITUDE { return 90 }
15 9     9   40 sub _MINIMUM_LONGITUDE { return -180 }
16 9     9   32 sub _MAXIMUM_LONGITUDE { return 180 }
17 4     4   11 sub _MAX_POINTY_PARAMETERS { return 3 }
18              
19             sub _can {
20 8     8   20 my ($can_pt, @keys) = @_;
21 8         16 for my $key (@keys) {
22 9 100       59 return $key if $can_pt->can($key);
23             }
24 3         12 return;
25             }
26              
27             sub _has {
28 6     6   15 my ($has_pt, @keys) = @_;
29 6         12 for my $key (@keys) {
30 14 100       40 return $key if exists $has_pt->{$key};
31             }
32 1         4 return;
33             }
34              
35             # Try hard to extract location information from something. We handle lat,
36             # lon, alt as scalars, arrays containing lat, lon, alt, hashes with
37             # suitably named keys and objects with suitably named methods.
38              
39             sub _location_of_pointy_thing {
40 10     10   21 my ($class, @parameters) = @_;
41              
42 10         28 my @lat = qw( lat latitude );
43 10         31 my @lon = qw( lon long longitude lng );
44 10         28 my @ele = qw( ele alt elevation altitude );
45              
46 10 100       30 if (ref $parameters[0]) {
47 6         16 my $pt = shift @parameters;
48              
49 6 50       22 if (@parameters) {
50 0         0 croak q[Too many arguments];
51             }
52              
53 6 100       12 if (eval { $pt->can('can') }) {
  6 100       98  
    50          
54 3         7 for my $m (qw( location latlong )) {
55 5 100       13 return $pt->$m() if _can($pt, $m);
56             }
57              
58 1         4 my $latk = _can($pt, @lat);
59 1         4 my $lonk = _can($pt, @lon);
60 1         3 my $elek = _can($pt, @ele);
61              
62 1 50 33     8 if (defined $latk && defined $lonk) {
63 1 50       6 return $pt->$latk(), $pt->$lonk(),
64             defined $elek ? $pt->$elek() : undef;
65             }
66             }
67             elsif ('ARRAY' eq ref $pt) {
68 1         3 return $class->_location_of_pointy_thing(@{$pt});
  1         6  
69             }
70             elsif ('HASH' eq ref $pt) {
71              
72 2         14 my $latk = _has($pt, @lat);
73 2         5 my $lonk = _has($pt, @lon);
74 2         6 my $elek = _has($pt, @ele);
75              
76 2 50 33     14 if (defined $latk && defined $lonk) {
77             return $pt->{$latk}, $pt->{$lonk},
78 2 100       16 defined $elek ? $pt->{$elek} : undef;
79             }
80             }
81              
82 0         0 croak q[Don't know how to convert point];
83             }
84             else {
85 4 50 33     20 croak q[Need lat, lon or lat, lon, alt]
86             if @parameters < 2 || @parameters > _MAX_POINTY_PARAMETERS();
87 4         21 return my ($lat, $lon, $alt) = @parameters;
88             }
89             }
90              
91             sub _num {
92 36     36   55 my ($class, $n) = @_;
93 36 50       58 if (!defined $n) {
94 0         0 return q[];
95             }
96 36         283 (my $rep = sprintf '%f', $n) =~ s/[.]0*$//smx;
97 36         181 return $rep;
98             }
99              
100             sub new {
101 9     9 1 578419 my ($self, @parameters) = @_;
102 9   33     54 my $class = ref $self || $self;
103 9         31 my $uri = uri_join 'geo', undef, $class->_path(@parameters);
104 9         82 return bless \$uri, $class;
105             }
106              
107             sub _init {
108 9     9   14 my ($class, $uri, $scheme) = @_;
109              
110 9         64 my $self = $class->SUPER::_init($uri, $scheme);
111              
112             # Normalise at poles.
113 9         14 my $lat = $self->latitude;
114 8 100 66     10 if ($lat == _MAXIMUM_LATITUDE() || $lat == _MINIMUM_LATITUDE()) {
115 2         3 $self->longitude(0);
116             }
117 8         36 return $self;
118             }
119              
120             sub location {
121 4     4 1 11 my ($self, @parameters) = @_;
122              
123 4 50       14 if (@parameters) {
124 0         0 my ($lat, $lon, $alt) = @parameters;
125 0         0 return $self->latitude($lat)->longitude($lon)->altitude($alt);
126             }
127              
128 4         12 return $self->latitude, $self->longitude, $self->altitude;
129             }
130              
131             sub latitude {
132 23     23 1 48 my ($self, @parameters) = @_;
133 23         923 return $self->field('latitude', @parameters);
134             }
135              
136             sub longitude {
137 16     16 1 35 my ($self, @parameters) = @_;
138 16         70 return $self->field('longitude', @parameters);
139             }
140              
141             sub altitude {
142 15     15 1 40 my ($self, @parameters) = @_;
143 15         56 return $self->field('altitude', @parameters);
144             }
145              
146             sub crs {
147 1     1 1 796 my ($self, @parameters) = @_;
148 1         3 return $self->field('crs', @parameters);
149             }
150              
151             sub uncertainty {
152 1     1 1 3 my ($self, @parameters) = @_;
153 1         5 return $self->field('uncertainty', @parameters);
154             }
155              
156             sub field {
157 56     56 1 107 my ($self, $name, @remainder) = @_;
158 56         105 my ($scheme, $auth, $v, $query, $frag) = $self->_parse;
159              
160 55 50       164 if (!exists $v->{$name}) {
161 0         0 croak "No such field: $name";
162             }
163 55 100       103 if (!@remainder) {
164 50         310 return $v->{$name};
165             }
166 5         8 $v->{$name} = shift @remainder;
167 5         8 ${$self} = uri_join $scheme, $auth, $self->_format($v), $query, $frag;
  5         8  
168 5         19 return $self;
169             }
170              
171             {
172             my $pnum = qr{\d+(?:[.]\d+)?}smx;
173             my $num = qr{-?$pnum}smx;
174             my $crsp = qr{(?:;crs=(\w+))}smx;
175             my $uncp = qr{(?:;u=($pnum))}smx;
176             my $parm = qr{(?:;\w+=[^;]*)+}smx;
177              
178             sub _parse {
179 56     56   95 my $self = shift;
180 56         74 my ($scheme, $auth, $path, $query, $frag) = uri_split ${$self};
  56         197  
181              
182 56 100       1523 $path =~ m{^ ($num), ($num) (?: , ($num) ) ?
183             (?: $crsp ) ?
184             (?: $uncp ) ?
185             ( $parm ) ?
186             $}smx or croak 'Badly formed geo uri';
187              
188             # No named captures before 5.10.0
189 55 50       569 return $scheme, $auth,
190             {
191             latitude => $1,
192             longitude => $2,
193             altitude => $3,
194             crs => $4,
195             uncertainty => $5,
196             parameters => (defined $6 ? substr $6, 1 : undef),
197             },
198             $query, $frag;
199             }
200             }
201              
202             sub _format {
203 14     14   30 my ($class, $v) = @_;
204             return join q[;],
205             (
206             join q[,],
207 36         65 map { $class->_num($_) } @{$v}{'latitude', 'longitude'},
  14         54  
208             (defined $v->{altitude} ? ($v->{altitude}) : ())
209             ),
210             (defined $v->{crs} ? ('crs=' . $class->_num($v->{crs})) : ()),
211             (
212             defined $v->{uncertainty}
213             ? ('u=' . $class->_num($v->{uncertainty}))
214 14 100       25 : ()), (defined $v->{parameters} ? ($v->{parameters}) : ());
    50          
    50          
    50          
215             }
216              
217             sub _path {
218 9     9   24 my ($class, @parameters) = @_;
219 9         37 my ($lat, $lon, $alt) = $class->_location_of_pointy_thing(@parameters);
220 9 50 33     55 croak 'Latitude out of range'
221             if $lat < _MINIMUM_LATITUDE() || $lat > _MAXIMUM_LATITUDE();
222 9 50 33     29 croak 'Longitude out of range'
223             if $lon < _MINIMUM_LONGITUDE() || $lon > _MAXIMUM_LONGITUDE();
224 9 50 33     19 if ($lat == _MINIMUM_LATITUDE() || $lat == _MAXIMUM_LATITUDE()) {
225 0         0 $lat = 0;
226             }
227 9         56 return $class->_format(
228             {latitude => $lat, longitude => $lon, altitude => $alt});
229             }
230              
231             1;
232              
233             __END__