aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/FixMyStreet/Geocode/Bing.pm
blob: f00cf967184b26ac496de22bf3b678da55967db9 (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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#!/usr/bin/perl
#
# FixMyStreet::Geocode::Bing
# Geocoding with Bing for FixMyStreet.
#
# Copyright (c) 2011 UK Citizens Online Democracy. All rights reserved.
# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/

package FixMyStreet::Geocode::Bing;

use strict;
use Encode;
use File::Slurp;
use File::Path ();
use LWP::Simple;
use Digest::MD5 qw(md5_hex);

# string STRING CONTEXT
# Looks up on Bing 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, $params ) = @_;
    $s .= '+' . $params->{town} if $params->{town} and $s !~ /$params->{town}/i;
    my $url = "http://dev.virtualearth.net/REST/v1/Locations?q=$s";
    $url .= '&userMapView=' . $params->{bounds}[0] . ',' . $params->{bounds}[1]
        if $params->{bounds};
    $url .= '&userLocation=' . $params->{centre} if $params->{centre};
    $url .= '&c=' . $params->{bing_culture} if $params->{bing_culture};

    my $cache_dir = FixMyStreet->config('GEO_CACHE') . 'bing/';
    my $cache_file = $cache_dir . md5_hex($url);
    my $js;
    if (-s $cache_file) {
        $js = File::Slurp::read_file($cache_file);
    } else {
        $url .= '&key=' . FixMyStreet->config('BING_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;
    }

    if (!$js) {
        return { error => _('Sorry, we could not parse that location. Please try again.') };
    } elsif ($js =~ /BT\d/ && $params->{bing_country} eq 'United Kingdom') {
        return { error => _("We do not currently cover Northern Ireland, I'm afraid.") };
    }

    $js = JSON->new->utf8->allow_nonref->decode($js);
    if ($js->{statusCode} ne '200') {
        return { error => _('Sorry, we could not find that location.') };
    }

    my $results = $js->{resourceSets}->[0]->{resources};
    my ( $error, @valid_locations, $latitude, $longitude );

    foreach (@$results) {
        my $address = $_->{name};
        next unless $_->{address}->{countryRegion} eq $params->{bing_country};
        ( $latitude, $longitude ) = @{ $_->{point}->{coordinates} };
        push (@$error, { address => $address, latitude => $latitude, longitude => $longitude });
        push (@valid_locations, $_);
    }
    return { latitude => $latitude, longitude => $longitude } if scalar @valid_locations == 1;
    return { error => $error };
}

sub reverse {
    my ( $latitude, $longitude, $bing_culture, $cache ) = @_;

    # Get nearest road-type thing from Bing
    my $key = mySociety::Config::get('BING_MAPS_API_KEY', '');
    if ($key) {
        my $url = "http://dev.virtualearth.net/REST/v1/Locations/$latitude,$longitude?key=$key";
        $url .= '&c=' . $bing_culture if $bing_culture;
        my $j;
        if ( $cache ) {
            my $cache_dir = FixMyStreet->config('GEO_CACHE') . 'bing/';
            my $cache_file = $cache_dir . md5_hex($url);

            if (-s $cache_file) {
                $j = File::Slurp::read_file($cache_file);
            } else {
                $j = LWP::Simple::get($url);
                File::Path::mkpath($cache_dir);
                File::Slurp::write_file($cache_file, $j) if $j;
            }
        } else {
            $j = LWP::Simple::get($url);
        }

        if ($j) {
            $j = JSON->new->utf8->allow_nonref->decode($j);
            return $j;
        }
    }

    return undef;
}

1;