File Coverage

blib/lib/Geo/Coder/Abbreviations.pm
Criterion Covered Total %
statement 50 72 69.4
branch 20 44 45.4
condition 9 15 60.0
subroutine 8 8 100.0
pod 3 3 100.0
total 90 142 63.3


line stmt bran cond sub pod time code
1             package Geo::Coder::Abbreviations;
2              
3 5     5   1062485 use warnings;
  5         10  
  5         299  
4 5     5   27 use strict;
  5         9  
  5         108  
5 5     5   2527 use JSON::MaybeXS;
  5         60725  
  5         418  
6 5     5   2615 use LWP::Simple::WithCache;
  5         790861  
  5         477  
7              
8             =head1 NAME
9              
10             Geo::Coder::Abbreviations - Quick and Dirty Interface to https://github.com/mapbox/geocoder-abbreviations
11              
12             =head1 VERSION
13              
14             Version 0.09
15              
16             =cut
17              
18             our %abbreviations;
19             our $VERSION = '0.09';
20              
21             # This is giving 404 errors at the moment
22             # https://github.com/mapbox/mapbox-java/issues/1460
23             # our location = 'https://raw.githubusercontent.com/mapbox/geocoder-abbreviations/master/tokens/en.json';
24 5     5   48 use constant LOCATION => 'https://raw.githubusercontent.com/allison-strandberg/geocoder-abbreviations/master/tokens/en.json';
  5         10  
  5         6829  
25              
26             =head1 SYNOPSIS
27              
28             Provides an interface to https://github.com/mapbox/geocoder-abbreviations.
29             One small function for now, I'll add others later.
30              
31             =head1 SUBROUTINES/METHODS
32              
33             =head2 new
34              
35             Creates a Geo::Coder::Abbreviations object.
36             It takes no arguments.
37             If you have L installed it will load much faster,
38             otherwise it will download the database from the Internet
39             when the class is first instantiated.
40              
41             =cut
42              
43             sub new {
44 6     6 1 485785 my $proto = shift;
45 6   100     82 my $class = ref($proto) || $proto;
46              
47 6 100       33 if(!defined($class)) {
    50          
48             # Using Geo::Coder::Abbreviations->new(), not Geo::Coder::Abbreviations::new()
49             # carp(__PACKAGE__, ' use ->new() not ::new() to instantiate');
50             # return;
51              
52             # FIXME: this only works when no arguments are given
53 1         4 $class = __PACKAGE__;
54             } elsif(ref($class)) {
55             # clone the given object
56             # return bless { %{$class}, %args }, ref($class);
57 0         0 return bless { %{$class} }, ref($class);
  0         0  
58             }
59              
60 6 100       25 unless(scalar keys(%abbreviations)) {
61 2 50       9 if(eval { require HTTP::Cache::Transparent; }) {
  2         488  
62 0         0 require File::Spec; # That should be installed
63              
64 0         0 File::Spec->import();
65 0         0 HTTP::Cache::Transparent->import();
66              
67 0         0 my $cache_dir;
68 0 0 0     0 if($cache_dir = ($ENV{'CACHE_DIR'} || $ENV{'CACHEDIR'})) {
69 0 0       0 mkdir $cache_dir, 02700 if(!-d $cache_dir);
70 0         0 $cache_dir = File::Spec->catfile($cache_dir, 'http-cache-transparent');
71             } else {
72             # $cache_dir = File::Spec->catfile(File::Spec->tmpdir(), 'cache', 'http-cache-transparent');
73 0         0 $cache_dir = File::Spec->catfile(File::HomeDir->my_home(), '.cache', 'http-cache-transparent');
74             }
75              
76 0 0       0 HTTP::Cache::Transparent::init({
77             BasePath => $cache_dir,
78             # Verbose => $opts{'v'} ? 1 : 0,
79             NoUpdate => 60 * 60 * 24,
80             MaxAge => 30 * 24
81             }) || die "$0: $cache_dir $!";
82             }
83              
84             # TODO: Support other languages
85 2         21 my $data = LWP::Simple::WithCache::get(LOCATION);
86              
87 2 50       733371 if(!defined($data)) {
88             # die 'error downloading from ', LOCATION;
89 0         0 $data = join('', grep(!/^\s*(#|$)/, ));
90             }
91             %abbreviations = map {
92 686         3098 my %rc = ();
93 686 100 100     1466 if(defined($_->{'type'}) && ($_->{'type'} eq 'way')) {
94 124         158 foreach my $token(@{$_->{'tokens'}}) {
  124         186  
95 288         667 $rc{uc($token)} = uc($_->{'canonical'});
96             }
97             }
98 686         1106 %rc;
99 2         33 } @{JSON::MaybeXS->new()->utf8()->decode($data)};
  2         26  
100              
101             # %abbreviations = map { (defined($_->{'type'}) && ($_->{'type'} eq 'way')) ? (uc($_->{'full'}) => uc($_->{'canonical'})) : () } @{JSON::MaybeXS->new()->utf8()->decode($data)};
102             }
103              
104 6         474 return bless {
105             table => \%abbreviations
106             }, $class;
107             }
108              
109             =head2 abbreviate
110              
111             Abbreviate a place.
112              
113             use Geo::Coder::Abbreviations;
114              
115             my $abbr = Geo::Coder::Abbreviations->new();
116             print $abbr->abbreviate('Road'), "\n"; # prints 'RD'
117             print $abbr->abbreviate('RD'), "\n"; # prints 'RD'
118              
119             =cut
120              
121             sub abbreviate {
122 3     3 1 8 my $self = shift;
123              
124 3         30 return $self->{'table'}->{uc(shift)};
125             }
126              
127             =head2 normalize
128              
129             Normalize and abbreviate street names - useful for comparisons
130              
131             print $abbr->normalize({ street => '10 Downing Street' }), "\n"; # prints '10 DOWNING ST'
132              
133             Can be run as a class method
134              
135             print Geo::Coder::Abbreviations('1600 Pennsylvania Avenue NW'), "\n"; # prints '1600 Pennsylvia Ave NW'
136              
137             =cut
138              
139             sub normalize
140             {
141 3     3 1 952 my $self = shift;
142 3         6 my %params;
143              
144             # Try hard to support whatever API that the user wants to use
145 3 100 33     30 if(!ref($self)) {
    50          
    50          
    50          
    50          
146 1 50       6 if(scalar(@_)) {
    0          
    0          
147 1         10 return(__PACKAGE__->new()->normalize(@_));
148             } elsif(!defined($self)) {
149             # Geo::Coder::Abbreviations->normalize()
150 0         0 Carp::croak('Usage: ', __PACKAGE__, '::normalize(street => $street)');
151             } elsif($self eq __PACKAGE__) {
152 0         0 Carp::croak("Usage: $self", '::normalize(street => $street)');
153             }
154 0         0 return(__PACKAGE__->new()->normalize($self));
155             } elsif(ref($self) eq 'HASH') {
156 0         0 return(__PACKAGE__->new()->noralize($self));
157             } elsif(ref($_[0]) eq 'HASH') {
158 0         0 %params = %{$_[0]};
  0         0  
159             # } elsif(ref($_[0]) && (ref($_[0] !~ /::/))) {
160             } elsif(ref($_[0])) {
161 0         0 Carp::croak('Usage: ', __PACKAGE__, '::normalize(street => $street)');
162             } elsif(scalar(@_) && (scalar(@_) % 2 == 0)) {
163 0         0 %params = @_;
164             } else {
165 2         9 $params{'street'} = shift;
166             }
167              
168 2         6 my $street = $params{'street'};
169              
170 2         7 $street = uc($street);
171 2 50       34 if($street =~ /(.+)\s+(.+)\s+(.+)/) {
    0          
172 2         91 my $a;
173 2 100 66     45 if((lc($2) ne 'cross') && ($a = $self->abbreviate($2))) {
    50          
174 1         7 $street = "$1 $a $3";
175             } elsif($a = $self->abbreviate($3)) {
176 1         6 $street = "$1 $2 $a";
177             }
178             } elsif($street =~ /(.+)\s(.+)$/) {
179 0 0       0 if(my $a = $self->abbreviate($2)) {
180 0         0 $street = "$1 $a";
181             }
182             }
183 2         17 $street =~ s/^0+//; # Turn 04th St into 4th St
184 2         21 return $street;
185             }
186              
187             =head1 SEE ALSO
188              
189             L
190             L
191             L
192              
193             =head1 AUTHOR
194              
195             Nigel Horne, C<< >>
196              
197             =head1 BUGS
198              
199             You may need to ensure you don't translate "Cross Street" to "X ST".
200             See t/abbreviations.t.
201              
202             =head1 SUPPORT
203              
204             You can find documentation for this module with the perldoc command.
205              
206             perldoc Geo::Coder::Abbreviations
207              
208             You can also look for information at:
209              
210             =over 4
211              
212             =item * RT: CPAN's request tracker
213              
214             L
215              
216             =item * Search CPAN
217              
218             L
219              
220             =back
221              
222             =head1 ACKNOWLEDGMENTS
223              
224             =head1 LICENSE AND COPYRIGHT
225              
226             Copyright 2020-2024 Nigel Horne.
227              
228             This program is released under the following licence: GPL2
229              
230             =cut
231              
232             1; # End of Geo::Coder::Abbreviations
233              
234             # https://raw.githubusercontent.com/mapbox/geocoder-abbreviations/master/tokens/en.json is giving 404 errors at the moment
235             # so here is a copy until it's back
236              
237             __DATA__