File Coverage

blib/lib/Geo/ICAO.pm
Criterion Covered Total %
statement 98 98 100.0
branch 31 38 81.5
condition 9 9 100.0
subroutine 22 22 100.0
pod 12 12 100.0
total 172 179 96.0


line stmt bran cond sub pod time code
1             #
2             # This file is part of Geo-ICAO
3             #
4             # This software is copyright (c) 2007 by Jerome Quelin.
5             #
6             # This is free software; you can redistribute it and/or modify it under
7             # the same terms as the Perl 5 programming language system itself.
8             #
9 3     3   2225 use 5.008;
  3         8  
  3         136  
10 3     3   19 use warnings;
  3         6  
  3         76  
11 3     3   15 use strict;
  3         7  
  3         1815  
12              
13             package Geo::ICAO;
14             our $VERSION = '1.100140';
15             # ABSTRACT: Airport and ICAO codes lookup
16              
17 3     3   17 use Carp;
  3         6  
  3         313  
18 3     3   16224 use File::ShareDir qw{ dist_dir };
  3         49861  
  3         302  
19 3     3   43 use List::Util qw{ first };
  3         5  
  3         2182  
20 3     3   3498 use Path::Class;
  3         522933  
  3         512  
21 3     3   5126 use Readonly;
  3         34920  
  3         249  
22 3     3   4852 use Sub::Exporter qw{ setup_exporter };
  3         132031  
  3         25  
23              
24              
25             # -- exporting
26             {
27             my @regions = qw{ all_region_codes all_region_names region2code code2region };
28             my @countries = qw{ all_country_codes all_country_names country2code code2country };
29             my @airports = qw{ all_airport_codes all_airport_names airport2code code2airport };
30             setup_exporter( {
31             exports => [ @regions, @countries, @airports ],
32             groups => {
33             region => \@regions,
34             country => \@countries,
35             airport => \@airports,
36             }
37             } );
38             }
39              
40              
41             #-- private vars.
42              
43             # - vars defined statically
44              
45             # list of ICAO codes for the regions with their name.
46             Readonly my %code2region => (
47             A => 'Western South Pacific',
48             B => 'Iceland/Greenland',
49             C => 'Canada',
50             D => 'West Africa',
51             E => 'Northern Europe',
52             F => 'Southern Africa',
53             G => 'Northwestern Africa',
54             H => 'Northeastern Africa',
55             K => 'USA',
56             L => 'Southern Europe and Israel',
57             M => 'Central America',
58             N => 'South Pacific',
59             O => 'Southwest Asia, Afghanistan and Pakistan',
60             P => 'Eastern North Pacific',
61             R => 'Western North Pacific',
62             S => 'South America',
63             T => 'Caribbean',
64             U => 'Russia and former Soviet States',
65             V => 'South Asia and mainland Southeast Asia',
66             W => 'Maritime Southeast Asia',
67             Y => 'Australia',
68             Z => 'China, Mongolia and North Korea',
69             );
70              
71             # list of ICAO codes for the countries with their name.
72             Readonly my %code2country => _get_code2country();
73              
74             # location of data file
75             Readonly my $FDATA => file( dist_dir('Geo-ICAO'), 'icao.data' );
76              
77              
78             # - vars computed after other vars
79              
80             my %region2code = reverse %code2region;
81             my %country2code;
82             { # need to loop, since some countries have more than one code.
83             foreach my $code ( keys %code2country ) {
84             my $country = $code2country{$code};
85             push @{ $country2code{$country} }, $code;
86             }
87             }
88              
89              
90             #-- public subs
91              
92             # - subs handling regions.
93              
94              
95 1     1 1 876 sub all_region_codes { return keys %code2region; }
96 1     1 1 19 sub all_region_names { return keys %region2code; }
97              
98              
99              
100 2     2 1 421 sub region2code { return $region2code{$_[0]}; }
101             sub code2region {
102 7     7 1 1913 my ($code) = @_;
103 7         18 my $letter = substr $code, 0, 1; # can be called with an airport code
104 7         89 return $code2region{$letter};
105             }
106              
107              
108             # - subs handling countries.
109              
110              
111             sub all_country_codes {
112 3     3 1 21933 my ($code) = @_;
113              
114 3 100       28 return keys %code2country unless defined $code; # no filters
115             # sanity checks on params
116 2 100       7 croak "'$code' is not a valid region code" unless defined code2region($code);
117 1         18 return grep { /^$code/ } keys %code2country; # filtering
  237         1210  
118             }
119              
120              
121              
122             sub all_country_names {
123 3     3 1 1735 my ($code) = @_;
124              
125 3 100       97 return keys %country2code unless defined $code; # no filters
126             # sanity checks on params
127 2 100       9 croak "'$code' is not a valid region code" unless defined code2region($code);
128              
129             # %country2code holds array refs. but even if a country has more
130             # than one code assigned, they will be in the same region: we just
131             # need to test the first code.
132 1         35 return grep { $country2code{$_}[0] =~ /^$code/ } keys %country2code;
  227         10125  
133             }
134              
135              
136              
137             sub country2code {
138 4     4 1 2500 my ($country) = @_;
139 4         11 my $codes = $country2code{$country};
140 4 100       39 return defined $codes ? @$codes : undef;
141             }
142              
143              
144              
145             sub code2country {
146 7     7 1 1853 my ($code) = @_;
147 7   100     36 return $code2country{$code}
148             || $code2country{substr($code,0,2)}
149             || $code2country{substr($code,0,1)};
150             }
151              
152              
153             # - subs handling airports
154              
155              
156             sub all_airport_codes {
157 5     5 1 4714 my ($code) = @_;
158              
159             # sanity checks on params
160 5 50       25 croak 'should provid a region or country code' unless defined $code;
161 5 100 100     52 croak "'$code' is not a valid region or country code"
162             unless exists $code2country{$code}
163             || exists $code2region{$code};
164              
165 3 50       85 open my $fh, '<', $FDATA or die "can't open $FDATA: $!";
166 3         887 my @codes;
167             LINE:
168 3         110 while ( my $line = <$fh>) {
169 25320 100       113963 next LINE unless $line =~ /^$code/; # filtering on $code
170 222         505 my ($c, undef) = split/\|/, $line;
171 222         769 push @codes, $c;
172             }
173 3         103 close $fh;
174 3         188 return @codes;
175             }
176              
177              
178              
179             sub all_airport_names {
180 5     5 1 4968 my ($code) = @_;
181              
182             # sanity checks on params
183 5 50       17 croak 'should provid a region or country code' unless defined $code;
184 5 100 100     39 croak "'$code' is not a valid region or country code"
185             unless exists $code2country{$code}
186             || exists $code2region{$code};
187              
188 3 50       75 open my $fh, '<', $FDATA or die "can't open $FDATA: $!";
189 3         601 my @codes;
190             LINE:
191 3         63 while ( my $line = <$fh>) {
192 25320 100       113315 next LINE unless $line =~ /^$code/; # filtering on $code
193 122         305 my (undef, $airport, undef) = split/\|/, $line;
194 122         500 push @codes, $airport;
195             }
196 3         76 close $fh;
197 3         131 return @codes;
198             }
199              
200              
201              
202             sub airport2code {
203 100     100 1 1958 my ($name) = @_;
204              
205 100 50       924 open my $fh, '<', $FDATA or die "can't open $FDATA: $!";
206             LINE:
207 100         17042 while ( my $line = <$fh>) {
208 66583         169571 my ($code, $airport, undef) = split/\|/, $line;
209 66583 100       295566 next LINE unless lc($airport) eq lc($name);
210 99         1718 close $fh;
211 99         676 return $code;
212             }
213 1         56 close $fh;
214 1         25 return; # no airport found
215             }
216              
217              
218              
219             sub code2airport {
220 4     4 1 636 my ($code) = @_;
221              
222 4 50       61 open my $fh, '<', $FDATA or die "can't open $FDATA: $!";
223             LINE:
224 4         912 while ( my $line = <$fh>) {
225 21402 100       90511 next LINE unless $line =~ /^$code\|/;
226 3         12 chomp $line;
227 3         20 my (undef, $airport, $location) = split/\|/, $line;
228 3         153 close $fh;
229 3 100       57 return wantarray ? ($airport, $location) : $airport;
230             }
231 1         49 close $fh;
232 1         16 return; # no airport found
233             }
234              
235              
236             # -- private subs
237              
238             #
239             # my %data = _get_code2country();
240             #
241             # read the country.data file shipped in the share/ directory, parse and
242             # return it as a hash. the key is the country code, the value is the
243             # country name.
244             #
245             sub _get_code2country {
246 3     3   7 my %data;
247 3         18 my $file = file( dist_dir('Geo-ICAO'), 'country.data' );
248 3 50       1700 open my $fh, '<', $file or die "can't open $file: $!";
249 3         1138 while ( my $line = <$fh>) {
250 711         1055 chomp $line;
251 711         1426 my ($code, $country) = split/\|/, $line;
252 711         6920 $data{$code} = $country;
253             }
254 3         115 close $fh;
255 3         614 return %data;
256             }
257              
258              
259             1;
260              
261              
262             =pod
263              
264             =head1 NAME
265              
266             Geo::ICAO - Airport and ICAO codes lookup
267              
268             =head1 VERSION
269              
270             version 1.100140
271              
272             =head1 SYNOPSIS
273              
274             use Geo::ICAO qw{ :all };
275              
276             my @region_codes = all_region_codes();
277             my @region_names = all_region_names();
278             my $code = region2code('Canada');
279             my $region = code2region('C');
280              
281             my @country_codes = all_country_codes();
282             my @country_names = all_country_names();
283             my @codes = country2code('Brazil');
284             my $region = code2country('SB');
285              
286             my @airport_codes = all_airport_codes('B');
287             my @airport_names = all_airport_names('B');
288             my $code = airport2code('Lyon Bron Airport');
289             my $airport = code2airport('LFLY');
290             my ($airport, $location) = code2airport('LFLY'); # list context
291              
292             =head1 DESCRIPTION
293              
294             The International Civil Aviation Organization (ICAO), a major agency of
295             the United Nations, codifies the principles and techniques of
296             international air navigation and fosters the planning and development of
297             international air transport to ensure safe and orderly growth. Among the
298             standards defined by ICAO is an airport code system (not to be confused
299             with IATA airport codes), using 4-letter for this.
300              
301             This module provides easy access to the list of airport ICAO codes, with
302             mapping of those codes with airport names, country and region codes.
303              
304             Nothing is exported by default, but all the functions described below
305             are exportable: it's up to you to decide what you want to import. Export
306             is done with L, so you can play all kind of tricks.
307              
308             Note that the keyword C<:all> will import everything, and each category
309             of function provides its own keyword. See below.
310              
311             =head2 Regions
312              
313             The first letter of an ICAO code refer to the region of the airport. The
314             region is quite loosely defined as per the ICAO. This set of functions
315             allow retrieval and digging of the regions.
316              
317             Note: you can import all those functions with the C<:region> keyword.
318              
319             =head2 Countries
320              
321             The first two letters of an ICAO code refer to the country of the
322             airport. Once again, the rules are not really set in stone: some codes
323             are shared by more than one country, some countries are defined more
324             than once... and some countries (Canada, USA, Russia, Australia and
325             China) are even coded on only one letter - ie, the country is the same
326             as the region). This set of functions allow retrieval and digging of the
327             countries.
328              
329             Note: you can import all those functions with the C<:country> keyword.
330              
331             =head2 Airports
332              
333             This set of functions allow retrieval and digging of the airports, which
334             are defined on 4 letters.
335              
336             Note: you can import all those functions with the C<:airport> keyword.
337              
338             =head1 REGION FUNCTIONS
339              
340             =head2 my @codes = all_region_codes();
341              
342             Return the list of all single letters defining an ICAO region. No
343             parameter needed.
344              
345             =head2 my @regions = all_region_names();
346              
347             Return the list of all ICAO region names. No parameter needed.
348              
349             =head2 my $code = region2code( $region );
350              
351             Return the one-letter ICAO C<$code> corresponding to C<$region>. If the
352             region does not exist, return undef.
353              
354             =head2 my $region = code2region( $code );
355              
356             Return the ICAO C<$region> corresponding to C<$code>. Note that C<$code>
357             can be a one-letter code (region), two-letters code (country) or a four-
358             letters code (airport): in either case, the region will be returned.
359              
360             Return undef if the associated region doesn't exist.
361              
362             =head1 COUNTRIES FUNCTIONS
363              
364             =head2 my @codes = all_country_codes( [$code] );
365              
366             Return the list of all single- or double-letters defining an ICAO
367             country. If a region C<$code> is given, return only the country codes of
368             this region. (Note: dies if C<$code> isn't a valid ICAO region code).
369              
370             =head2 my @countries = all_country_names( [$code] );
371              
372             Return the list of all ICAO country names. If a region C<$code> is
373             given, return only the country names of this region. (Note: dies if
374             C<$code> isn't a valid ICAO region code).
375              
376             =head2 my @codes = country2code( $country );
377              
378             Return the list of ICAO codes corresponding to C<$country>. It's a list
379             since some countries have more than one code. Note that the codes can be
380             single-letters (USA, etc.)
381              
382             =head2 my $country = code2country( $code );
383              
384             Return the ICAO C<$country> corresponding to C<$code>. Note that
385             C<$code> can be a classic country code, or a four-letters code
386             (airport): in either case, the region will be returned.
387              
388             Return undef if the associated region doesn't exist.
389              
390             =head1 AIRPORT FUNCTIONS
391              
392             =head2 my @codes = all_airport_codes( $code );
393              
394             Return the list of all ICAO airport codes in the C<$code> country
395             (C<$code> can also be a region code). Note that compared to the
396             region or country equivalent, this function B an argument.
397             It will die otherwise (or if C<$code> isn't a valid ICAO country or
398             region code).
399              
400             =head2 my @codes = all_airport_names( $code );
401              
402             Return the list of all ICAO airport names in the C<$code> country
403             (C<$code> can also be a region code). Note that compared to the
404             region or country equivalent, this function B an argument.
405             It will die otherwise (or if C<$code> isn't a valid ICAO country or
406             region code).
407              
408             =head2 my $code = airport2code( $airport );
409              
410             Return the C<$code> of the C<$airport>, undef i no airport matched. Note
411             that the string comparison is done on a case-insensitive basis.
412              
413             =head2 my $airport = code2airport( $code );
414              
415             Return the C<$airport> name corresponding to C<$code>. In list context,
416             return both the airport name and its location (if known).
417              
418             =head1 SEE ALSO
419              
420             You can look for information on this module at:
421              
422             =over 4
423              
424             =item * Search CPAN
425              
426             L
427              
428             =item * See open / report bugs
429              
430             L
431              
432             =item * Git repository
433              
434             L
435              
436             =item * AnnoCPAN: Annotated CPAN documentation
437              
438             L
439              
440             =item * CPAN Ratings
441              
442             L
443              
444             =back
445              
446             =head1 AUTHOR
447              
448             Jerome Quelin
449              
450             =head1 COPYRIGHT AND LICENSE
451              
452             This software is copyright (c) 2007 by Jerome Quelin.
453              
454             This is free software; you can redistribute it and/or modify it under
455             the same terms as the Perl 5 programming language system itself.
456              
457             =cut
458              
459              
460             __END__