NAME
Geo::Coder::Free::OpenAddresses - Provides a geocoding functionality to a local SQLite database containing geo-coding data.
VERSION
Version 0.51
SYNOPSIS
use Geo::Coder::Free::OpenAddresses;
# Use a local download of http://results.openaddresses.io/
my $geocoder;
if(my $openaddr = $ENV{'OPENADDR_HOME'}) {
$geocoder = Geo::Coder::Free::OpenAddresses->new(openaddr => $openaddr);
} else {
$geocoder = Geo::Coder::Free::OpenAddresses->new(openaddr => '/usr/share/geo-coder-free/data');
}
my $location = $geocoder->geocode(location => '1600 Pennsylvania Avenue NW, Washington DC, USA');
my @matches = $geocoder->geocode({ scantext => 'arbitrary text', region => 'GB' });
DESCRIPTION
Geo::Coder::Free::OpenAddresses provides an interface to the free geolocation databases at http://results.openaddresses.io, https://github.com/whosonfirst-data, https://github.com/dr5hn/countries-states-cities-database.git and https://download.geofabrik.de/europe-latest.osm.bz2. The SQLite database is in a file held in $OPENADDR_HOME/openaddresses.sql.
Refer to the source URL for licencing information for these files.
To install, run the createdatabases.PL script which imports the data into an SQLite database. This process will take some time.
Optional modules
Two modules are used to parse an address when they are installed, and the lookup goes on without them when they are not:
Geo::Address::Parser splits a US address into its fields before the database is searched for them.
Geo::libpostal is tried last, for US and Canadian addresses the other steps could not parse. The first call to it in a process loads libpostal's model, which is several gigabytes, so that call can take several seconds and the process then holds that memory. Pass
libpostal => 0tonew()to leave it unused, for example in a short-lived or memory-constrained process, or in a test with a time limit.
METHODS
new
$geocoder = Geo::Coder::Free::OpenAddresses->new(openaddr => $ENV{'OPENADDR_HOME'});
Takes an optional parameter "openaddr", which is the directory of the file openaddresses.sql.
Takes an optional parameter cache, which points to an object that understands get() and set() messages to store data in
Takes an optional parameter libpostal. When it is false, Geo::libpostal is never loaded or called, even if it is installed; see "Optional modules". It defaults to true.
API SPECIFICATION
input
# Input schema (Params::Validate::Strict)
openaddr => { type => 'scalar' } # path to the directory containing openaddresses.sql
cache => { type => 'object', optional => 1, can => ['get', 'set'] } # CHI-compatible cache object
libpostal => { type => 'boolean', optional => 1, default => 1 } # false: never use Geo::libpostal
output
# Output schema (Return::Set)
{ type => 'object', isa => 'Geo::Coder::Free::OpenAddresses' }
reads
my $count = $geocoder->reads();
How many times this backend has read its database since the object was created. A lookup answered from the result cache or from the shared miss cache does not increase it, so geocode_many() can take the count before and after a value to tell a lookup that cost a read from one that was answered from a cache.
API SPECIFICATION
input
# Input schema (Params::Validate::Strict)
# none
output
# Output schema (Return::Set)
{ type => 'integer', min => 0 } # 0 for a new object
geocode
$location = $geocoder->geocode(location => $location);
print 'Latitude: ', $location->lat(), "\n";
print 'Longitude: ', $location->long(), "\n";
# TODO:
# @locations = $geocoder->geocode('Portland, USA');
# diag 'There are Portlands in ', join (', ', map { $_->{'state'} } @locations);
@locations = $geo_coder->geocode(scantext => 'arbitrary text', region => 'US', ignore_words => [ 'foo', 'bar' ]);
When looking for a house number in a street, if that address isn't found but that street is found, a place in the street is given. So "106 Wells Street, Fort Wayne, Allen, Indiana, USA" isn't found, a match for "Wells Street, Fort Wayne, Allen, Indiana, USA" will be given instead. Arguably that's incorrect, but it is the behaviour I want. If "exact" is not given, it will go on to look just for the town if the street isn't found.
The word "county" is removed from US county searches, that either Leesburg, Loudoun County, Virginia, US or Leesburg, Loudoun, Virginia, US will work.
API SPECIFICATION
input
# Input schema (Params::Validate::Strict) - exactly one of location or scantext is required
location => { type => 'scalar', optional => 1 } # address string (exclusive with scantext)
scantext => { type => 'scalar', optional => 1 } # free text to scan for place names
region => { type => 'scalar', optional => 1 } # ISO 3166-1 alpha-2 country code hint
ignore_words => { type => 'arrayref', optional => 1 } # words to suppress during scantext scan
exact => { type => 'scalar', optional => 1 } # if set, do not fall back to town-only match
output
# Output schema (Return::Set)
# scalar context: { type => 'object', isa => 'Geo::Location::Point', optional => 1 }
# list context: { type => 'arrayref', of => { isa => 'Geo::Location::Point' } }
provenance
Every result carries geocoder, database, confidence and accuracy (section 2.1 of BI-Phase6-Dependency-Specs.md). This backend reports database as OpenAddresses, and accuracy is address when the database answered for the address itself and locality when a hand-curated %known_locations entry did, since that entry's coordinate is a town's.
reverse_geocode
$location = $geocoder->reverse_geocode(latlng => '37.778907,-122.39732');
To be done. The database holds a hash of each address rather than the address itself, so a coordinate cannot be turned back into one. It always croaks Reverse lookup is not yet supported, whatever it is given.
API SPECIFICATION
input
# Input schema (Params::Validate::Strict)
# any arguments are accepted and ignored
output
# Output schema (Return::Set)
# none: always croaks
ua
Does nothing, here for compatibility with other geocoders
API SPECIFICATION
input
# Input schema (Params::Validate::Strict)
# any arguments are accepted and ignored
output
# Output schema (Return::Set)
# nothing: an empty list, or undef in scalar context
AUTHOR
Nigel Horne <njh@bandsman.co.uk>
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
The contents of lib/Geo/Coder/Free/OpenAddresses/databases comes from the places listed in the synopsis.
BUGS
Lots of lookups fail at the moment.
There are die()s where the code path has yet to be written.
The openaddresses data doesn't cover the globe.
Can't parse and handle "London, England".
Currently only searches US and Canadian data.
If you do search in the UK, only look up towns, full addresses aren't included. So these will print the same.
use Geo::Coder::Free::OpenAddresses;
$location = $geo_coder->geocode(location => '22 Central Road, Ramsgate, Kent, England');
print $location->{latitude}, "\n";
print $location->{longitude}, "\n";
$location = $geo_coder->geocode(location => '7 Hillbrow Road, St Lawrence, Thanet, Kent, England');
print $location->{latitude}, "\n";
print $location->{longitude}, "\n";
When I added the WhosOnFirst data I should have renamed this as it contains data from both sources.
The database shouldn't be called $OPENADDR_HOME/openaddresses.sql, since the database now also includes data from WhosOnFirst.
The name openaddresses.sql shouldn't be hardcoded, add support to "new" for the parameter "dbname".
The argument "openaddr", would be less confusing if it were called "directory",
SEE ALSO
VWF, openaddresses.
LICENSE AND COPYRIGHT
Copyright 2017-2025 Nigel Horne.
The program code is released under the following licence: GPL for personal use on a single computer. All other users (including Commercial, Charity, Educational, Government) must apply in writing for a licence for use from Nigel Horne at `<njh at nigelhorne.com>`.