File Coverage

blib/lib/Geo/Coordinates/Converter.pm
Criterion Covered Total %
statement 117 125 93.6
branch 27 36 75.0
condition 12 24 50.0
subroutine 22 24 91.6
pod 6 14 42.8
total 184 223 82.5


line stmt bran cond sub pod time code
1             package Geo::Coordinates::Converter;
2 8     8   498027 use strict;
  8         16  
  8         264  
3 8     8   39 use warnings;
  8         13  
  8         325  
4             use Class::Accessor::Lite (
5 8         58 rw => [qw/ source current /],
6 8     8   6616 );
  8         9576  
7              
8 8     8   1001 use 5.008001;
  8         52  
  8         415  
9              
10             our $VERSION = '0.13';
11              
12 8     8   38 use Carp;
  8         14  
  8         795  
13 8     8   9983 use String::CamelCase qw( camelize );
  8         6560  
  8         623  
14 8     8   10453 use Module::Load ();
  8         10620  
  8         1393  
15              
16 8     8   4331 use Geo::Coordinates::Converter::Point;
  8         24  
  8         8846  
17              
18             our $DEFAULT_CONVERTER = 'Geo::Coordinates::Converter::Datum';
19             our $DEFAULT_FORMAT = [qw/ Degree Dms Milliseconds ISO6709 /];
20             our $DEFAULT_INETRNAL_FORMAT = 'degree';
21              
22             sub add_default_formats {
23 0     0 0 0 my($class, @formats) = @_;
24 0         0 my %default_formats = map { $_ => 1 } @{ $DEFAULT_FORMAT }, @formats;
  0         0  
  0         0  
25 0         0 $DEFAULT_FORMAT = [ keys %default_formats ];
26             }
27              
28             sub new {
29 68     68 1 725185 my($class, %opt) = @_;
30              
31 68   33     564 my $converter = delete $opt{converter} || $DEFAULT_CONVERTER;
32 68 50       256 unless (ref $converter) {
33 68         361 Module::Load::load($converter);
34 68 50       11070 $converter = $converter->new unless ref $converter;
35             }
36              
37 68   33     408 my $internal_format = delete $opt{internal_format} || $DEFAULT_INETRNAL_FORMAT;
38 68         2288 my $formats = delete $opt{formats};
39 68   66     690 my $source = delete $opt{point} || Geo::Coordinates::Converter::Point->new(\%opt);
40              
41 68         2729 my $self = bless {
42             source => $source,
43             formats => {},
44             converter => $converter,
45             internal_format => $internal_format,
46             }, $class;
47              
48 68         112 my @plugins = @{ $DEFAULT_FORMAT };
  68         321  
49 68 50       1182 push @plugins, @{ $formats } if ref $formats eq 'ARRAY';
  0         0  
50 68         153 for my $plugin (@plugins) {
51 272         2375 $self->load_format($plugin);
52             }
53              
54 68 50       322 $self->format_detect($self->source) unless $source->format;
55 68         401 $self->normalize($self->source);
56 68         249 $self->reset;
57              
58 68         785 $self;
59             }
60              
61             sub load_format {
62 272     272 0 414 my($self, $format) = @_;
63              
64 272 50       629 unless (ref $format) {
65 272 50       672 if ($format =~ s/^\+//) {
66 0         0 Module::Load::load($format);
67             } else {
68 272         486 my $name = $format;
69 272         967 $format = sprintf '%s::Format::%s', ref $self, camelize($name);
70 272         5623 Module::Load::load($format);
71             }
72 272         21066 $format = $format->new;
73             }
74 272         1482 $self->formats($format->name, $format);
75             }
76              
77             sub formats {
78 932     932 1 3167 my($self, $format, $plugin) = @_;
79 932 100       2617 $self->{formats}->{$format} = $plugin if $plugin;
80 932 100       7091 wantarray ? keys %{ $self->{formats} } : $self->{formats}->{$format};
  68         318  
81             }
82              
83             sub format_detect {
84 68     68 0 1919 my($self, $point) = @_;
85              
86 68         241 for my $format ($self->formats) {
87 174         593 my $name = $self->formats($format)->detect($point);
88 174 100       14213 next unless $name;
89 68         209 $point->format($name);
90 68         2666 last;
91             }
92 68         239 $point->format;
93             }
94              
95 0     0 0 0 sub normaraiz { goto &normalize; } # alias for backward compatibility.
96             sub normalize {
97 68     68 0 393 my($self, $point) = @_;
98 68         192 $self->formats($point->format)->normalize($point);
99 68         915 $point;
100             }
101              
102             sub convert {
103 10     10 1 167 my($self, @opt) = @_;
104 10 50       33 return $self->point unless @opt;
105              
106 10         46 my $point = $self->source->clone;
107 10         46 my $format = $point->format;
108 10         92 $self->format($self->{internal_format}, $point);
109 10         35 for my $type (@opt) {
110 12 100       32 if ($self->formats($type)) {
111 8 100       43 $format = $type unless $format eq $type;
112             } else {
113 4         6 eval { $self->datum($type, $point) };
  4         17  
114 4 50       27 croak "It dosen't correspond to the $type format/datum: $@" if $@;
115             }
116             }
117 10         35 $self->format($format, $point);
118              
119 10         79 $point->$_( $self->$_($point) ) for qw/ lat lng /;
120 10         106 $self->current($point->clone);
121             }
122              
123             for my $meth (qw/ lat lng /) {
124 8     8   76 no strict 'refs';
  8         12  
  8         5786  
125             *{__PACKAGE__ . "::$meth"} = sub {
126 136     136   62752 my $self = shift;
127 136   66     631 my $point = shift || $self->current;
128 136         3262 $self->formats($point->format)->round($point->$meth);
129             };
130             }
131             sub height {
132 13     13 0 4382 my $self = shift;
133 13   33     48 my $point = shift || $self->current;
134 13         115 $point->height;
135             }
136              
137             sub datum {
138 22     22 1 4262 my $self = shift;
139              
140 22 100       63 if (my $datum = shift) {
141 9   66     41 my $point = shift || $self->current;
142 9 100       56 return $self if $point->datum eq $datum;
143              
144 8         64 my $format = $point->format;
145 8         64 $self->format($self->{internal_format}, $point);
146 8         75 $self->{converter}->convert($point => $datum);
147 8         26 $self->format($format, $point);
148              
149 8         38 return $self;
150             } else {
151 13         33 return $self->current->datum;
152             }
153             }
154              
155             sub format {
156 89     89 1 27718 my $self = shift;
157              
158 89 100       262 if (my $fmt = shift) {
159 72 50       168 croak "It dosen't correspond to the $fmt format" unless $self->formats($fmt);
160 72   66     288 my $point = shift || $self->current;
161 72 100       366 return $self if $point->format eq $fmt;
162              
163 58         417 $self->formats($point->format)->to($point);
164 58         159 $self->formats($fmt)->from($point);
165 58         167 $point->format($fmt);
166              
167 58         372 return $self;
168             } else {
169 17         92 return $self->current->format;
170             }
171             }
172              
173             sub round {
174 14     14 0 18 my($self, $point) = @_;
175 14         35 my $fmt = $self->formats($point->format);
176 14         56 $point->$_($fmt->round($point->$_)) for (qw/ lat lng /);
177 14         111 $point;
178             }
179              
180             sub point {
181 14     14 1 39 my($self, $point) = @_;
182 14   33     50 $point ||= $self->current;
183 14         91 $self->round($point->clone);
184             }
185              
186             sub reset {
187 68     68 0 108 my $self = shift;
188 68         195 $self->current($self->source->clone);
189 68         468 $self;
190             }
191              
192             1;
193              
194             __END__