File Coverage

blib/lib/Geo/LookupPostcode.pm
Criterion Covered Total %
statement 82 89 92.1
branch 47 52 90.3
condition 111 122 90.9
subroutine 30 30 100.0
pod 1 1 100.0
total 271 294 92.1


line stmt bran cond sub pod time code
1             package Geo::LookupPostcode;
2              
3 2     2   28518 use 5.006;
  2         5  
4 2     2   7 use strict;
  2         2  
  2         43  
5 2     2   7 use warnings FATAL => 'all';
  2         5  
  2         61  
6 2     2   482 use utf8;
  2         9  
  2         8  
7              
8 2     2   44 use Carp qw(croak);
  2         2  
  2         3906  
9             require Exporter;
10              
11             our @ISA = qw(Exporter);
12             our @EXPORT_OK = qw(lookup_postcode);
13              
14             # These are initialised further down
15             my %lookup_table;
16             my %lookup_functions;
17              
18             =encoding utf-8
19              
20             =head1 NAME
21              
22             Geo::LookupPostcode - Get a province name for a given postcode
23              
24             =head1 VERSION
25              
26             Version 0.01_01
27              
28             This is a developer release, as the API interface may change.
29              
30             =cut
31              
32             our $VERSION = '0.01_01';
33              
34              
35             =head1 SYNOPSIS
36              
37             Given a country name and a postcode, get the name of the province.
38              
39             use Geo::LookupPostcode qw(lookup_postcode);
40              
41             my $province = lookup_postcode("it", "00118");
42             # $province is now:
43             # {
44             # region_code => 'IT-62',
45             # province_code => 'IT-RM',
46             # }
47              
48             =head1 Supported countries
49              
50             =over 12
51              
52             =item Italy
53              
54             This includes the Vatican and the Republic of San Marino.
55              
56             =back
57              
58             =head1 SUBROUTINES/METHODS
59              
60             =head2 lookup_postcode
61              
62             Takes two character string arguments, the first one being a two-letter ISO
63             3166-1 country code, and the second one being a postcode in that country.
64              
65             If successful, it returns a reference to a hash, with these key/value pairs:
66              
67             {
68             region_code => $region_code,
69             province_code => $province_code,
70             }
71            
72             If it cannot find the province, it returns undef if not.
73              
74             Note that the names may be anglicised (eg: "Vatican City", not "Città del Vaticano").
75              
76             This subroutine will die if the wrong number of arguments is passed or if an
77             unsupported country is passed.
78              
79             my $rh_province = lookup_postcode("it", "00118");
80              
81             =cut
82              
83             sub lookup_postcode {
84 100002 50   100002 1 9352117 croak "Expected two arguments" if (@_ != 2);
85 100002         87752 my ($country, $postcode) = @_;
86              
87             # Ensure that the UTF-8 flag is turned on, so that `lc` treats these
88             # strings as character strings.
89 100002         101750 utf8::upgrade $country;
90 100002         82586 utf8::upgrade $postcode;
91              
92 100002         84864 $country = lc($country);
93 100002         72111 $postcode = lc($postcode);
94              
95 100002 50       155375 croak "Unsupported country '$country'" if ! $lookup_table{$country};
96              
97             # Do some cleanup of postcode:
98 100002         88924 utf8::upgrade $postcode;
99 100002         435521 $postcode =~ s/\s*//g;
100              
101 100002         139696 my $lookup_key = $lookup_functions{$country}($postcode);
102 100002 100       159173 if ($lookup_table{$country}{$lookup_key}) {
103 20301         14476 my @results;
104 20301         13122 for my $rh_geo_entry (@{ $lookup_table{$country}{$lookup_key} }) {
  20301         42285  
105 22003         25984 local $_ = $postcode;
106 22003 100 100     48415 if (! $rh_geo_entry->{function} || $rh_geo_entry->{function}()) {
107 19269         30730 push @results, $rh_geo_entry;
108             }
109             }
110 20301 50       47140 if (@results > 1) {
    100          
111 0         0 my $provinces = join(", ", map { $_->{province} } @results);
  0         0  
112 0         0 die "Found more than one province for postcode '$postcode': $provinces ";
113             }
114             elsif (@results == 1) {
115             return {
116             region_code => $results[0]{region_code},
117             province_code => $results[0]{province_code},
118 19269         65639 };
119             }
120             }
121 80733         92499 return;
122             }
123              
124             sub _build_data() {
125             %lookup_functions = (
126 100002     100002   150169 it => sub { substr($_[0], 0, 3) },
127 2     2   11 );
128              
129             # Italian data from https://en.wikipedia.org/wiki/List_of_postal_codes_in_Italy
130 2         5 my %geo_provinces = ();
131             $geo_provinces{it} = [
132             {
133             'region' => 'Sicily',
134             'lookup_keys' => [ '920', '921' ],
135             'province' => 'Agrigento',
136             'province_code' => 'IT-AG'
137             },
138             {
139             'region' => 'Piedmont',
140             'lookup_keys' => [ '150', '151' ],
141             'province' => 'Alessandria',
142             'province_code' => 'IT-AL'
143             },
144             {
145             'region' => 'Marche',
146             'lookup_keys' => [ '600', '601' ],
147             'province' => 'Ancona',
148             'province_code' => 'IT-AN'
149             },
150             {
151             'region' => 'Aosta Valley',
152             'lookup_keys' => [ '110', '111' ],
153             'province' => 'Aosta',
154             'province_code' => 'IT-AO'
155             },
156             {
157             'region' => 'Marche',
158             'lookup_keys' => [ '630', '631' ],
159             'province' => 'Ascoli Piceno',
160             'province_code' => 'IT-AP'
161             },
162             {
163             'region' => 'Abruzzo',
164             'lookup_keys' => [ '670', '671' ],
165             'province' => 'L\'Aquila',
166             'province_code' => 'IT-AQ'
167             },
168             {
169             'region' => 'Tuscany',
170             'lookup_keys' => [ '520', '521' ],
171             'province' => 'Arezzo',
172             'province_code' => 'IT-AR'
173             },
174             {
175             'region' => 'Piedmont',
176             'lookup_keys' => [ '140', '141' ],
177             'province' => 'Asti',
178             'province_code' => 'IT-AT'
179             },
180             {
181             'region' => 'Campania',
182             'lookup_keys' => [ '830', '831' ],
183             'province' => 'Avellino',
184             'province_code' => 'IT-AV'
185             },
186             {
187             'region' => 'Apulia',
188             'lookup_keys' => [ '700', '701' ],
189             'province' => 'Bari',
190             'province_code' => 'IT-BA'
191             },
192             {
193             'region' => 'Lombardy',
194             'lookup_keys' => [ '240', '241' ],
195             'province' => 'Bergamo',
196             'province_code' => 'IT-BG'
197             },
198             {
199             'region' => 'Piedmont',
200             'lookup_keys' => [ '138', '139' ],
201             'province' => 'Biella',
202             'province_code' => 'IT-BI'
203             },
204             {
205             'region' => 'Veneto',
206             'lookup_keys' => [ '320', '321' ],
207             'province' => 'Belluno',
208             'province_code' => 'IT-BL'
209             },
210             {
211             'region' => 'Campania',
212             'comment' => 'Province could also be Beneventum',
213             'lookup_keys' => [ '820', '821' ],
214             'province' => 'Benevento',
215             'province_code' => 'IT-BN'
216             },
217             {
218             'region' => 'Emilia-Romagna',
219             'lookup_keys' => [ '400', '401' ],
220             'province' => 'Bologna',
221             'province_code' => 'IT-BO'
222             },
223             {
224             'region' => 'Apulia',
225             'lookup_keys' => [ '720', '721' ],
226             'province' => 'Brindisi',
227             'province_code' => 'IT-BR'
228             },
229             {
230             'region' => 'Lombardy',
231             'lookup_keys' => [ '250', '251' ],
232             'province' => 'Brescia',
233             'province_code' => 'IT-BS'
234             },
235             {
236             'region' => 'Apulia',
237             'lookup_keys' => [ '760', '761' ],
238             'province' => 'Barletta-Andria-Trani',
239             'province_code' => 'IT-BT'
240             },
241             {
242             'region' => 'Trentino-Alto Adige/Südtirol',
243             'lookup_keys' => [ '390', '391' ],
244             'province' => 'Bolzano/Bozen',
245             'province_code' => 'IT-BZ'
246             },
247             {
248             'region' => 'Sardinia',
249 300 100 100 300   4370 'function' => sub { ($_ >= 9121 && $_ <= 9134) || ($_ >= 9018 && $_ <= 9019) || ($_ >= 9042 && $_ <= 9049) || $_ == 8030 || $_ == 8033 || $_ == 8035 || $_ == 8043 },
      100        
      66        
      100        
      66        
      100        
      100        
      100        
250             'lookup_keys' => [ '080', '090', '091' ],
251             'province' => 'Cagliari',
252             'province_code' => 'IT-CA',
253             'comment' => 'Remove 09010 to 09017 because it conflicts with Carbonia-Iglesias, and 09020 to 09041 because it conflicts with Medio Campidano',
254             },
255             {
256             'region' => 'Molise',
257 200 100 100 200   1109 'function' => sub { $_ == 86100 || ($_ >= 86010 && $_ <= 86049) },
258             'lookup_keys' => [ '860', '861' ],
259             'province' => 'Campobasso',
260             'province_code' => 'IT-CB'
261             },
262             {
263             'region' => 'Campania',
264             'lookup_keys' => [ '810', '811' ],
265             'province' => 'Caserta',
266             'province_code' => 'IT-CE'
267             },
268             {
269             'region' => 'Abruzzo',
270             'lookup_keys' => [ '660', '661' ],
271             'province' => 'Chieti',
272             'province_code' => 'IT-CH'
273             },
274             {
275             'region' => 'Sardinia',
276 100 100   100   470 'function' => sub { $_ >= 9010 && $_ <= 9017 },
277             'lookup_keys' => [ '090' ],
278             'province' => 'Carbonia-Iglesias',
279             'province_code' => 'IT-CI'
280             },
281             {
282             'region' => 'Sicily',
283             'lookup_keys' => [ '930', '931' ],
284             'province' => 'Caltanissetta',
285             'province_code' => 'IT-CL'
286             },
287             {
288             'region' => 'Piedmont',
289             'lookup_keys' => [ '120', '121' ],
290             'province' => 'Cuneo',
291             'province_code' => 'IT-CN'
292             },
293             {
294             'region' => 'Lombardy',
295             'lookup_keys' => [ '220', '221' ],
296             'province' => 'Como',
297             'province_code' => 'IT-CO'
298             },
299             {
300             'region' => 'Lombardy',
301             'lookup_keys' => [ '260', '261' ],
302             'province' => 'Cremona',
303             'province_code' => 'IT-CR'
304             },
305             {
306             'region' => 'Calabria',
307             'lookup_keys' => [ '870', '871' ],
308             'province' => 'Cosenza',
309             'province_code' => 'IT-CS'
310             },
311             {
312             'region' => 'Sicily',
313             'lookup_keys' => [ '950', '951' ],
314             'province' => 'Catania',
315             'province_code' => 'IT-CT'
316             },
317             {
318             'region' => 'Calabria',
319             'lookup_keys' => [ '880', '881' ],
320             'province' => 'Catanzaro',
321             'province_code' => 'IT-CZ'
322             },
323             {
324             'region' => 'Sicily',
325             'lookup_keys' => [ '940', '941' ],
326             'province' => 'Enna',
327             'province_code' => 'IT-EN'
328             },
329             {
330             'region' => 'Emilia-Romagna',
331             'lookup_keys' => [ '470', '471' ],
332             'province' => 'Forl�-Cesena',
333             'province_code' => 'IT-FC'
334             },
335             {
336             'region' => 'Emilia-Romagna',
337             'lookup_keys' => [ '440', '441' ],
338             'province' => 'Ferrara',
339             'province_code' => 'IT-FE'
340             },
341             {
342             'region' => 'Apulia',
343             'lookup_keys' => [ '710', '711' ],
344             'province' => 'Foggia',
345             'province_code' => 'IT-FG'
346             },
347             {
348             'region' => 'Tuscany',
349             'lookup_keys' => [ '500', '501' ],
350             'province' => 'Florence',
351             'province_code' => 'IT-FI'
352             },
353             {
354             'region' => 'Marche',
355             'lookup_keys' => [ '638', '639' ],
356             'province' => 'Fermo',
357             'province_code' => 'IT-FM'
358             },
359             {
360             'region' => 'Lazio',
361             'lookup_keys' => [ '030', '031' ],
362             'province' => 'Frosinone',
363             'province_code' => 'IT-FR'
364             },
365             {
366             'region' => 'Liguria',
367             'lookup_keys' => [ '160', '161' ],
368             'province' => 'Genoa',
369             'province_code' => 'IT-GE'
370             },
371             {
372             'region' => 'Friuli-Venezia Giulia',
373 200 100 33 200   831 'function' => sub { $_ == 34170 && ($_ >= 34070 && $_ <= 34079) },
374             'lookup_keys' => [ '340', '341' ],
375             'province' => 'Gorizia',
376             'province_code' => 'IT-GO'
377             },
378             {
379             'region' => 'Tuscany',
380             'lookup_keys' => [ '580', '581' ],
381             'province' => 'Grosseto',
382             'province_code' => 'IT-GR'
383             },
384             {
385             'region' => 'Liguria',
386             'lookup_keys' => [ '180', '181' ],
387             'province' => 'Imperia',
388             'province_code' => 'IT-IM'
389             },
390             {
391             'region' => 'Molise',
392 200 100 100 200   949 'function' => sub { $_ == 86170 || ($_ >= 86070 && $_ <= 86097) },
393             'lookup_keys' => [ '860', '861' ],
394             'province' => 'Isernia',
395             'province_code' => 'IT-IS'
396             },
397             {
398             'region' => 'Calabria',
399             'lookup_keys' => [ '888', '889' ],
400             'province' => 'Crotone',
401             'province_code' => 'IT-KR'
402             },
403             {
404             'region' => 'Lombardy',
405             'lookup_keys' => [ '238', '239' ],
406             'province' => 'Lecco',
407             'province_code' => 'IT-LC'
408             },
409             {
410             'region' => 'Apulia',
411             'lookup_keys' => [ '730', '731' ],
412             'province' => 'Lecce',
413             'province_code' => 'IT-LE'
414             },
415             {
416             'region' => 'Tuscany',
417             'comment' => 'Province could also be Leghorn',
418             'lookup_keys' => [ '570', '571' ],
419             'province' => 'Livorno',
420             'province_code' => 'IT-LI'
421             },
422             {
423             'region' => 'Lombardy',
424             'lookup_keys' => [ '268', '269' ],
425             'province' => 'Lodi',
426             'province_code' => 'IT-LO'
427             },
428             {
429             'region' => 'Lazio',
430             'lookup_keys' => [ '040', '041' ],
431             'province' => 'Latina',
432             'province_code' => 'IT-LT'
433             },
434             {
435             'region' => 'Tuscany',
436             'lookup_keys' => [ '550', '551' ],
437             'province' => 'Lucca',
438             'province_code' => 'IT-LU'
439             },
440             {
441             'region' => 'Lombardy',
442             'lookup_keys' => [ '208', '209' ],
443             'province' => 'Monza & Brianza',
444             'province_code' => 'IT-MB'
445             },
446             {
447             'region' => 'Marche',
448             'lookup_keys' => [ '620', '621' ],
449             'province' => 'Macerata',
450             'province_code' => 'IT-MC'
451             },
452             {
453             'region' => 'Sardinia',
454 100 100   100   404 'function' => sub { $_ >= 9020 && $_ <= 9041 },
455             'comment' => 'Provisional code MD modified in VS from December 2006.',
456             'lookup_keys' => [ '090' ],
457             'province' => 'Medio Campidano',
458             'province_code' => 'IT-MD',
459             },
460             {
461             'region' => 'Sicily',
462             'lookup_keys' => [ '980', '981' ],
463             'province' => 'Messina',
464             'province_code' => 'IT-ME'
465             },
466             {
467             'region' => 'Lombardy',
468             'lookup_keys' => [ '200', '201' ],
469             'province' => 'Milan',
470             'province_code' => 'IT-MI'
471             },
472             {
473             'region' => 'Lombardy',
474             'lookup_keys' => [ '460', '461' ],
475             'province' => 'Mantua',
476             'province_code' => 'IT-MN'
477             },
478             {
479             'region' => 'Emilia-Romagna',
480             'lookup_keys' => [ '410', '411' ],
481             'province' => 'Modena',
482             'province_code' => 'IT-MO'
483             },
484             {
485             'region' => 'Tuscany',
486             'lookup_keys' => [ '541', '750' ],
487             'province' => 'Massa-Carrara',
488             'province_code' => 'IT-MS'
489             },
490             {
491             'region' => 'Basilicata',
492             'lookup_keys' => [ '751', '80750' ],
493             'province' => 'Matera',
494             'province_code' => 'IT-MT'
495             },
496             {
497             'region' => 'Campania',
498             'lookup_keys' => [ '800', '801' ],
499             'province' => 'Naples',
500             'province_code' => 'IT-NA'
501             },
502             {
503             'region' => 'Piedmont',
504             'lookup_keys' => [ '280', '281' ],
505             'province' => 'Novara',
506             'province_code' => 'IT-NO'
507             },
508             {
509             'region' => 'Sardinia',
510 200 100 100 200   3433 'function' => sub { $_ == 8100 || $_ == 8012 || ($_ >= 8014 && $_ <= 8018) || ($_ >= 8021 && $_ <= 8029) || ($_ >= 8031 && $_ <= 8032) || ($_ >= 8036 && $_ <= 8039) },
      100        
      100        
      66        
      100        
      66        
      100        
      66        
511             'lookup_keys' => [ '080', '081' ],
512             'province' => 'Nuoro',
513             'province_code' => 'IT-NU',
514             'comment' => 'Removed 08010, 08013, 08019, 08020 and 08034 as these conflict with Oristano, and 08030, 08033 and 08035 as these conflict with Cagliari',
515             },
516             {
517             'region' => 'Sardinia',
518 100 100 100 100   595 'function' => sub { $_ >= 8040 && $_ <= 8049 && $_ != 8043 },
519             'lookup_keys' => [ '080' ],
520             'province' => 'Ogliastra',
521             'province_code' => 'IT-OG',
522             'comment' => 'Removed 08043 because it conflicts with Cagliari',
523             },
524             {
525             'region' => 'Sardinia',
526 300 100 100 300   3162 'function' => sub { $_ == 9170 || ($_ >= 9070 && $_ <= 9099) || $_ == 8010 || $_ == 8013 || $_ == 8019 || $_ == 8034 },
      66        
      100        
      100        
      100        
527             'lookup_keys' => [ '080', '090', '091' ],
528             'province' => 'Oristano',
529             'province_code' => 'IT-OR',
530             'comment' => 'Remove 08030 because this conflicts with Cagliari',
531             },
532             {
533             'region' => 'Sardinia',
534 200 100 100 200   1494 'function' => sub { ($_ >= 7020 && $_ <= 7029) || $_ == 8020 },
535             'lookup_keys' => [ '070', '080' ],
536             'province' => 'Olbia-Tempio',
537             'province_code' => 'IT-OT'
538             },
539             {
540             'region' => 'Sicily',
541             'lookup_keys' => [ '900', '901' ],
542             'province' => 'Palermo',
543             'province_code' => 'IT-PA'
544             },
545             {
546             'region' => 'Emilia-Romagna',
547             'lookup_keys' => [ '290', '291' ],
548             'province' => 'Piacenza',
549             'province_code' => 'IT-PC'
550             },
551             {
552             'region' => 'Veneto',
553             'lookup_keys' => [ '350', '351' ],
554             'province' => 'Padua',
555             'province_code' => 'IT-PD'
556             },
557             {
558             'region' => 'Abruzzo',
559             'lookup_keys' => [ '650', '651' ],
560             'province' => 'Pescara',
561             'province_code' => 'IT-PE'
562             },
563             {
564             'region' => 'Umbria',
565             'lookup_keys' => [ '060', '061' ],
566             'province' => 'Perugia',
567             'province_code' => 'IT-PG'
568             },
569             {
570             'region' => 'Tuscany',
571             'lookup_keys' => [ '560', '561' ],
572             'province' => 'Pisa',
573             'province_code' => 'IT-PI'
574             },
575             {
576             'region' => 'Friuli-Venezia Giulia',
577 200 100 100 200   1025 'function' => sub { $_ == 33170 || ($_ >= 33070 && $_ <= 33099) },
578             'lookup_keys' => [ '330', '331' ],
579             'province' => 'Pordenone',
580             'province_code' => 'IT-PN'
581             },
582             {
583             'region' => 'Tuscany',
584             'lookup_keys' => [ '590', '591' ],
585             'province' => 'Prato',
586             'province_code' => 'IT-PO'
587             },
588             {
589             'region' => 'Emilia-Romagna',
590             'lookup_keys' => [ '430', '431' ],
591             'province' => 'Parma',
592             'province_code' => 'IT-PR'
593             },
594             {
595             'region' => 'Tuscany',
596             'lookup_keys' => [ '510', '511' ],
597             'province' => 'Pistoia',
598             'province_code' => 'IT-PT'
599             },
600             {
601             'region' => 'Marche',
602             'lookup_keys' => [ '610', '611' ],
603             'province' => 'Pesaro e Urbino',
604             'province_code' => 'IT-PU'
605             },
606             {
607             'region' => 'Lombardy',
608             'lookup_keys' => [ '270', '271' ],
609             'province' => 'Pavia',
610             'province_code' => 'IT-PV'
611             },
612             {
613             'region' => 'Basilicata',
614             'lookup_keys' => [ '850', '851' ],
615             'province' => 'Potenza',
616             'province_code' => 'IT-PZ'
617             },
618             {
619             'region' => 'Emilia-Romagna',
620             'lookup_keys' => [ '480', '481' ],
621             'province' => 'Ravenna',
622             'province_code' => 'IT-RA'
623             },
624             {
625             'region' => 'Calabria',
626             'lookup_keys' => [ '890', '891' ],
627             'province' => 'Reggio Calabria',
628             'province_code' => 'IT-RC'
629             },
630             {
631             'region' => 'Emilia-Romagna',
632             'lookup_keys' => [ '420', '421' ],
633             'province' => 'Reggio Emilia',
634             'province_code' => 'IT-RE'
635             },
636             {
637             'region' => 'Sicily',
638             'lookup_keys' => [ '970', '971' ],
639             'province' => 'Ragusa',
640             'province_code' => 'IT-RG'
641             },
642             {
643             'region' => 'Lazio',
644             'lookup_keys' => [ '020', '021' ],
645             'province' => 'Rieti',
646             'province_code' => 'IT-RI'
647             },
648             {
649             'region' => 'Lazio',
650 201 100 100 201   1460 'function' => sub { ($_ >= 118 && $_ <= 119) || ($_ >= 121 && $_ <= 199) || ($_ >= 10 && $_ <= 69) },
      100        
      66        
      66        
651             'lookup_keys' => [ '000', '001' ],
652             'province' => 'Rome',
653             'province_code' => 'IT-RM'
654             },
655             {
656             'region' => 'Emilia-Romagna',
657 200 100 100 200   1497 'function' => sub { ($_ >= 47921 && $_ <= 47924) || ($_ >= 47814 && $_ <= 47866) },
      100        
658             'lookup_keys' => [ '478', '479' ],
659             'province' => 'Rimini',
660             'province_code' => 'IT-RN'
661             },
662             {
663             'region' => 'Veneto',
664             'lookup_keys' => [ '450', '451' ],
665             'province' => 'Rovigo',
666             'province_code' => 'IT-RO'
667             },
668             {
669             'region' => '-',
670 100 100   100   373 'function' => sub { $_ >= 47890 && $_ <= 47899 },
671             'lookup_keys' => [ '478' ],
672             'province' => 'Republic of San Marino',
673             'province_code' => 'IT-RSM'
674             },
675             {
676             'region' => 'Campania',
677             'lookup_keys' => [ '840', '841' ],
678             'province' => 'Salerno',
679             'province_code' => 'IT-SA'
680             },
681             {
682             'region' => '-',
683 102     102   281 'function' => sub { $_ == 120 },
684             'lookup_keys' => [ '001' ],
685             'province' => 'Vatican City',
686             'province_code' => 'IT-SCV'
687             },
688             {
689             'region' => 'Tuscany',
690             'lookup_keys' => [ '530', '531' ],
691             'province' => 'Siena',
692             'province_code' => 'IT-SI'
693             },
694             {
695             'region' => 'Lombardy',
696             'lookup_keys' => [ '230', '231' ],
697             'province' => 'Sondrio',
698             'province_code' => 'IT-SO'
699             },
700             {
701             'region' => 'Liguria',
702             'lookup_keys' => [ '190', '191' ],
703             'province' => 'La Spezia',
704             'province_code' => 'IT-SP'
705             },
706             {
707             'region' => 'Sicily',
708             'lookup_keys' => [ '960', '961' ],
709             'province' => 'Syracuse',
710             'province_code' => 'IT-SR'
711             },
712             {
713             'region' => 'Sardinia',
714 200 100 100 200   1428 'function' => sub { $_ == 7100 || ($_ >= 7010 && $_ <= 7019) || ($_ >= 7030 && $_ <= 7049) },
      100        
      66        
715             'lookup_keys' => [ '070', '071' ],
716             'province' => 'Sassari',
717             'province_code' => 'IT-SS'
718             },
719             {
720             'region' => 'Liguria',
721             'lookup_keys' => [ '170', '171' ],
722             'province' => 'Savona',
723             'province_code' => 'IT-SV'
724             },
725             {
726             'region' => 'Apulia',
727             'lookup_keys' => [ '740', '741' ],
728             'province' => 'Taranto',
729             'province_code' => 'IT-TA'
730             },
731             {
732             'region' => 'Abruzzo',
733             'lookup_keys' => [ '640', '641' ],
734             'province' => 'Teramo',
735             'province_code' => 'IT-TE'
736             },
737             {
738             'region' => 'Trentino-Alto Adige/Südtirol',
739             'lookup_keys' => [ '380', '381' ],
740             'province' => 'Trento',
741             'province_code' => 'IT-TN'
742             },
743             {
744             'region' => 'Piedmont',
745             'lookup_keys' => [ '100', '101' ],
746             'province' => 'Turin',
747             'province_code' => 'IT-TO'
748             },
749             {
750             'region' => 'Sicily',
751             'lookup_keys' => [ '910', '911' ],
752             'province' => 'Trapani',
753             'province_code' => 'IT-TP'
754             },
755             {
756             'region' => 'Umbria',
757             'lookup_keys' => [ '050', '051' ],
758             'province' => 'Terni',
759             'province_code' => 'IT-TR'
760             },
761             {
762             'region' => 'Friuli-Venezia Giulia',
763 200 100 100 200   1252 'function' => sub { ($_ >= 34121 && $_ <= 34151) || ($_ >= 34010 && $_ <= 34018) },
      100        
764             'lookup_keys' => [ '340', '341' ],
765             'province' => 'Trieste',
766             'province_code' => 'IT-TS'
767             },
768             {
769             'region' => 'Veneto',
770             'lookup_keys' => [ '310', '311' ],
771             'province' => 'Treviso',
772             'province_code' => 'IT-TV'
773             },
774             {
775             'region' => 'Friuli-Venezia Giulia',
776 200 100 100 200   919 'function' => sub { $_ == 33100 || ($_ >= 33010 && $_ <= 33059) },
777 2         344 'lookup_keys' => [ '330', '331' ],
778             'province' => 'Udine',
779             'province_code' => 'IT-UD'
780             },
781             {
782             'region' => 'Lombardy',
783             'lookup_keys' => [ '210', '211' ],
784             'province' => 'Varese',
785             'province_code' => 'IT-VA'
786             },
787             {
788             'region' => 'Piedmont',
789             'lookup_keys' => [ '288', '289' ],
790             'province' => 'Verbano-Cusio-Ossola',
791             'province_code' => 'IT-VB'
792             },
793             {
794             'region' => 'Piedmont',
795             'lookup_keys' => [ '130', '131' ],
796             'province' => 'Vercelli',
797             'province_code' => 'IT-VC'
798             },
799             {
800             'region' => 'Veneto',
801             'lookup_keys' => [ '300', '301' ],
802             'province' => 'Venice',
803             'province_code' => 'IT-VE'
804             },
805             {
806             'region' => 'Veneto',
807             'lookup_keys' => [ '360', '361' ],
808             'province' => 'Vicenza',
809             'province_code' => 'IT-VI'
810             },
811             {
812             'region' => 'Veneto',
813             'lookup_keys' => [ '370', '371' ],
814             'province' => 'Verona',
815             'province_code' => 'IT-VR'
816             },
817             {
818             'region' => 'Lazio',
819             'lookup_keys' => [ '010', '011' ],
820             'province' => 'Viterbo',
821             'province_code' => 'IT-VT'
822             },
823             {
824             'region' => 'Calabria',
825             'lookup_keys' => [ '898', '899' ],
826             'province' => 'Vibo Valentia',
827             'province_code' => 'IT-VV'
828             },
829             ];
830 2         36 my %geo_regions = (
831             # Take from https://en.wikipedia.org/wiki/ISO_3166-2:IT
832             'it' => {
833             'Abruzzo' => 'IT-65',
834             'Basilicata' => 'IT-77',
835             'Calabria' => 'IT-78',
836             'Campania' => 'IT-72',
837             'Emilia-Romagna' => 'IT-45',
838             'Friuli-Venezia Giulia' => 'IT-36',
839             'Lazio' => 'IT-62',
840             'Liguria' => 'IT-42',
841             'Lombardy' => 'IT-25',
842             'Marche' => 'IT-57',
843             'Molise' => 'IT-67',
844             'Piedmont' => 'IT-21',
845             'Apulia' => 'IT-75',
846             'Sardinia' => 'IT-88',
847             'Sicily' => 'IT-82',
848             'Tuscany' => 'IT-52',
849             'Trentino-Alto Adige/Südtirol' => 'IT-32',
850             'Umbria' => 'IT-55',
851             'Aosta Valley' => 'IT-23',
852             'Veneto' => 'IT-34',
853             '-' => '-', # For the Vatican and San Marino
854             },
855             );
856              
857              
858 2         3 %lookup_table = ();
859 2         6 for my $country (keys %geo_provinces) {
860 2         6 my $rh_lookup = $lookup_table{$country} = {};
861 2         3 for my $rh_geo_entry (@{ $geo_provinces{$country} }) {
  2         3  
862 224         226 $rh_geo_entry->{region_code} = $geo_regions{$country}{$rh_geo_entry->{region}};
863 224         121 for my $key (@{ $rh_geo_entry->{lookup_keys} }) {
  224         199  
864 442   100     981 $rh_lookup->{$key} //= [];
865 442         261 push @{ $rh_lookup->{$key} }, $rh_geo_entry;
  442         552  
866             }
867             }
868              
869             }
870              
871             }
872              
873             sub _test_check_lookup_table {
874             # This test is written here, instead of in a .t file, as only it has access
875             # to the private variable %lookup_table
876 1     1   7727 require Test::More;
877 1         4 for my $country (sort keys %lookup_table) {
878 1         2 my $rh_lookup = $lookup_table{$country};
879 1         88 for my $key (sort keys %$rh_lookup) {
880 204 100       109 if (@{ $rh_lookup->{$key} } >= 2) {
  204         276  
881 12 50   29   15 if (! _all(sub { $_->{function} }, @{ $rh_lookup->{$key} })) {
  29         54  
  12         17  
882             my $provinces_string = join(", ",
883 0         0 map { $_->{province} } @{ $rh_lookup->{$key} }
  0         0  
  0         0  
884             );
885 0         0 Test::More::fail("Lookup key '$key' in country $country has multiple provinces $provinces_string but missing functions to distinguish between them. ");
886             }
887             }
888             }
889             }
890             }
891              
892             sub _all {
893 12     12   9 my $rc_sub = shift;
894 12         14 for (@_) {
895 29 50       23 return 0 if ! $rc_sub->();
896             }
897 12         24 return 1;
898             }
899              
900             BEGIN {
901 2     2   6 _build_data();
902             }
903              
904              
905             # --------------------------
906              
907             =head1 AUTHOR
908              
909             David D Lowe, C<< >>
910              
911             =head1 BUGS
912              
913             Please report any bugs or feature requests to C, or through
914             the web interface at L. I will be notified, and then you'll
915             automatically be notified of progress on your bug as I make changes.
916              
917              
918              
919              
920              
921              
922             =head1 SUPPORT
923              
924             You can find documentation for this module with the perldoc command.
925              
926             perldoc Geo::LookupPostcode
927              
928              
929             You can also look for information at:
930              
931             =over 4
932              
933             =item * GitHub issue tracker (report bugs here)
934              
935             L
936              
937             =item * AnnoCPAN: Annotated CPAN documentation
938              
939             L
940              
941             =item * CPAN Ratings
942              
943             L
944              
945             =item * Search CPAN
946              
947             L
948              
949             =back
950              
951             =head1 SEE ALSO
952              
953              
954             L, L and L provides UK
955             postcode validation and location.
956              
957             L provides USA postal code functions.
958              
959             L provides an abstract interface for looking up postcodes. The
960             only two implementations that I am aware of are L and
961             L, for Norway and Denmark.
962              
963              
964             =head1 ACKNOWLEDGEMENTS
965              
966             Most of the data was sourced from Wikipedia.
967              
968             =head1 LICENSE AND COPYRIGHT
969              
970             Copyright 2016 David D Lowe and contributors.
971              
972             This work is licensed under CC BY-SA 3.0.
973              
974             =cut
975              
976             1; # End of Geo::LookupPostcode