File Coverage

blib/lib/Geo/Shape.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             # Copyrights 2005-2014 by [Mark Overmeer].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.01.
5              
6 6     6   41 use strict;
  6         14  
  6         211  
7 6     6   34 use warnings;
  6         16  
  6         226  
8              
9             package Geo::Shape;
10 6     6   33 use vars '$VERSION';
  6         13  
  6         370  
11             $VERSION = '0.96';
12              
13              
14 6     6   4274 use Geo::Proj; # defines wgs84
  0            
  0            
15             use Geo::Point ();
16             use Geo::Line ();
17             use Geo::Surface ();
18             use Geo::Space ();
19              
20             use Geo::Distance ();
21             use Math::Trig qw/deg2rad/;
22             use Carp qw/croak confess/;
23              
24              
25             use overload '""' => 'string'
26             , bool => sub {1}
27             , fallback => 1;
28              
29              
30             sub new(@) { my $class = shift; (bless {}, $class)->init( {@_} ) }
31              
32             sub init($)
33             { my ($self, $args) = @_;
34             my $proj = $self->{G_proj}
35             = $args->{proj} || Geo::Proj->defaultProjection->nick;
36              
37             croak "proj parameter must be a label, not a Geo::Proj object"
38             if UNIVERSAL::isa($proj, 'Geo::Proj');
39              
40             $self;
41             }
42              
43             #---------------------------
44              
45             sub proj() { shift->{G_proj} }
46             sub proj4() { Geo::Proj->proj4(shift->{G_proj}) }
47              
48             #---------------------------
49              
50             sub in($) { croak "ERROR: in() not implemented for a ".ref(shift) }
51              
52              
53             sub projectOn($@)
54             { # fast check: nothing to be done
55             return () if @_<2 || $_[0]->{G_proj} eq $_[1];
56              
57             my ($self, $projnew) = (shift, shift);
58             my $projold = $self->{G_proj};
59              
60             return ($projnew, @_)
61             if $projold eq $projnew;
62              
63             if($projnew eq 'utm')
64             { my $point = $_[0];
65             $point = Geo::Point->xy(@$point, $projold)
66             if ref $point eq 'ARRAY';
67             $projnew = Geo::Proj->bestUTMprojection($point, $projold)->nick;
68             return ($projnew, @_)
69             if $projnew eq $projold;
70             }
71              
72             my $points = Geo::Proj->to($projold, $projnew, \@_);
73             ($projnew, @$points);
74             }
75              
76             #---------------------------
77              
78             my $geodist;
79             sub distance($;$)
80             { my ($self, $other, $unit) = (shift, shift, shift);
81             $unit ||= 'kilometer';
82              
83             unless($geodist)
84             { $geodist = Geo::Distance->new;
85             $geodist->formula('hsin');
86             $geodist->reg_unit(1 => 'radians');
87             $geodist->reg_unit(deg2rad(1) => 'degrees');
88             $geodist->reg_unit(km => 1, 'kilometer');
89             }
90              
91             my $proj = $self->proj;
92             $other = $other->in($proj)
93             if $other->proj ne $proj;
94              
95             if($self->isa('Geo::Point') && $other->isa('Geo::Point'))
96             { return $self->distancePointPoint($geodist, $unit, $other);
97             }
98              
99             die "ERROR: distance calculation not implemented between a "
100             . ref($self) . " and a " . ref($other);
101             }
102              
103              
104             sub bboxRing(@)
105             { my ($thing, $xmin, $ymin, $xmax, $ymax, $proj) = @_;
106              
107             if(@_==1 && ref $_[0]) # instance method without options
108             { $proj = $thing->proj;
109             ($xmin, $ymin, $xmax, $ymax) = $thing->bbox;
110             }
111              
112             Geo::Line->new # just a little faster than calling ring()
113             ( points => [ [$xmin,$ymin], [$xmax,$ymin], [$xmax,$ymax]
114             , [$xmin,$ymax], [$xmin,$ymin] ]
115             , proj => $proj
116             , ring => 1
117             , bbox => [$xmin, $ymin, $xmax, $ymax]
118             , clockwise => 0
119             );
120             }
121              
122              
123             sub bbox() { confess "INTERNAL: bbox() not implemented for ".ref(shift) }
124              
125              
126             sub bboxCenter()
127             { my $self = shift;
128             my ($xmin, $ymin, $xmax, $ymax) = $self->bbox;
129             Geo::Point->xy(($xmin+$xmax)/2, ($ymin+$ymax)/2, $self->proj);
130             }
131              
132              
133             sub area() { confess "INTERNAL: area() not implemented for ".ref(shift) }
134              
135              
136             sub perimeter() { confess "INTERNAL: perimeter() not implemented for ".ref(shift) }
137              
138              
139             sub deg2dms($$$)
140             { my ($thing, $degrees, $pos, $neg) = @_;
141             $degrees -= 360 while $degrees > 180;
142             $degrees += 360 while $degrees <= -180;
143              
144             my $sign = $pos;
145             if($degrees < 0)
146             { $sign = $neg;
147             $degrees= -$degrees;
148             }
149              
150             my $d = int $degrees;
151             my $frac = ($degrees - $d) * 60;
152             my $m = int($frac + 0.00001);
153             my $s = ($frac - $m) * 60;
154             $s = 0 if $s < 0.001;
155              
156             my $g = int($s + 0.00001);
157             my $h = int(($s - $g) * 1000 + 0.0001);
158             $h ? sprintf("%dd%02d'%02d.%03d\"$sign", $d, $m, $g, $h)
159             : $s ? sprintf("%dd%02d'%02d\"$sign", $d, $m, $g)
160             : $m ? sprintf("%dd%02d'$sign", $d, $m)
161             : sprintf("%d$sign", $d);
162             }
163              
164              
165             sub deg2dm($$$)
166             { my ($thing, $degrees, $pos, $neg) = @_;
167             defined $degrees or return '(null)';
168              
169             $degrees -= 360 while $degrees > 180;
170             $degrees += 360 while $degrees <= -180;
171              
172             my $sign = $pos;
173             if($degrees < 0)
174             { $sign = $neg;
175             $degrees= -$degrees;
176             }
177              
178             my $d = int $degrees;
179             my $frac = ($degrees - $d) * 60;
180             my $m = int($frac + 0.00001);
181              
182             $m ? sprintf("%dd%02d'$sign", $d, $m)
183             : sprintf("%d$sign", $d);
184             }
185              
186              
187             sub dms2deg($)
188             { my ($thing, $dms) = @_;
189              
190             my $o = 'E';
191             $dms =~ s/^\s+//;
192              
193             if($dms =~ s/([ewsn])\s*$//i) { $o = uc $1 }
194             elsif($dms =~ s/^([ewsn])\s*//i) { $o = uc $1 }
195              
196             if($dms =~ m/^( [+-]? \d+ (?: \.\d+)? ) [\x{B0}dD]?
197             \s* (?: ( \d+ (?: \.\d+)? ) [\'mM\x{92}]? )?
198             \s* (?: ( \d+ (?: \.\d+)? ) [\"sS]? )?
199             /xi
200             )
201             { my ($d, $m, $s) = ($1, $2||0, $3||0);
202              
203             my $deg = ($o eq 'W' || $o eq 'S' ? -1 : 1)
204             * ($d + $m/60 + $s/3600);
205              
206             return $deg;
207             }
208              
209             ();
210             }
211              
212             1;