our
%known_locations
= (
'Newport Pagnell, Buckinghamshire, England'
=> {
'latitude'
=> 52.08675,
'longitude'
=> -0.72270
},
);
our
%unknown_locations
;
use
constant
LIBPOSTAL_NOT_INSTALLED
=> -1;
our
$libpostal_is_installed
= LIBPOSTAL_UNKNOWN;
our
$VERSION
=
'0.32'
;
sub
new {
my
(
$proto
,
%param
) =
@_
;
my
$class
=
ref
(
$proto
) ||
$proto
;
return
unless
(
$class
);
if
(
my
$openaddr
=
$param
{
'openaddr'
}) {
Carp::croak
"Can't find the directory $openaddr"
if
((!-d
$openaddr
) || (!-r
$openaddr
));
return
bless
{
openaddr
=>
$openaddr
,
cache
=>
$param
{
'cache'
} },
$class
;
}
Carp::croak(__PACKAGE__,
": usage: new(openaddr => '/path/to/openaddresses')"
);
}
sub
geocode {
my
$self
=
shift
;
my
%param
;
if
(
ref
(
$_
[0]) eq
'HASH'
) {
%param
= %{
$_
[0]};
}
elsif
(
ref
(
$_
[0])) {
Carp::croak(
'Usage: geocode(location => $location|scantext => $text)'
);
}
elsif
(
scalar
(
@_
) % 2 == 0) {
%param
=
@_
;
}
else
{
$param
{location} =
shift
;
}
if
(
my
$scantext
=
$param
{
'scantext'
}) {
return
if
(
length
(
$scantext
) < 6);
$scantext
=~ s/\W+/ /g;
my
@words
=
split
(/\s/,
$scantext
);
my
$count
=
scalar
(
@words
);
my
$offset
= 0;
my
@rc
;
while
(
$offset
<
$count
) {
if
(
length
(
$words
[
$offset
]) < 2) {
$offset
++;
next
;
}
my
$l
;
if
((
$l
=
$self
->geocode(
location
=>
$words
[
$offset
])) &&
ref
(
$l
)) {
push
@rc
,
$l
;
}
if
(
$offset
<
$count
- 1) {
my
$addr
=
join
(
', '
,
$words
[
$offset
],
$words
[
$offset
+ 1]);
if
(
length
(
$addr
) == 0) {
$offset
++;
}
if
(
$addr
=~ /\s+(\d{2,5}\s+)(?![a|p]m\b)(([a-zA-Z|\s+]{1,5}){1,2})?([\s|\,|.]+)?(([a-zA-Z|\s+]{1,30}){1,4})(court|ct|street|st|drive|dr|lane|ln|road|rd|blvd)([\s|\,|.|\;]+)?(([a-zA-Z|\s+]{1,30}){1,2})([\s|\,|.]+)?\b(AK|AL|AR|AZ|CA|CO|CT|DC|DE|FL|GA|GU|HI|IA|ID|IL|IN|KS|KY|LA|MA|MD|ME|MI|MN|MO|MS|MT|NC|ND|NE|NH|NJ|NM|NV|NY|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VA|VI|VT|WA|WI|WV|WY)([\s|\,|.]+)?(\s+\d{5})?([\s|\,|.]+)/i) {
if
((
$l
=
$self
->geocode(
location
=>
"$addr, US"
)) &&
ref
(
$l
)) {
$l
->confidence(0.8);
$l
->location(
"$addr, USA"
);
$l
->country(
'US'
);
push
@rc
,
$l
;
}
}
elsif
(
$addr
=~ /\s+(\d{2,5}\s+)(?![a|p]m\b)(([a-zA-Z|\s+]{1,5}){1,2})?([\s|\,|.]+)?(([a-zA-Z|\s+]{1,30}){1,4})(court|ct|street|st|drive|dr|lane|ln|road|rd|blvd)([\s|\,|.|\;]+)?(([a-zA-Z|\s+]{1,30}){1,2})([\s|\,|.]+)?\b(AB|BC|MB|NB|NL|NT|NS|ON|PE|QC|SK|YT)([\s|\,|.]+)?(\s+\d{5})?([\s|\,|.]+)/i) {
if
((
$l
=
$self
->geocode(
location
=>
"$addr, Canada"
)) &&
ref
(
$l
)) {
$l
->confidence(0.8);
$l
->location(
"$addr, Canada"
);
$l
->country(
'CA'
);
push
@rc
,
$l
;
}
}
elsif
(
$addr
=~ /([a-zA-Z|\s+]{1,30}){1,2}([\s|\,|.]+)?\b(AK|AL|AR|AZ|CA|CO|CT|DC|DE|FL|GA|GU|HI|IA|ID|IL|IN|KS|KY|LA|MA|MD|ME|MI|MN|MO|MS|MT|NC|ND|NE|NH|NJ|NM|NV|NY|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VA|VI|VT|WA|WI|WV|WY)/i) {
if
((
$l
=
$self
->geocode(
location
=>
"$addr, US"
)) &&
ref
(
$l
)) {
$l
->confidence(0.6);
$l
->location(
"$addr, USA"
);
$l
->city(
uc
($1));
$l
->state(
uc
($3));
$l
->country(
'US'
);
push
@rc
,
$l
;
}
}
elsif
(
$addr
=~ /([a-zA-Z|\s+]{1,30}){1,2}([\s|\,|.]+)?\b(AB|BC|MB|NB|NL|NT|NS|ON|PE|QC|SK|YT)/i) {
if
((
$l
=
$self
->geocode(
location
=>
"$addr, Canada"
)) &&
ref
(
$l
)) {
$l
->confidence(0.6);
$l
->location(
"$addr, Canada"
);
$l
->city(
uc
($1));
$l
->state(
uc
($3));
$l
->country(
'US'
);
push
@rc
,
$l
;
}
}
if
((
$l
=
$self
->geocode(
location
=>
$addr
)) &&
ref
(
$l
)) {
$l
->confidence(0.1);
$l
->location(
$addr
);
push
@rc
,
$l
;
}
if
(
$offset
<
$count
- 2) {
$addr
=
join
(
', '
,
$words
[
$offset
],
$words
[
$offset
+ 1],
$words
[
$offset
+ 2]);
if
((
$l
=
$self
->geocode(
location
=>
$addr
)) &&
ref
(
$l
)) {
$l
->confidence(1.0);
$l
->location(
$addr
);
push
@rc
,
$l
;
}
}
}
$offset
++;
}
return
@rc
;
}
my
$location
=
$param
{location}
or Carp::croak(
'Usage: geocode(location => $location|scantext => $text)'
);
$location
=~ s/,\s+,\s+/, /g;
if
(
$location
=~ /^,\s*(.+)/) {
$location
= $1;
}
if
(
$location
=~ /^(.+),?\s
*Washington
\s
*DC
$/i) {
$location
=
"$1, Washington, DC, USA"
;
}
elsif
(
$location
=~ /^(.*),?\s
*Saint
Louis, (Missouri|MO)(.*)$/) {
$location
=
"$1, St. Louis, MO$3"
;
}
if
(
my
$rc
=
$known_locations
{
$location
}) {
return
Geo::Location::Point->new({
'lat'
=>
$rc
->{
'latitude'
},
'long'
=>
$rc
->{
'longitude'
},
'location'
=>
$location
,
'database'
=>
'OpenAddresses'
});
}
$self
->{
'location'
} =
$location
;
my
$county
;
my
$state
;
my
$country
;
my
$street
;
$location
=~ s/\.//g;
if
(
$location
!~ /,/) {
if
(
$location
=~ /^(.+?)\s+(United States|USA|US)$/i) {
my
$l
= $1;
$l
=~ s/\s+//g;
if
(
my
$rc
=
$self
->_get(
$l
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
}
elsif
(
$location
=~ /^(.+?)\s+(England|Scotland|Wales|Northern Ireland|UK|GB)$/i) {
my
$l
= $1;
$l
=~ s/\s+//g;
if
(
my
$rc
=
$self
->_get(
$l
,
'GB'
)) {
$rc
->{
'country'
} =
'GB'
;
return
$rc
;
}
}
elsif
(
$location
=~ /^(.+?)\s+Canada$/i) {
my
$l
= $1;
$l
=~ s/\s+//g;
if
(
my
$rc
=
$self
->_get(
$l
,
'CA'
)) {
$rc
->{
'country'
} =
'CA'
;
return
$rc
;
}
}
}
my
$ap
;
if
((
$location
=~ /USA$/) || (
$location
=~ /United States$/)) {
$ap
=
$self
->{
'ap'
}->{
'us'
} // Lingua::EN::AddressParse->new(
country
=>
'US'
,
auto_clean
=> 1,
force_case
=> 1,
force_post_code
=> 0);
$self
->{
'ap'
}->{
'us'
} =
$ap
;
}
elsif
(
$location
=~ /(England|Scotland|Wales|Northern Ireland|UK|GB)$/i) {
$ap
=
$self
->{
'ap'
}->{
'gb'
} // Lingua::EN::AddressParse->new(
country
=>
'GB'
,
auto_clean
=> 1,
force_case
=> 1,
force_post_code
=> 0);
$self
->{
'ap'
}->{
'gb'
} =
$ap
;
}
elsif
(
$location
=~ /Canada$/) {
$ap
=
$self
->{
'ap'
}->{
'ca'
} // Lingua::EN::AddressParse->new(
country
=>
'CA'
,
auto_clean
=> 1,
force_case
=> 1,
force_post_code
=> 0);
$self
->{
'ap'
}->{
'ca'
} =
$ap
;
}
elsif
(
$location
=~ /Australia$/) {
$ap
=
$self
->{
'ap'
}->{
'au'
} // Lingua::EN::AddressParse->new(
country
=>
'AU'
,
auto_clean
=> 1,
force_case
=> 1,
force_post_code
=> 0);
$self
->{
'ap'
}->{
'au'
} =
$ap
;
}
if
(
$ap
) {
my
$l
=
$location
;
if
(
$l
=~ /(.+), (England|UK)$/i) {
$l
=
"$1, GB"
;
}
if
(
my
$error
=
$ap
->parse(
$l
)) {
}
else
{
my
%c
=
$ap
->components();
my
%addr
= (
'location'
=>
$l
);
$street
=
$c
{
'street_name'
};
if
(
my
$type
=
$c
{
'street_type'
}) {
if
(
my
$a
= Geo::Coder::Free::_abbreviate(
$type
)) {
$street
.=
" $a"
;
}
else
{
$street
.=
" $type"
;
}
if
(
my
$suffix
=
$c
{
'street_direction_suffix'
}) {
$street
.=
" $suffix"
;
}
$street
=~ s/^0+//;
$addr
{
'road'
} =
$street
;
}
if
(
length
(
$c
{
'subcountry'
}) == 2) {
$addr
{
'state'
} =
$c
{
'subcountry'
};
}
else
{
if
(
$c
{
'country'
} =~ /Canada/i) {
$addr
{
'country'
} =
'CA'
;
if
(
my
$twoletterstate
= Locale::CA->new()->{province2code}{
uc
(
$c
{
'subcountry'
})}) {
$addr
{
'state'
} =
$twoletterstate
;
}
}
elsif
(
$c
{
'country'
} =~ /^(United States|USA|US)$/i) {
$addr
{
'country'
} =
'US'
;
if
(
my
$twoletterstate
= Locale::US->new()->{state2code}{
uc
(
$c
{
'subcountry'
})}) {
$addr
{
'state'
} =
$twoletterstate
;
}
}
elsif
(
$c
{
'country'
}) {
$addr
{
'country'
} =
$c
{
'country'
};
if
(
$c
{
'subcountry'
}) {
$addr
{
'state'
} =
$c
{
'subcountry'
};
}
}
}
$addr
{
'house_number'
} =
$c
{
'property_identifier'
};
$addr
{
'city'
} =
$c
{
'suburb'
};
if
(
$addr
{
'house_number'
}) {
if
(
my
$rc
=
$self
->_search(\
%addr
, (
'house_number'
,
'road'
,
'city'
,
'state'
,
'country'
))) {
return
$rc
;
}
}
if
(
my
$rc
=
$self
->_search(\
%addr
, (
'road'
,
'city'
,
'state'
,
'country'
))) {
return
$rc
;
}
}
}
if
(
$location
=~ /^(.+?)[,\s]+(United States|USA|US)$/i) {
my
$l
= $1;
$l
=~ s/,/ /g;
$l
=~ s/\s\s+/ /g;
if
((
$location
!~ /\sCounty,/i) && (
my
$href
= (Geo::StreetAddress::US->parse_location(
$l
) || Geo::StreetAddress::US->parse_address(
$l
)))) {
if
(
$state
=
$href
->{
'state'
}) {
if
(
length
(
$state
) > 2) {
if
(
my
$twoletterstate
= Locale::US->new()->{state2code}{
uc
(
$state
)}) {
$state
=
$twoletterstate
;
}
}
my
$city
;
if
(
$href
->{city}) {
$city
=
uc
(
$href
->{city});
}
if
(
$street
=
$href
->{street}) {
if
(
$href
->{
'type'
} && (
my
$type
= Geo::Coder::Free::_abbreviate(
$href
->{
'type'
}))) {
$street
.=
" $type"
;
}
if
(
$href
->{suffix}) {
$street
.=
' '
.
$href
->{suffix};
}
if
(
my
$prefix
=
$href
->{prefix}) {
$street
=
"$prefix $street"
;
}
if
(
$href
->{
'number'
}) {
if
(
my
$rc
=
$self
->_get(
$href
->{
'number'
},
"$street$city$state"
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
}
if
(
my
$rc
=
$self
->_get(
"$street$city$state"
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
}
}
}
my
@addr
=
split
(/,\s*/,
$location
);
if
(
scalar
(
@addr
) == 5) {
$state
=
$addr
[3];
if
(
length
(
$state
) > 2) {
if
(
my
$twoletterstate
= Locale::US->new()->{state2code}{
uc
(
$state
)}) {
$state
=
$twoletterstate
;
}
}
if
(
length
(
$state
) == 2) {
$addr
[1] = Geo::Coder::Free::_normalize(
$addr
[1]);
if
(
my
$rc
=
$self
->_get(
$addr
[0],
$addr
[1],
$addr
[2],
$state
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
}
if
(
length
(
$state
) == 2) {
$addr
[0] = Geo::Coder::Free::_normalize(
$addr
[0]);
$addr
[2] =~ s/\s+COUNTY$//i;
if
(
my
$rc
=
$self
->_get(
$addr
[0],
$addr
[1],
$addr
[2],
$state
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
if
(
my
$rc
=
$self
->_get(
$addr
[0],
$addr
[1],
$state
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
}
}
}
if
(
$location
=~ /(.+),\s*([\s\w]+),\s*([\w\s]+)$/) {
my
$city
= $1;
$state
= $2;
$country
= $3;
$state
=~ s/\s$//g;
$country
=~ s/\s$//g;
my
$c
;
if
((
uc
(
$country
) eq
'ENGLAND'
) ||
(
uc
(
$country
) eq
'SCOTLAND'
) ||
(
uc
(
$country
) eq
'WALES'
)) {
$country
=
'Great Britain'
;
$c
=
'gb'
;
}
else
{
$c
= country2code(
$country
);
}
if
(
$c
) {
if
(
$c
eq
'us'
) {
if
(
length
(
$state
) > 2) {
if
(
my
$twoletterstate
= Locale::US->new()->{state2code}{
uc
(
$state
)}) {
$state
=
$twoletterstate
;
}
}
my
$rc
;
if
(
$city
!~ /,/) {
$city
=
uc
(
$city
);
if
(
$city
=~ /^(.+)\sCOUNTY$/) {
if
(
$rc
=
$self
->_get(
"$1$state"
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
}
else
{
if
(
$rc
=
$self
->_get(
"$city$state"
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
}
}
elsif
(
my
$href
= Geo::StreetAddress::US->parse_address(
"$city, $state"
)) {
$state
=
$href
->{
'state'
};
if
(
length
(
$state
) > 2) {
if
(
my
$twoletterstate
= Locale::US->new()->{state2code}{
uc
(
$state
)}) {
$state
=
$twoletterstate
;
}
}
if
(
$href
->{city}) {
$city
=
uc
(
$href
->{city});
}
my
$fullstreet
=
$href
->{
'street'
};
if
(
$street
=
$fullstreet
) {
$fullstreet
.=
' '
.
$href
->{
'type'
};
if
(
my
$type
= Geo::Coder::Free::_abbreviate(
$href
->{
'type'
})) {
$street
.=
" $type"
;
}
if
(
$href
->{suffix}) {
$street
.=
' '
.
$href
->{suffix};
$fullstreet
.=
' '
.
$href
->{suffix};
}
}
if
(
$street
) {
if
(
my
$prefix
=
$href
->{prefix}) {
$street
=
"$prefix $street"
;
$fullstreet
=
"$prefix $fullstreet"
;
}
if
(
$href
->{
'number'
}) {
if
(
$rc
=
$self
->_get(
$href
->{
'number'
},
"$street$city$state"
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
if
(
$rc
=
$self
->_get(
$href
->{
'number'
},
"$fullstreet$city$state"
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
}
if
(
$rc
=
$self
->_get(
"$street$city$state"
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
if
(
$rc
=
$self
->_get(
"$fullstreet$city$state"
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
}
warn
"Fast lookup of US location '$location' failed"
;
}
else
{
if
(
$city
=~ /^(\d.+),\s*([\w\s]+),\s*([\w\s]+)/) {
my
$lookup
=
"$1, $2, $state"
;
if
(
my
$href
= (Geo::StreetAddress::US->parse_address(
$lookup
) || Geo::StreetAddress::US->parse_location(
$lookup
))) {
$county
= $3;
$county
=~ s/\s
*county
$//i;
if
(
$href
->{
'state'
}) {
$state
=
$href
->{
'state'
};
}
else
{
Carp::croak(__PACKAGE__,
": Geo::StreetAddress::US couldn't find the state in '$lookup'"
);
}
if
(
length
(
$state
) > 2) {
if
(
my
$twoletterstate
= Locale::US->new()->{state2code}{
uc
(
$state
)}) {
$state
=
$twoletterstate
;
}
}
my
%args
= (
county
=>
uc
(
$county
),
state
=>
$state
,
country
=>
'US'
);
if
(
$href
->{city}) {
$city
=
$args
{city} =
uc
(
$href
->{city});
}
if
(
$href
->{number}) {
$args
{number} =
$href
->{number};
}
if
(
$street
=
$href
->{street}) {
if
(
my
$type
= Geo::Coder::Free::_abbreviate(
$href
->{
'type'
})) {
$street
.=
" $type"
;
}
if
(
$href
->{suffix}) {
$street
.=
' '
.
$href
->{suffix};
}
if
(
my
$prefix
=
$href
->{prefix}) {
$street
=
"$prefix $street"
;
}
$args
{street} =
uc
(
$street
);
if
(
$href
->{
'number'
}) {
if
(
$county
) {
if
(
$rc
=
$self
->_get(
$href
->{
'number'
},
"$street$city$county$state"
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
}
if
(
$rc
=
$self
->_get(
$href
->{
'number'
},
"$street$city$state"
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
if
(
$county
) {
if
(
$rc
=
$self
->_get(
"$street$city$county$state"
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
}
if
(
$rc
=
$self
->_get(
"$street$city$state"
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
}
}
return
;
}
die
$city
;
}
elsif
(
$city
=~ /^(\w[\w\s]+),\s*([\w\s]+)/) {
my
$first
=
uc
($1);
my
$second
=
uc
($2);
if
(
$second
=~ /(\d+)\s+(.+)/) {
$second
=
"$1$2"
;
}
if
(
$rc
=
$self
->_get(
"$first$second$state"
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
$second
=~ s/\s+COUNTY$//;
if
(
$rc
=
$self
->_get(
"$first$second$state"
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
if
(
$rc
=
$self
->_get(
"$first$state"
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
my
$copy
=
uc
(
$location
);
$copy
=~ s/,\s+//g;
$copy
=~ s/\s
*USA
$//;
if
(
$rc
=
$self
->_get(
$copy
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
if
(
$copy
=~ s/(\d+)\s+/$1/) {
if
(
$rc
=
$self
->_get(
$copy
,
'US'
)) {
$rc
->{
'country'
} =
'US'
;
return
$rc
;
}
}
}
}
}
elsif
(
$c
eq
'ca'
) {
if
(
length
(
$state
) > 2) {
if
(
my
$twoletterstate
= Locale::CA->new()->{province2code}{
uc
(
$state
)}) {
$state
=
$twoletterstate
;
}
}
my
$rc
;
if
(
$city
!~ /,/) {
$city
=
uc
(
$city
);
if
(
$rc
=
$self
->_get(
"$city$state"
,
'CA'
)) {
$rc
->{
'country'
} =
'CA'
;
return
$rc
;
}
}
elsif
(
my
$href
= 0) {
$state
=
$href
->{
'province'
};
if
(
length
(
$state
) > 2) {
if
(
my
$twoletterstate
= Locale::CA->new()->{province2code}{
uc
(
$state
)}) {
$state
=
$twoletterstate
;
}
}
my
%args
= (
state
=>
$state
,
country
=>
'CA'
);
if
(
$href
->{city}) {
$args
{city} =
uc
(
$href
->{city});
}
if
(
$href
->{number}) {
$args
{number} =
$href
->{number};
}
if
(
$street
=
$href
->{street}) {
if
(
my
$type
= Geo::Coder::Free::_abbreviate(
$href
->{
'type'
})) {
$street
.=
" $type"
;
}
if
(
$href
->{suffix}) {
$street
.=
' '
.
$href
->{suffix};
}
}
if
(
$street
) {
if
(
my
$prefix
=
$href
->{prefix}) {
$street
=
"$prefix $street"
;
}
$args
{street} =
uc
(
$street
);
}
warn
"Fast lookup of Canadian location' $location' failed"
;
}
else
{
if
(
$city
=~ /^(\w[\w\s]+),\s*([\w\s]+)/) {
my
$first
=
uc
($1);
my
$second
=
uc
($2);
if
(
$rc
=
$self
->_get(
"$first$second$state"
,
'CA'
)) {
$rc
->{
'country'
} =
'CA'
;
return
$rc
;
}
$second
=~ s/\s+COUNTY$//;
if
(
$rc
=
$self
->_get(
"$first$second$state"
,
'CA'
)) {
$rc
->{
'country'
} =
'CA'
;
return
$rc
;
}
if
(
$rc
=
$self
->_get(
"$first$state"
,
'CA'
)) {
$rc
->{
'country'
} =
'CA'
;
return
$rc
;
}
my
$copy
=
uc
(
$location
);
$copy
=~ s/,\s+//g;
$copy
=~ s/\s
*Canada
$//i;
if
(
$rc
=
$self
->_get(
$copy
,
'CA'
)) {
$rc
->{
'country'
} =
'CA'
;
return
$rc
;
}
if
(
$copy
=~ s/(\d+)\s+/$1/) {
if
(
$rc
=
$self
->_get(
$copy
,
'CA'
)) {
$rc
->{
'country'
} =
'CA'
;
return
$rc
;
}
}
}
}
}
else
{
if
((
$c
eq
'au'
) && (
length
(
$state
) > 3)) {
if
(
my
$abbrev
= Locale::SubCountry->new(
'AU'
)->code(
ucfirst
(
lc
(
$state
)))) {
if
(
$abbrev
ne
'unknown'
) {
$state
=
$abbrev
;
}
}
}
if
(
$city
=~ /^(\w[\w\s]+),\s*([,\w\s]+)/) {
my
$street
=
uc
($1);
$city
=
uc
($2);
my
$number
;
if
(
$street
=~ /^(\d+)\s+(.+)/) {
$number
= $1;
$street
= $2;
}
if
(
$city
eq
'MINSTER, THANET'
) {
$city
=
'RAMSGATE'
;
}
$street
= Geo::Coder::Free::_normalize(
$street
);
if
(
$number
) {
if
(
my
$rc
=
$self
->_get(
"$number$street$city$state$c"
)) {
return
$rc
;
}
}
if
(
my
$rc
=
$self
->_get(
"$street$city$state$c"
)) {
return
$rc
;
}
}
if
(
my
$rc
=
$self
->_get(
"$city$state$c"
)) {
return
$rc
;
}
}
}
}
elsif
(
$location
=~ /([a-z\s]+),?\s*(United States|USA|US|Canada)$/i) {
$state
= $1;
$country
= $2;
if
(
$country
=~ /Canada/i) {
$country
=
'CA'
;
if
(
length
(
$state
) > 2) {
if
(
my
$twoletterstate
= Locale::CA->new()->{province2code}{
uc
(
$state
)}) {
$state
=
$twoletterstate
;
}
}
}
else
{
$country
=
'US'
;
if
(
length
(
$state
) > 2) {
if
(
my
$twoletterstate
= Locale::US->new()->{state2code}{
uc
(
$state
)}) {
$state
=
$twoletterstate
;
}
}
}
if
(
my
$rc
=
$self
->_get(
"$state$country"
)) {
$rc
->{
'country'
} =
$country
;
return
$rc
;
}
}
if
(
$libpostal_is_installed
== LIBPOSTAL_UNKNOWN) {
Geo::libpostal->
import
();
$libpostal_is_installed
= LIBPOSTAL_INSTALLED;
}
else
{
$libpostal_is_installed
= LIBPOSTAL_NOT_INSTALLED;
}
}
if
((
$libpostal_is_installed
== LIBPOSTAL_INSTALLED) && (
my
%addr
= Geo::libpostal::parse_address(
$location
))) {
if
(
$addr
{
'country'
} &&
$addr
{
'state'
} && (
$addr
{
'country'
} =~ /^(Canada|United States|USA|US)$/i)) {
if
(
$street
=
$addr
{
'road'
}) {
$street
= Geo::Coder::Free::_normalize(
$street
);
$addr
{
'road'
} =
$street
;
}
if
(
$addr
{
'country'
} =~ /Canada/i) {
$addr
{
'country'
} =
'Canada'
;
if
(
length
(
$addr
{
'state'
}) > 2) {
if
(
my
$twoletterstate
= Locale::CA->new()->{province2code}{
uc
(
$addr
{
'state'
})}) {
$addr
{
'state'
} =
$twoletterstate
;
}
}
}
else
{
$addr
{
'country'
} =
'US'
;
if
(
length
(
$addr
{
'state'
}) > 2) {
if
(
my
$twoletterstate
= Locale::US->new()->{state2code}{
uc
(
$addr
{
'state'
})}) {
$addr
{
'state'
} =
$twoletterstate
;
}
}
}
if
(
$addr
{
'state_district'
}) {
$addr
{
'state_district'
} =~ s/^(.+)\s+COUNTY/$1/i;
if
(
my
$rc
=
$self
->_search(\
%addr
, (
'house_number'
,
'road'
,
'city'
,
'state_district'
,
'state'
,
'country'
))) {
return
$rc
;
}
}
if
(
my
$rc
=
$self
->_search(\
%addr
, (
'house_number'
,
'road'
,
'city'
,
'state'
,
'country'
))) {
return
$rc
;
}
if
(
$addr
{
'house_number'
}) {
if
(
my
$rc
=
$self
->_search(\
%addr
, (
'road'
,
'city'
,
'state'
,
'country'
))) {
return
$rc
;
}
}
}
}
if
(
$location
=~ s/,//g) {
return
$self
->geocode(
$location
);
}
undef
;
}
sub
_search {
my
(
$self
,
$data
,
@columns
) =
@_
;
my
$location
;
foreach
my
$column
(
@columns
) {
if
(
$data
->{
$column
}) {
$location
.=
$data
->{
$column
};
}
}
if
(
$location
) {
return
$self
->_get(
$location
);
}
}
sub
_get {
my
(
$self
,
@location
) =
@_
;
my
$location
=
join
(
''
,
@location
);
$location
=~ s/^\s+//;
$location
=~ s/,\s*//g;
my
$digest
=
substr
Digest::MD5::md5_base64(
uc
(
$location
)), 0, 16;
if
(
defined
(
$unknown_locations
{
$digest
})) {
return
;
}
if
(
my
$cache
=
$self
->{
'cache'
}) {
if
(
my
$rc
=
$cache
->get_object(
$digest
)) {
return
Storable::thaw(
$rc
->value());
}
}
my
$openaddr_db
=
$self
->{openaddr_db} ||
Geo::Coder::Free::DB::openaddresses->new(
directory
=>
$self
->{openaddr},
cache
=>
$self
->{cache} || CHI->new(
driver
=>
'Memory'
,
datastore
=> {})
);
$self
->{openaddr_db} =
$openaddr_db
;
my
$rc
=
$openaddr_db
->fetchrow_hashref(
md5
=>
$digest
);
if
(
$rc
&&
defined
(
$rc
->{
'lat'
})) {
$rc
->{
'latitude'
} =
delete
$rc
->{
'lat'
};
$rc
->{
'longitude'
} =
delete
$rc
->{
'lon'
};
}
if
(
$rc
&&
defined
(
$rc
->{
'latitude'
})) {
$rc
= Geo::Location::Point->new({
'lat'
=>
$rc
->{
'latitude'
},
'long'
=>
$rc
->{
'longitude'
},
'location'
=>
$location
,
'database'
=>
'OpenAddresses'
});
if
(
my
$cache
=
$self
->{
'cache'
}) {
$cache
->set(
$digest
, Storable::freeze(
$rc
),
'1 week'
);
}
return
$rc
;
}
$unknown_locations
{
$digest
} = 1;
return
;
}
sub
reverse_geocode {
Carp::croak(__PACKAGE__,
': Reverse lookup is not yet supported'
);
}
sub
ua {
}
1;