aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/FixMyStreet/Geocode.pm
diff options
context:
space:
mode:
Diffstat (limited to 'perllib/FixMyStreet/Geocode.pm')
-rw-r--r--perllib/FixMyStreet/Geocode.pm172
1 files changed, 20 insertions, 152 deletions
diff --git a/perllib/FixMyStreet/Geocode.pm b/perllib/FixMyStreet/Geocode.pm
index c06c3bb55..4ae3df368 100644
--- a/perllib/FixMyStreet/Geocode.pm
+++ b/perllib/FixMyStreet/Geocode.pm
@@ -9,172 +9,40 @@
package FixMyStreet::Geocode;
use strict;
-use Encode;
-use Error qw(:try);
-use File::Slurp;
-use File::Path ();
-use LWP::Simple;
-use Digest::MD5 qw(md5_hex);
use URI::Escape;
+use FixMyStreet::Geocode::Bing;
+use FixMyStreet::Geocode::Google;
-use Cobrand;
-use Page;
-use Utils;
-use mySociety::Config;
-use mySociety::Locale;
-use mySociety::MaPit;
-use mySociety::PostcodeUtil;
-use mySociety::Web qw(NewURL);
-
-BEGIN {
- (my $dir = __FILE__) =~ s{/[^/]*?$}{};
- mySociety::Config::set_file("$dir/../../conf/general");
-}
-
-# lookup STRING QUERY
+# lookup STRING CONTEXT
# Given a user-inputted string, try and convert it into co-ordinates using either
-# MaPit if it's a postcode, or Google Maps API otherwise. Returns an array of
-# data, including an error if there is one (which includes a location being in
+# MaPit if it's a postcode, or some web API otherwise. Returns an array of
+# data, including an error if there is one (which includes a location being in
# Northern Ireland). The information in the query may be used by cobranded versions
# of the site to diambiguate locations.
sub lookup {
- my ($s, $q) = @_;
- my ($latitude, $longitude, $error);
- if (mySociety::Config::get('COUNTRY') eq 'GB') {
- if ($s =~ /^\d+$/) {
- $error = 'FixMyStreet is a UK-based website that currently works in England, Scotland, and Wales. Please enter either a postcode, or a Great British street name and area.';
- } elsif (mySociety::PostcodeUtil::is_valid_postcode($s)) {
- my $location = mySociety::MaPit::call('postcode', $s);
- unless ($error = Page::mapit_check_error($location)) {
- $latitude = $location->{wgs84_lat};
- $longitude = $location->{wgs84_lon};
- }
- }
- } elsif (mySociety::Config::get('COUNTRY') eq 'NO') {
- if ($s =~ /^\d{4}$/) {
- my $location = mySociety::MaPit::call('postcode', $s);
- unless ($error = Page::mapit_check_error($location)) {
- $latitude = $location->{wgs84_lat};
- $longitude = $location->{wgs84_lon};
- }
- }
- }
- unless ($error || defined $latitude) {
- ($latitude, $longitude, $error) = FixMyStreet::Geocode::string($s, $q);
- }
- return ($latitude, $longitude, $error);
-}
-
-sub geocoded_string_coordinates {
- my ($js, $q) = @_;
- my ($latitude, $longitude, $error);
- my ($accuracy) = $js =~ /"Accuracy" *: *(\d)/;
- if ($accuracy < 4) {
- $error = _('Sorry, that location appears to be too general; please be more specific.');
- } elsif ( $js =~ /"coordinates" *: *\[ *(.*?), *(.*?),/ ) {
- $longitude = $1;
- $latitude = $2;
- if (mySociety::Config::get('COUNTRY') eq 'GB') {
- try {
- my ($easting, $northing) = Utils::convert_latlon_to_en( $latitude, $longitude );
- } catch Error::Simple with {
- mySociety::Locale::pop(); # We threw exception, so it won't have happened.
- $error = shift;
- $error = _('That location does not appear to be in Britain; please try again.')
- if $error =~ /out of the area covered/;
- }
- }
- }
- return ($latitude, $longitude, $error);
+ my ($s, $c) = @_;
+ my $data = $c->cobrand->geocode_postcode($s);
+ $data = string($s, $c)
+ unless $data->{error} || defined $data->{latitude};
+ $data->{error} = _('Sorry, we could not find that location.')
+ unless $data->{error} || defined $data->{latitude};
+ return ( $data->{latitude}, $data->{longitude}, $data->{error} );
}
-sub results_check {
- my $q = shift;
- my ($error, @valid_locations);
- foreach (@_) {
- next unless /"address" *: *"(.*?)"/s;
- my $address = $1;
- next unless Cobrand::geocoded_string_check(Page::get_cobrand($q), $address, $q);
- next if $address =~ /BT\d/;
- push (@$error, $address);
- push (@valid_locations, $_);
- }
- if (scalar @valid_locations == 1) {
- return geocoded_string_coordinates($valid_locations[0], $q);
- }
- $error = _('Sorry, we could not find that location.') unless $error;
- return (undef, undef, $error);
-}
-
-# string STRING QUERY
-# Canonicalises, looks up on Google Maps API, and caches, a user-inputted location.
-# Returns array of (LAT, LON, ERROR), where ERROR is either undef, a string, or
-# an array of matches if there are more than one. The information in the query
-# may be used to disambiguate the location in cobranded versions of the site.
+# string STRING CONTEXT
+# Canonicalises, and then passes to some external API to look stuff up.
sub string {
- my ($s, $q) = @_;
+ my ($s, $c) = @_;
$s = lc($s);
$s =~ s/[^-&\w ']/ /g;
$s =~ s/\s+/ /g;
$s = URI::Escape::uri_escape_utf8($s);
- $s = Cobrand::disambiguate_location(Page::get_cobrand($q), "q=$s", $q);
$s =~ s/%20/+/g;
- my $url = 'http://maps.google.com/maps/geo?' . $s;
- my $cache_dir = mySociety::Config::get('GEO_CACHE');
- my $cache_file = $cache_dir . md5_hex($url);
- my ($js, $error);
- if (-s $cache_file) {
- $js = File::Slurp::read_file($cache_file);
- } else {
- $url .= ',+UK' unless $url =~ /united\++kingdom$/ || $url =~ /uk$/i
- || mySociety::Config::get('COUNTRY') ne 'GB';
- $url .= '&sensor=false&key=' . mySociety::Config::get('GOOGLE_MAPS_API_KEY');
- $js = LWP::Simple::get($url);
- $js = encode_utf8($js) if utf8::is_utf8($js);
- File::Path::mkpath($cache_dir);
- File::Slurp::write_file($cache_file, $js) if $js && $js !~ /"code":6[12]0/;
- }
- if (!$js) {
- $error = _('Sorry, we could not parse that location. Please try again.');
- } elsif ($js !~ /"code" *: *200/) {
- $error = _('Sorry, we could not find that location.');
- } elsif ($js =~ /}, *{/) { # Multiple
- return results_check($q, (split /}, *{/, $js));
- } elsif ($js =~ /BT\d/) {
- # Northern Ireland, hopefully
- $error = _("We do not cover Northern Ireland, I'm afraid, as our licence doesn't include any maps for the region.");
- } else {
- return results_check($q, $js);
- }
- return (undef, undef, $error);
-}
-
-# list_choices
-# Prints response if there's more than one possible result
-sub list_choices {
- my ($choices, $page, $q) = @_;
- my $url;
- my $cobrand = Page::get_cobrand($q);
- my $message = _('We found more than one match for that location. We show up to ten matches, please try a different search if yours is not here.');
- my $out = '<p>' . $message . '</p>';
- my $choice_list = '<ul>';
- foreach my $choice (@$choices) {
- $choice = decode_utf8($choice);
- $choice =~ s/, United Kingdom//;
- $choice =~ s/, UK//;
- $url = Cobrand::url($cobrand, NewURL($q, -retain => 1, -url => $page, 'pc' => $choice), $q);
- $url =~ s/%20/+/g;
- $choice_list .= '<li><a href="' . $url . '">' . $choice . "</a></li>\n";
- }
- $choice_list .= '</ul>';
- $out .= $choice_list;
- my %vars = (message => $message,
- choice_list => $choice_list,
- header => _('More than one match'),
- url_home => Cobrand::url($cobrand, '/', $q));
- my $cobrand_choice = Page::template_include('geocode-choice', $q, Page::template_root($q), %vars);
- return $cobrand_choice if $cobrand_choice;
- return $out;
+ my $params = $c->cobrand->disambiguate_location();
+ return FixMyStreet::Geocode::Bing::string($s, $c, $params)
+ if FixMyStreet->config('BING_MAPS_API_KEY');
+ return FixMyStreet::Geocode::Google::string($s, $c, $params)
+ if FixMyStreet->config('GOOGLE_MAPS_API_KEY');
}
1;