File Coverage

lib/Geo/Proj.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 7     7   854 use strict;
  7         15  
  7         253  
7 7     7   38 use warnings;
  7         12  
  7         338  
8              
9             package Geo::Proj;
10 7     7   47 use vars '$VERSION';
  7         13  
  7         329  
11             $VERSION = '0.96';
12              
13              
14 7     7   19511 use Geo::Proj4 ();
  0            
  0            
15             use Carp qw/croak/;
16              
17              
18             use overload '""' => sub { shift->nick }
19             , fallback => 1;
20              
21              
22             sub import()
23             {
24             Geo::Proj->new
25             ( nick => 'wgs84'
26             , proj4 => '+proj=latlong +datum=WGS84 +ellps=WGS84'
27             );
28             }
29              
30              
31             my %projections;
32             my $defproj;
33              
34             sub new(@)
35             { my ($class, %args) = @_;
36             my $proj = $projections{$args{nick} || 'dead'};
37             return $proj if defined $proj;
38              
39             my $self = (bless {}, $class)->init(\%args);
40             $projections{$self->nick} = $self;
41             $defproj ||= $self;
42             $self;
43             }
44              
45             sub init($)
46             { my ($self, $args) = @_;
47              
48             my $nick = $self->{GP_nick} = $args->{nick}
49             or croak "ERROR: nick required";
50              
51             $self->{GP_srid} = $args->{srid};
52              
53             my $proj4 = $args->{proj4}
54             or croak "ERROR: proj4 parameter required";
55              
56             if(ref $proj4 eq 'ARRAY')
57             { $proj4 = Geo::Proj4->new(@$proj4);
58             croak "ERROR: cannot create proj4: ".Geo::Proj4->error
59             unless $proj4;
60             }
61             elsif(!ref $proj4)
62             { $proj4 = Geo::Proj4->new($proj4);
63             croak "ERROR: cannot create proj4: ".Geo::Proj4->error
64             unless $proj4;
65             }
66             $self->{GP_proj4} = $proj4;
67             $self->{GP_name} = $args->{name};
68             $self;
69             }
70              
71              
72             sub nick() {shift->{GP_nick}}
73              
74              
75             sub name()
76             { my $self = shift;
77             my $name = $self->{GP_name};
78             return $name if defined $name;
79              
80             my $proj = $self->proj4;
81             my $abbrev = $proj->projection
82             or return $self->{nick};
83              
84             my $def = $proj->type($abbrev);
85             $def->{description};
86             }
87              
88              
89             sub proj4(;$)
90             { my $thing = shift;
91             return $thing->{GP_proj4} unless @_;
92              
93             my $proj = $thing->projection(shift) or return undef;
94             $proj->proj4;
95             }
96              
97              
98             sub srid() {shift->{GP_srid}}
99              
100              
101             sub projection($)
102             { my $which = $_[1];
103             UNIVERSAL::isa($which, __PACKAGE__) ? $which : $projections{$which};
104             }
105              
106              
107             sub defaultProjection(;$)
108             { my $thing = shift;
109             if(@_)
110             { my $proj = shift;
111             $defproj = ref $proj ? $proj : $thing->projection($proj);
112             }
113             $defproj;
114             }
115              
116              
117             sub listProjections() { sort keys %projections }
118              
119              
120             sub dumpProjections(;$)
121             { my $class = shift;
122             my $fh = shift || select;
123              
124             my $default = $class->defaultProjection;
125             my $defnick = defined $default ? $default->nick : '';
126              
127             foreach my $nick ($class->listProjections)
128             { my $proj = $class->projection($nick);
129             my $name = $proj->name;
130             my $norm = $proj->proj4->normalized;
131             $fh->print("$nick: $name".($defnick eq $nick ? ' (default)':'')."\n");
132             $fh->print(" $norm\n") if $norm ne $name;
133             }
134             }
135              
136              
137             sub to($@)
138             { my $thing = shift;
139             my $myproj4 = ref $thing ? $thing->proj4 : __PACKAGE__->proj4(shift);
140             my $toproj4 = __PACKAGE__->proj4(shift);
141             $myproj4->transform($toproj4, shift);
142             }
143              
144              
145             # These methods may have been implemented in Geo::Point, however may get
146             # supported by any external library later. Knowledge about projections
147             # is as much as possible concentrated here.
148              
149              
150             sub zoneForUTM($)
151             { my ($thing, $point) = @_;
152             my ($long, $lat) = $point->longlat;
153              
154             my $zone
155             = ($lat >= 56 && $lat < 64)
156             ? ( $long < 3 ? undef
157             : $long < 12 ? 32
158             : undef
159             )
160             : ($lat >= 72 && $lat < 84)
161             ? ( $long < 0 ? undef
162             : $long < 9 ? 31
163             : $long < 21 ? 33
164             : $long < 33 ? 35
165             : $long < 42 ? 37
166             : undef
167             )
168             : undef;
169              
170             my $meridian = int($long/6)*6 + ($long < 0 ? -3 : +3);
171             $zone ||= int(($meridian+180)/6) +1;
172            
173             my $letter
174             = ($lat < -80 || $lat > 84) ? ''
175             : ('C'..'H', 'J'..'N', 'P'..'X', 'X')[ ($lat+80)/8 ];
176              
177             wantarray ? ($zone, $letter, $meridian)
178             : defined $zone ? "$zone$letter"
179             : undef;
180             }
181              
182              
183             sub bestUTMprojection($;$)
184             { my ($thing, $point) = (shift, shift);
185             my $proj = @_ ? shift : $point->proj;
186              
187             my ($zone, $letter, $meridian) = $thing->zoneForUTM($point);
188             $thing->UTMprojection($proj, $zone);
189             }
190              
191              
192             sub UTMprojection($$)
193             { my ($class, $base, $zone) = @_;
194              
195             $base ||= $class->defaultProjection;
196             my $datum = UNIVERSAL::isa($base, __PACKAGE__) ? $base->proj4->datum :$base;
197             $datum ||= 'wgs84';
198              
199             my $label = "utm$zone-\L$datum\E";
200             my $proj = "+proj=utm +zone=$zone +datum=\U$datum\E"
201             . " +ellps=\U$datum\E +units=m +no_defs";
202              
203             Geo::Proj->new(nick => $label, proj4 => $proj);
204             }
205              
206             1;