File Coverage

blib/lib/Locale/Geocode.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             package Locale::Geocode;
2              
3 1     1   3676 use strict;
  1         3  
  1         100  
4              
5             =head1 NAME
6              
7             Locale::Geocode
8              
9             =head1 DESCRIPTION
10              
11             Locale::Geocode is a module that provides an
12             interface with which to find codes and information
13             on geographical locations and their administrative
14             subdivisions as defined primarily by ISO 3166-1
15             and ISO 3166-2. It is the most complete ISO 3166
16             module available on CPAN.
17              
18             Also included are, where applicable, FIPS codes.
19              
20             =head1 SYNOPSIS
21              
22             my $lc = new Locale::Geocode;
23              
24             # retrieve a Locale::Geocode::Territory object
25             # for the ISO 3166-1 alpha-2 code 'US'
26             my $lct = $lc->lookup('US');
27              
28             # retrieve ISO 3166-1 information for US
29             my $name = $lct->name; # United States
30             my $alpha2 = $lct->alpha2; # US
31             my $alpha3 = $lct->alpha3; # USA
32             my $num = $lct->num; # 840
33              
34             # lookup a subdivision of US
35             my $lcd = $lct->lookup('TN');
36              
37             # retrieve ISO 3166-2 information for US-TN
38             my $name = $lcd->name; # Tennessee
39             my $code = $lcd->code; # TN
40              
41             # returns an array of Locale::Geocode::Division
42             # objects representing all divisions of US
43             my @divs = $lct->divisions;
44              
45             # retrieve a Locale::Geocode::Division object
46             # for the ISO 3166-1/ISO 3166-2 combo 'GB-ESS'
47             my $lct = $lc->lookup('GB-ESS');
48              
49             # retrieve ISO 3166-2 information for GB-ESS
50             # as well as special regional information
51             my $name = $lct->name; # Essex
52             my $code = $lct->name; # ESS
53             my $region = $lct->region; # ENG
54              
55             =head1 SOURCES
56              
57             Wikipedia - http://en.wikipedia.org/wiki/ISO_3166
58             Statoids - http://www.statoids.com
59              
60             =head1 CONFORMING TO
61              
62             BS 6879
63             ISO 3166-1
64             ISO 3166-1 Newsletter V-1 (1998-02-05; Samoa)
65             ISO 3166-1 Newsletter V-2 (1999-10-01; Occupied Palestinian Territory)
66             ISO 3166-1 Newsletter V-3 (2002-02-01; Romania)
67             ISO 3166-1 Newsletter V-4 (2002-05-20; Name changes)
68             ISO 3166-1 Newsletter V-5 (2002-05-20; East Timor)
69             ISO 3166-1 Newsletter V-6 (2002-11-15; Timor-Leste)
70             ISO 3166-1 Newsletter V-7 (2003-01-14; Comoros)
71             ISO 3166-1 Newsletter V-8 (2003-07-23; Serbia and Montenegro)
72             ISO 3166-1 Newsletter V-9 (2004-02-13; Åland Islands)
73             ISO 3166-1 Newsletter V-10 (2004-04-26; Name changes)
74             ISO 3166-1 Newsletter V-11 (2006-03-29; Jersey, Guernsey, Isle of Man)
75             ISO 3166-1 Newsletter V-12 (2006-09-26; Serbia, Montenegro)
76             ISO 3166-2
77             ISO 3166-2 Newsletter I-1 (2000-06-12)
78             ISO 3166-2 Newsletter I-2 (2002-05-21)
79             ISO 3166-2 Newsletter I-3 (2002-08-20)
80             ISO 3166-2 Newsletter I-4 (2002-12-10)
81             ISO 3166-2 Newsletter I-5 (2003-09-05)
82             ISO 3166-2 Newsletter I-6 (2004-03-08)
83             ISO 3166-2 Newsletter I-7 (2006-09-12)
84              
85             =cut
86              
87             our $VERSION = 1.20;
88              
89 1     1   845 use Locale::Geocode::Territory;
  1         3  
  1         28  
90 1     1   6 use Locale::Geocode::Division;
  1         2  
  1         19  
91 1     1   1882 use XML::Simple;
  0            
  0            
92              
93             # XML::SAX::PurePerl barfs for some reason :/
94             $XML::Simple::PREFERRED_PARSER = 'XML::SAX::Expat';
95              
96             # Locale::Geocode extensions. the following recognized extensions
97             # are switchable flags that alter the behavior of the lookup methods.
98             # many of these extensions are part of the ISO 3166 standard as a
99             # courtesy to other international organizations (such as the UPU or
100             # ITU). others are specific to Locale::Geocode for other practical
101             # reasons (such as the usm extension for US overseas military or
102             # usps for all US postal abbreviations).
103             my @exts = qw(upu wco itu uk fx eu usm usps ust);
104             my @defs = qw(ust);
105              
106             # parse the XML data
107             my $opts = { ForceArray => [ 'division', 'ext', 'note' ], KeyAttr => [], SuppressEmpty => 1 };
108             my $data = { raw => XMLin(\*DATA, %$opts) };
109              
110             # create lookup tables
111             foreach my $href (@{ $data->{raw}->{territory} }) {
112             my $aref = $href->{division};
113              
114             $href->{divs_code} = { map { lc($_->{code}), $_ } grep { defined $_->{code} } @$aref };
115             $href->{divs_fips} = { map { lc($_->{fips}), $_ } grep { defined $_->{fips} } @$aref };
116             $href->{divs_name} = { map { lc($_->{name}), $_ } grep { defined $_->{name} } @$aref };
117             }
118              
119             my $aref = $data->{raw}->{territory};
120              
121             $data->{alpha2} = { map { lc($_->{alpha2}) => $_ } grep { defined $_->{alpha2} } @$aref };
122             $data->{alpha3} = { map { lc($_->{alpha3}) => $_ } grep { defined $_->{alpha3} } @$aref };
123             $data->{name} = { map { lc($_->{name}) => $_ } grep { defined $_->{name} } @$aref };
124              
125             =head1 METHODS
126              
127             =over 4
128              
129             =item new
130              
131             =cut
132              
133             sub new
134             {
135             my $proto = shift;
136             my $class = ref($proto) || $proto;
137              
138             my $args = { @_ };
139             my $self = {};
140              
141             bless $self, $class;
142              
143             my @exts = @defs;
144              
145             if ($args->{ext}) {
146             my $reftype = ref $args->{ext};
147              
148             die 'ext argument must be scalar or list reference'
149             if $reftype ne '' && $reftype ne 'ARRAY';
150              
151             @exts = $reftype eq 'ARRAY' ? @{ $args->{ext} } : $args->{ext};
152             }
153              
154             $self->ext(@exts);
155              
156             return $self;
157             }
158              
159             =item lookup
160              
161             =cut
162              
163             sub lookup
164             {
165             my $self = shift;
166             my $key = shift;
167              
168             my $lct = new Locale::Geocode::Territory $key, $self;
169              
170             if (!$lct && $key =~ /-/) {
171             my ($iso3166_1, $iso3166_2) = split '-', $key;
172              
173             $lct = new Locale::Geocode::Territory $iso3166_1, $self;
174              
175             return $lct->lookup($iso3166_2) if $lct;
176             }
177              
178             return $lct;
179             }
180              
181             =item territories
182              
183             =cut
184              
185             sub territories
186             {
187             my $self = shift;
188              
189             return map { $self->lookup($_) || () } keys %{ $data->{alpha2} };
190             }
191              
192             =item territories_sorted
193              
194             =cut
195              
196             sub territories_sorted
197             {
198             my $self = shift;
199              
200             return sort { $a->name cmp $b->name } $self->territories;
201             }
202              
203             =item territories_sorted_us
204              
205             =cut
206              
207             sub territories_sorted_us
208             {
209             my $self = shift;
210              
211             sub sorted_us
212             {
213             return -1 if $a->alpha2 eq 'US';
214             return 1 if $b->alpha2 eq 'US';
215             return $a->name cmp $b->name;
216             };
217              
218             return sort sorted_us $self->territories;
219             }
220              
221             =item ext
222              
223             =cut
224              
225             sub ext
226             {
227             my $self = shift;
228              
229             if (scalar @_ > 0) {
230             $self->{ext} =
231             {
232             ust => 1, # 'ust' is always on unless explicitly disabled
233             map {
234             /^-(.*)$/
235             ? ($1 => 0)
236             : $_ eq 'all'
237             ? map { $_ => 1 } @exts
238             : ($_ => 1)
239             } @_
240             };
241             }
242              
243             return grep { $self->{ext}->{$_} } keys %{ $self->{ext} };
244             }
245              
246             =item ext_enable
247              
248             =cut
249              
250             sub ext_enable
251             {
252             my $self = shift;
253              
254             foreach my $ext (@_) {
255             $self->{ext}->{$ext} = 1 if grep { $ext eq $_ } @exts;
256             }
257             }
258              
259             =item ext_disable
260              
261             =cut
262              
263             sub ext_disable
264             {
265             my $self = shift;
266              
267             delete $self->{ext}->{$_} foreach @_;
268             }
269              
270             sub chkext
271             {
272             my $self = shift;
273             my $href = shift;
274              
275             return $href->{ext} ? grep { $self->{ext}->{$_} } @{ $href->{ext} } : 1;
276             }
277              
278             sub data { return $data }
279              
280             sub import { @defs = @_[1..$#_] }
281              
282             =back
283              
284             =cut
285              
286             =head1 AUTHOR
287              
288             Mike Eldridge
289              
290             =head1 CREDITS
291              
292             Kim Ryan
293              
294             =head1 SEE ALSO
295              
296             L
297             L
298              
299             =cut
300              
301             1;
302              
303             __DATA__