aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/FixMyStreet/Geocode/Google.pm
blob: ad188154183d5e99bf0193b5341f3f1351fadf31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# FixMyStreet::Geocode::Google
# The geocoding functions for FixMyStreet.
#
# Copyright (c) 2010 UK Citizens Online Democracy. All rights reserved.
# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/

package FixMyStreet::Geocode::Google;

use strict;
use Utils;
use URI::Escape;

# string STRING CONTEXT
# 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.
sub string {
    my ( $s, $c ) = @_;

    my $params = $c->cobrand->disambiguate_location($s);
    # Allow cobrand to fixup the user input
    $s = $params->{string} if $params->{string};

    my $components = "";

    # For some reason adding gl=uk is no longer sufficient to make google
    # think we are in the UK for some locations so we explicitly tell Google
    # the country.
    if ($c->cobrand->country eq 'GB') {
        $components = "country:GB";
    }

    $s = FixMyStreet::Geocode::escape($s);

    my $url = 'https://maps.googleapis.com/maps/api/geocode/json?address=' . $s;
    $url .= '&bounds=' . $params->{bounds}[0] . ',' . $params->{bounds}[1]
        . '|' . $params->{bounds}[2] . ',' . $params->{bounds}[3]
        if $params->{bounds};
    if ($params->{google_country}) {
        $url .=  '&region=' . $params->{google_country};
    } elsif ($params->{country}) {
        $url .=  '&region=' . $params->{country};
    }
    if ($params->{components}) {
        $components .= ($components ? '|' : '') . URI::Escape::uri_escape_utf8($params->{components});
    }
    $url .=  '&language=' . $params->{lang} if $params->{lang};

    $url .= '&components=' . $components if $components;

    my $args = 'key=' . FixMyStreet->config('GOOGLE_MAPS_API_KEY');
    my $js = FixMyStreet::Geocode::cache('google', $url, $args, qr/"status"\s*:\s*"(OVER_QUERY_LIMIT|REQUEST_DENIED|INVALID_REQUEST|UNKNOWN_ERROR)"/);
    if (!$js) {
        return { error => _('Sorry, we could not parse that location. Please try again.') };
    }
    return unless $js->{status} eq 'OK';

    my $results = $js->{results};
    my ( $error, @valid_locations, $latitude, $longitude );
    foreach (@$results) {
        my $address = $_->{formatted_address};
        next unless $c->cobrand->geocoded_string_check( $address );
        ( $longitude, $latitude ) =
            map { Utils::truncate_coordinate($_) }
            ($_->{geometry}{location}{lng}, $_->{geometry}{location}{lat});
        push (@$error, {
            address => $address,
            latitude => $latitude,
            longitude => $longitude
        });
        push (@valid_locations, $_);
    }
    return { latitude => $latitude, longitude => $longitude } if scalar @valid_locations == 1;
    return { error => $error };
}

1;