File Coverage

blib/lib/Geography/States.pm
Criterion Covered Total %
statement 172 172 100.0
branch 24 28 85.7
condition 9 12 75.0
subroutine 9 9 100.0
pod 1 3 33.3
total 215 224 95.9


line stmt bran cond sub pod time code
1             package Geography::States;
2              
3 1     1   606 use 5.006;
  1         4  
4              
5 1     1   4 use strict;
  1         2  
  1         17  
6 1     1   5 use warnings;
  1         4  
  1         25  
7 1     1   5 no warnings 'syntax';
  1         1  
  1         2035  
8              
9             our $VERSION = '2015072102';
10              
11             sub init_data;
12              
13             my %states;
14              
15             sub _c_length ($) {
16 254 100   254   599 lc $_ [0] eq "australia" ? 3 : 2
17             }
18              
19             sub _norm ($$) {
20 254     254   312 my ($str, $country) = @_;
21 254 100       394 if (_c_length ($country) >= length $str) {
22 131         230 $str = uc $str;
23             }
24             else {
25 123         294 $str = join " " => map {ucfirst lc} split /\s+/ => $str;
  182         381  
26 123 100       254 $str =~ s/\bOf\b/of/ if $country eq lc 'USA';
27 123 50       203 $str =~ s/\bD([eo])\b/d$1/ if $country eq lc 'Brazil';
28             }
29              
30 254         418 $str;
31             }
32              
33             {
34             my $data = init_data;
35              
36             while (my ($country, $country_data) = each %$data) {
37             while (my ($code, $state_data) = each %$country_data) {
38             my ($strict, $name) = @$state_data;
39             my $info = [$code, $name, !$strict];
40             my $_code = _norm ($code, $country);
41             my $_name = _norm ($name, $country);
42             my $_country = lc $country;
43             $states {$_country} -> {$_code} = $info;
44             $states {$_country} -> {$_name} = $info;
45             }
46             }
47             }
48              
49              
50             sub new {
51 7 50   7 0 108 die "Not enough arguments for Geography::States -> new ()\n" unless @_ > 1;
52              
53 7         15 my $proto = shift;
54 7   33     32 my $class = ref $proto || $proto;
55              
56 7         16 my $country = lc shift;
57 7         20 $country =~ s/\s+/ /g;
58              
59 7 50       23 die "No such country $country\n" unless $states {$country};
60              
61 7         10 my $strict = shift;
62              
63 7         11 my $self;
64 7         9 my ($cs, $info);
65 7         9 while (($cs, $info) = each %{$states {$country}}) {
  395         1271  
66 388 100       927 next unless $cs eq $info -> [0];
67 195 100 66     462 next if $strict && $info -> [2];
68 184         411 my $inf = [@$info [0, 1]];
69 184         318 foreach my $i (0 .. 1) {
70             #
71             # Hardcoded exception.
72             #
73 368 100 100     2317 next if $country eq 'canada' &&
      100        
74             $$inf [0] eq 'PQ' &&
75             $i == 1;
76             $self -> {cs} -> {$info -> [$i]} = $inf unless
77 367 50       1396 exists $self -> {cs} -> {$info -> [$i]};
78             }
79             }
80 7         21 $self -> {country} = $country;
81              
82 7         31 bless $self => $class;
83             }
84              
85              
86             sub state {
87 14     14 1 212 my $self = shift;
88 14 100       32 unless (@_) {
89 2         4 my %h;
90 2         4 return grep {!$h {$_} ++} values %{$self -> {cs}};
  200         527  
  2         20  
91             }
92 12         30 my $query = _norm shift, $self -> {country};
93 12 100       41 my $answer = $self -> {cs} -> {$query} or return;
94 10 100       51 return @$answer if wantarray;
95 9 100       34 $answer -> [$answer -> [0] eq $query ? 1 : 0];
96             }
97              
98              
99             1;
100            
101             =pod
102              
103             =head1 NAME
104              
105             Geography::States - Map states and provinces to their codes, and vica versa.
106              
107             =head1 SYNOPSIS
108              
109             use Geography::States;
110              
111             my $obj = Geography::States -> new (COUNTRY [, STRICT]);
112              
113              
114             =head1 EXAMPLES
115              
116             my $canada = Geography::States -> new ('Canada');
117              
118             my $name = $canada -> state ('NF'); # Newfoundland.
119             my $code = $canada -> state ('Ontario'); # ON.
120             my ($code, $name) = $canada -> state ('BC'); # BC, British Columbia.
121             my @all_states = $canada -> state; # List code/name pairs.
122              
123              
124             =head1 DESCRIPTION
125              
126             This module lets you map states and provinces to their codes, and codes
127             to names of provinces and states.
128              
129             The C<< Geography::States -> new () >> call takes 1 or 2 arguments. The
130             first, required, argument is the country we are interested in. Current
131             supported countries are I, I, I, I,
132             and I. If a second non-false argument is given, we use I
133             mode>. In non-strict mode, we will map territories and alternative codes
134             as well, while we do not do that in strict mode. For example, if the
135             country is B, in non-strict mode, we will map B to B,
136             while in strict mode, neither B and B will be found.
137              
138             =head2 The state() method
139              
140             All queries are done by calling the C method in the object. This method
141             takes an optional argument. If an argument is given, then in scalar context,
142             it will return the name of the state if a code of a state is given, and the
143             code of a state, if the argument of the method is a name of a state. In list
144             context, both the code and the state will be returned.
145              
146             If no argument is given, then the C method in list context will return
147             a list of all code/name pairs for that country. In scalar context, it will
148             return the number of code/name pairs. Each code/name pair is a 2 element
149             anonymous array.
150              
151             Arguments can be given in a case insensitive way; if a name consists of
152             multiple parts, the number of spaces does not matter, as long as there is
153             some whitespace. (That is "NewYork" is wrong, but S<"new YORK"> is fine.)
154              
155             =head1 ODDITIES AND OPEN QUESTIONS
156              
157             I found conflicting abbreviations for the US I,
158             listed as I and I. I picked I from the USPS site.
159              
160             One site listed I as having code I. It is not listed by
161             the USPS site, and because it conflicts with I, it is not put in
162             this listing.
163              
164             The USPS also has so-called I, with non-unique codes.
165             Those are not listed here.
166              
167             Canada's I has two codes, the older I and the modern I. Both
168             I and I will map to I, but I will only map to I.
169             With strict mode, I will not be listed. Similary, Newfoundland has an
170             old code I, and a new code I (the province is now called
171             I).
172              
173             =head1 DEVELOPMENT
174            
175             The current sources of this module are found on github,
176             L<< git://github.com/Abigail/geography--states.git >>.
177              
178             =head1 AUTHOR
179              
180             Abigail L<< mailto:geography-states@abigail.be >>.
181              
182             =head1 COPYRIGHT and LICENSE
183              
184             Copyright (C) 1999 - 2001, 2009 by Abigail.
185              
186             Permission is hereby granted, free of charge, to any person obtaining a
187             copy of this software and associated documentation files (the "Software"),
188             to deal in the Software without restriction, including without limitation
189             the rights to use, copy, modify, merge, publish, distribute, sublicense,
190             and/or sell copies of the Software, and to permit persons to whom the
191             Software is furnished to do so, subject to the following conditions:
192              
193             The above copyright notice and this permission notice shall be included
194             in all copies or substantial portions of the Software.
195            
196             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
197             IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
198             FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
199             THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
200             WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
201             OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
202             THE SOFTWARE.
203              
204             =cut
205              
206              
207             sub init_data {
208 1     1 0 1 my $data;
209              
210 1         3 $$data {'USA'} {AK} = [1 => "Alaska"];
211 1         2 $$data {'USA'} {AL} = [1 => "Alabama"];
212 1         3 $$data {'USA'} {AR} = [1 => "Arkansas"];
213 1         2 $$data {'USA'} {AS} = [0 => "American Samoa"];
214 1         2 $$data {'USA'} {AZ} = [1 => "Arizona"];
215 1         2 $$data {'USA'} {CA} = [1 => "California"];
216 1         2 $$data {'USA'} {CO} = [1 => "Colorado"];
217 1         3 $$data {'USA'} {CT} = [1 => "Connecticut"];
218 1         2 $$data {'USA'} {DC} = [0 => "District of Columbia"];
219 1         2 $$data {'USA'} {DE} = [1 => "Delaware"];
220 1         2 $$data {'USA'} {FL} = [1 => "Florida"];
221 1         3 $$data {'USA'} {FM} = [0 => "Federated States of Micronesia"];
222 1         2 $$data {'USA'} {GA} = [1 => "Georgia"];
223 1         2 $$data {'USA'} {GU} = [0 => "Guam"];
224 1         2 $$data {'USA'} {HI} = [1 => "Hawaii"];
225 1         3 $$data {'USA'} {IA} = [1 => "Iowa"];
226 1         2 $$data {'USA'} {ID} = [1 => "Idaho"];
227 1         2 $$data {'USA'} {IL} = [1 => "Illinois"];
228 1         2 $$data {'USA'} {IN} = [1 => "Indiana"];
229 1         2 $$data {'USA'} {KS} = [1 => "Kansas"];
230 1         2 $$data {'USA'} {KY} = [1 => "Kentucky"];
231 1         2 $$data {'USA'} {LA} = [1 => "Louisiana"];
232 1         7 $$data {'USA'} {MA} = [1 => "Massachusetts"];
233 1         3 $$data {'USA'} {MD} = [1 => "Maryland"];
234 1         2 $$data {'USA'} {ME} = [1 => "Maine"];
235 1         3 $$data {'USA'} {MH} = [0 => "Marshall Islands"];
236 1         2 $$data {'USA'} {MI} = [1 => "Michigan"];
237 1         3 $$data {'USA'} {MN} = [1 => "Minnesota"];
238 1         2 $$data {'USA'} {MO} = [1 => "Missouri"];
239 1         2 $$data {'USA'} {MP} = [0 => "Northern Mariana Islands"];
240 1         13 $$data {'USA'} {MS} = [1 => "Mississippi"];
241 1         4 $$data {'USA'} {MT} = [1 => "Montana"];
242 1         2 $$data {'USA'} {NC} = [1 => "North Carolina"];
243 1         2 $$data {'USA'} {ND} = [1 => "North Dakota"];
244 1         2 $$data {'USA'} {NE} = [1 => "Nebraska"];
245 1         2 $$data {'USA'} {NH} = [1 => "New Hampshire"];
246 1         3 $$data {'USA'} {NJ} = [1 => "New Jersey"];
247 1         2 $$data {'USA'} {NM} = [1 => "New Mexico"];
248 1         2 $$data {'USA'} {NV} = [1 => "Nevada"];
249 1         2 $$data {'USA'} {NY} = [1 => "New York"];
250 1         2 $$data {'USA'} {OH} = [1 => "Ohio"];
251 1         2 $$data {'USA'} {OK} = [1 => "Oklahoma"];
252 1         3 $$data {'USA'} {OR} = [1 => "Oregon"];
253 1         2 $$data {'USA'} {PA} = [1 => "Pennsylvania"];
254 1         3 $$data {'USA'} {PR} = [0 => "Puerto Rico"];
255 1         1 $$data {'USA'} {PW} = [0 => "Palau"];
256 1         2 $$data {'USA'} {RI} = [1 => "Rhode Island"];
257 1         2 $$data {'USA'} {SC} = [1 => "South Carolina"];
258 1         2 $$data {'USA'} {SD} = [1 => "South Dakota"];
259 1         3 $$data {'USA'} {TN} = [1 => "Tennessee"];
260 1         2 $$data {'USA'} {TX} = [1 => "Texas"];
261 1         2 $$data {'USA'} {UT} = [1 => "Utah"];
262 1         2 $$data {'USA'} {VA} = [1 => "Virginia"];
263 1         7 $$data {'USA'} {VI} = [0 => "Virgin Islands"];
264 1         2 $$data {'USA'} {VT} = [1 => "Vermont"];
265 1         17 $$data {'USA'} {WA} = [1 => "Washington"];
266 1         2 $$data {'USA'} {WI} = [1 => "Wisconsin"];
267 1         2 $$data {'USA'} {WV} = [1 => "West Virginia"];
268 1         2 $$data {'USA'} {WY} = [1 => "Wyoming"];
269              
270 1         2 $$data {'Brazil'} {AC} = [1 => "Acre"];
271 1         2 $$data {'Brazil'} {AL} = [1 => "Alagoas"];
272 1         2 $$data {'Brazil'} {AM} = [1 => "Amazonas"];
273 1         2 $$data {'Brazil'} {AP} = [1 => "Amap\x{e1}"];
274 1         2 $$data {'Brazil'} {BA} = [1 => "Bahia"];
275 1         2 $$data {'Brazil'} {CE} = [1 => "Cear\x{e1}"];
276 1         2 $$data {'Brazil'} {DF} = [1 => "Distrito Federal"];
277 1         3 $$data {'Brazil'} {ES} = [1 => "Espir\x{ed}to Santo"];
278 1         1 $$data {'Brazil'} {FN} = [1 => "Fernando de Noronha"];
279 1         2 $$data {'Brazil'} {GO} = [1 => "Goi\x{e1}s"];
280 1         2 $$data {'Brazil'} {MA} = [1 => "Maranh\x{e3}o"];
281 1         2 $$data {'Brazil'} {MG} = [1 => "Minas Gerais"];
282 1         2 $$data {'Brazil'} {MS} = [1 => "Mato Grosso do Sul"];
283 1         2 $$data {'Brazil'} {MT} = [1 => "Mato Grosso"];
284 1         2 $$data {'Brazil'} {PA} = [1 => "Par\x{e1}"];
285 1         3 $$data {'Brazil'} {PB} = [1 => "Para\x{ed}ba"];
286 1         2 $$data {'Brazil'} {PE} = [1 => "Pernambuco"];
287 1         3 $$data {'Brazil'} {PI} = [1 => "Piau\x{ed}"];
288 1         2 $$data {'Brazil'} {PR} = [1 => "Paran\x{e1}"];
289 1         2 $$data {'Brazil'} {RJ} = [1 => "Rio de Janeiro"];
290 1         1 $$data {'Brazil'} {RN} = [1 => "Rio Grande do Norte"];
291 1         2 $$data {'Brazil'} {RO} = [1 => "Rond\x{f4}nia"];
292 1         8 $$data {'Brazil'} {RR} = [1 => "Roraima"];
293 1         7 $$data {'Brazil'} {RS} = [1 => "Rio Grande do Sul"];
294 1         2 $$data {'Brazil'} {SC} = [1 => "Santa Catarina"];
295 1         3 $$data {'Brazil'} {SE} = [1 => "Sergipe"];
296 1         2 $$data {'Brazil'} {SP} = [1 => "S\x{e3}o Paulo"];
297 1         13 $$data {'Brazil'} {TO} = [1 => "Tocantins"];
298              
299 1         3 $$data {'Canada'} {AB} = [1 => "Alberta"];
300 1         3 $$data {'Canada'} {BC} = [1 => "British Columbia"];
301 1         1 $$data {'Canada'} {MB} = [1 => "Manitoba"];
302 1         2 $$data {'Canada'} {NB} = [1 => "New Brunswick"];
303 1         2 $$data {'Canada'} {NF} = [0 => "Newfoundland"];
304 1         2 $$data {'Canada'} {NL} = [1 => "Newfoundland and Labrador"];
305 1         2 $$data {'Canada'} {NS} = [1 => "Nova Scotia"];
306 1         2 $$data {'Canada'} {NT} = [1 => "Northwest Territories"];
307 1         3 $$data {'Canada'} {NU} = [1 => "Nunavut"];
308 1         2 $$data {'Canada'} {ON} = [1 => "Ontario"];
309 1         2 $$data {'Canada'} {PE} = [1 => "Prince Edward Island"];
310 1         2 $$data {'Canada'} {PQ} = [0 => "Quebec"];
311 1         3 $$data {'Canada'} {QC} = [1 => "Quebec"];
312 1         1 $$data {'Canada'} {SK} = [1 => "Saskatchewan"];
313 1         3 $$data {'Canada'} {YT} = [1 => "Yukon Territory"];
314              
315 1         2 $$data {'The Netherlands'} {DR} = [1 => "Drente"];
316 1         1 $$data {'The Netherlands'} {FL} = [1 => "Flevoland"];
317 1         2 $$data {'The Netherlands'} {FR} = [1 => "Friesland"];
318 1         2 $$data {'The Netherlands'} {GL} = [1 => "Gelderland"];
319 1         3 $$data {'The Netherlands'} {GR} = [1 => "Groningen"];
320 1         2 $$data {'The Netherlands'} {LB} = [1 => "Limburg"];
321 1         2 $$data {'The Netherlands'} {NB} = [1 => "Noord Brabant"];
322 1         7 $$data {'The Netherlands'} {NH} = [1 => "Noord Holland"];
323 1         2 $$data {'The Netherlands'} {OV} = [1 => "Overijssel"];
324 1         7 $$data {'The Netherlands'} {UT} = [1 => "Utrecht"];
325 1         2 $$data {'The Netherlands'} {ZH} = [1 => "Zuid Holland"];
326 1         2 $$data {'The Netherlands'} {ZL} = [1 => "Zeeland"];
327              
328 1         2 $$data {'Australia'} {ACT} = [1 => "Australian Capital Territory"];
329 1         1 $$data {'Australia'} {NSW} = [1 => "New South Wales"];
330 1         2 $$data {'Australia'} {QLD} = [1 => "Queensland"];
331 1         3 $$data {'Australia'} {SA} = [1 => "South Australia"];
332 1         2 $$data {'Australia'} {TAS} = [1 => "Tasmania"];
333 1         6 $$data {'Australia'} {VIC} = [1 => "Victoria"];
334 1         2 $$data {'Australia'} {WA} = [1 => "Western Australia"];
335              
336 1         2 $data;
337             }