diff options
author | Matthew Somerville <matthew@mysociety.org> | 2011-07-12 19:09:45 +0100 |
---|---|---|
committer | Matthew Somerville <matthew@mysociety.org> | 2011-07-12 19:09:45 +0100 |
commit | d2d54ea0d9844833ab8544c3df34f9043b21a6a7 (patch) | |
tree | da892b606e08b798d0023683f18db4ca043ac779 | |
parent | 42a92773a123d2b0fa1eaed93201fa45a8b7e436 (diff) |
Factor out find_closest from send-reports, initial OSM.pm from osm_road_info branch, needs altering before it will work.
-rwxr-xr-x | bin/send-reports | 33 | ||||
-rw-r--r-- | perllib/FixMyStreet/Cobrand/Default.pm | 38 | ||||
-rw-r--r-- | perllib/FixMyStreet/Cobrand/FiksGataMi.pm | 5 | ||||
-rw-r--r-- | perllib/FixMyStreet/Geocode/OSM.pm | 143 |
4 files changed, 190 insertions, 29 deletions
diff --git a/bin/send-reports b/bin/send-reports index b55447e8a..1af3ba1ea 100755 --- a/bin/send-reports +++ b/bin/send-reports @@ -88,7 +88,10 @@ while (my $row = $unsent->next) { = "Easting: $h{easting}\n\n" # . "Northing: $h{northing}\n\n"; - $h{closest_address} = find_closest($row, $h{latitude}, $h{longitude}); + } + + if ( $row->used_map ) { + $h{closest_address} = $cobrand->find_closest( $h{latitude}, $h{longitude} ); } my (@to, @recips, $template, $areas_info); @@ -422,34 +425,6 @@ sub post_london_report { # Nearest things -sub find_closest { - my ($row, $latitude, $longitude) = @_; - my $str = ''; - - return '' unless $row->used_map; - - # Get nearest road-type thing from Bing - my $url = "http://dev.virtualearth.net/REST/v1/Locations/$latitude,$longitude?c=en-GB&key=" . mySociety::Config::get('BING_MAPS_API_KEY'); - my $j = LWP::Simple::get($url); - if ($j) { - $j = JSON->new->utf8->allow_nonref->decode($j); - if ($j->{resourceSets}[0]{resources}[0]{name}) { - $str .= "Nearest road to the pin placed on the map (automatically generated by Bing Maps): $j->{resourceSets}[0]{resources}[0]{name}\n\n"; - } - } - - # Get nearest postcode from Matthew's random gazetteer (put in MaPit? Or elsewhere?) - $url = "http://gazetteer.dracos.vm.bytemark.co.uk/point/$latitude,$longitude.json"; - $j = LWP::Simple::get($url); - if ($j) { - $j = JSON->new->utf8->allow_nonref->decode($j); - if ($j->{postcode}) { - $str .= "Nearest postcode to the pin placed on the map (automatically generated): $j->{postcode}[0] ($j->{postcode}[1]m away)\n\n"; - } - } - return $str; -} - sub london_lookup { my $org = shift; my $str = "Unknown ($org)"; diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm index 62c73761b..2ac1a81b8 100644 --- a/perllib/FixMyStreet/Cobrand/Default.pm +++ b/perllib/FixMyStreet/Cobrand/Default.pm @@ -537,6 +537,44 @@ string LOCATION passes the cobrands checks. sub geocoded_string_check { return 1; } +=head2 find_closest + +Used by send-reports to attach nearest things to the bottom of the report + +=cut + +sub find_closest { + my ( $self, $latitude, $longitude ) = @_; + my $str = ''; + + # 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?c=en-GB&key=$key"; + my $j = LWP::Simple::get($url); + if ($j) { + $j = JSON->new->utf8->allow_nonref->decode($j); + if ($j->{resourceSets}[0]{resources}[0]{name}) { + $str .= sprintf(_("Nearest road to the pin placed on the map (automatically generated by Bing Maps): %s"), + $j->{resourceSets}[0]{resources}[0]{name}) . "\n\n"; + } + } + } + + # Get nearest postcode from Matthew's random gazetteer (put in MaPit? Or elsewhere?) + my $url = "http://gazetteer.dracos.vm.bytemark.co.uk/point/$latitude,$longitude.json"; + my $j = LWP::Simple::get($url); + if ($j) { + $j = JSON->new->utf8->allow_nonref->decode($j); + if ($j->{postcode}) { + $str .= sprintf(_("Nearest postcode to the pin placed on the map (automatically generated): %s (%sm away)"), + $j->{postcode}[0], $j->{postcode}[1]) . "\n\n"; + } + } + + return $str; +} + =head2 council_check Paramters are COUNCILS, QUERY, CONTEXT. Return a boolean indicating whether diff --git a/perllib/FixMyStreet/Cobrand/FiksGataMi.pm b/perllib/FixMyStreet/Cobrand/FiksGataMi.pm index a606dc3b9..3ba449a33 100644 --- a/perllib/FixMyStreet/Cobrand/FiksGataMi.pm +++ b/perllib/FixMyStreet/Cobrand/FiksGataMi.pm @@ -87,6 +87,11 @@ sub geocoded_string_check { return 0; } +sub find_closest { + my ( $self, $latitude, $longitude ) = @_; + return FixMyStreet::Geocode::OSM::closest_road_text( $self, $latitude, $longitude ); +} + sub remove_redundant_councils { my $self = shift; my $all_councils = shift; diff --git a/perllib/FixMyStreet/Geocode/OSM.pm b/perllib/FixMyStreet/Geocode/OSM.pm new file mode 100644 index 000000000..fad5baf45 --- /dev/null +++ b/perllib/FixMyStreet/Geocode/OSM.pm @@ -0,0 +1,143 @@ +#!/usr/bin/perl +# +# FixMyStreet:Geocode::OSM +# OpenStreetmap forward and reverse geocoding for FixMyStreet. +# +# Copyright (c) 2011 Petter Reinholdtsen. Some rights reserved. +# Email: pere@hungry.com + +package FixMyStreet::Geocode::OSM; + +use warnings; +use strict; + +use Memcached; +use mySociety::Config; +use LWP::Simple; +use XML::Simple; + +my $osmapibase = "http://www.openstreetmap.org/api/"; +my $nominatimbase = "http://nominatim.openstreetmap.org/"; + + +sub lookup_location { + my ($latitude, $longitude, $zoom) = @_; + my $url = + "${nominatimbase}reverse?format=xml&zoom=$zoom&lat=$latitude&lon=$longitude"; + my $key = "OSM:lookup_location:$url"; + my $result = Memcached::get($key); + unless ($result) { + my $j = LWP::Simple::get($url); + if ($j) { + Memcached::set($key, $j, 3600); + my $ref = XMLin($j); + return $ref; + } else { + print STDERR "No reply from $url\n"; + } + return undef; + } + return XMLin($result); +} + +sub _osmxml_to_hash { + my ($xml, $type) = @_; + my $ref = XMLin($xml); + my %tags; + if ('ARRAY' eq ref $ref->{$type}->{tag}) { + map { $tags{$_->{'k'}} = $_->{'v'} } @{$ref->{$type}->{tag}}; + return \%tags; + } else { + return undef; + } +} + +sub get_object_tags { + my ($type, $id) = @_; + my $url = "${osmapibase}0.6/$type/$id"; + my $key = "OSM:get_object_tags:$url"; + my $result = Memcached::get($key); + unless ($result) { + my $j = LWP::Simple::get($url); + if ($j) { + Memcached::set($key, $j, 3600); + return _osmxml_to_hash($j, $type); + } else { + print STDERR "No reply from $url\n"; + } + return undef; + } + return _osmxml_to_hash($result, $type); +} + +sub guess_road_operator { + my $inforef = shift; + my $highway = $inforef->{highway} || "unknown"; + my $refs = $inforef->{ref} || "unknown"; + + my $operator; + if ( mySociety::Config::get('COUNTRY') eq 'NO' ) { + if ($highway eq "trunk" + || $highway eq "primary" + ) { + $operator = "Statens vegvesen"; + } + unless (defined $operator) { + for my $ref (split(/;/, $refs)) { + if ($ref =~ m/E ?\d+/ + || $ref =~ m/Fv\d+/i + ) { + $operator = "Statens vegvesen"; + } + } + } + } + return $operator; +} + +# A better alternative might be +# http://www.geonames.org/maps/osm-reverse-geocoder.html#findNearbyStreetsOSM +sub get_nearest_road_tags { + my ($latitude, $longitude) = @_; + my $inforef = lookup_location($latitude, $longitude, 16); + if (exists $inforef->{result}->{osm_type} + && 'way' eq $inforef->{result}->{osm_type}) { + my $osmtags = get_object_tags('way', + $inforef->{result}->{osm_id}); + if (mySociety::Config::get('OSM_GUESS_OPERATOR') + && !exists $osmtags->{operator}) { + my $guess = guess_road_operator($osmtags); + $osmtags->{operatorguess} = $guess if $guess; + } + return $osmtags; + } + return undef; +} + +sub closest_road_text { + my ($latitude, $longitude) = @_; + my $str = ''; + my $osmtags = get_nearest_road_tags($latitude, $longitude); + if ($osmtags) { + my ($name, $ref) = ('',''); + $name = $osmtags->{name} if exists $osmtags->{name}; + $ref = " ($osmtags->{ref})" if exists $osmtags->{ref}; + if ($name || $ref) { + $str .= _('The following information about the nearest road might be inaccurate or irrelevant, if the problem is close to several roads or close to a road without a name registered in OpenStreetmap.') . "\n\n"; + $str .= sprintf(_("Nearest named road to the pin placed on the map (automatically generated using OpenStreetmap): %s%s"), + $name, $ref) . "\n\n"; + + if (my $operator = $osmtags->{operator}) { + $str .= sprintf(_("Road operator for this named road (from OpenStreetmap): %s"), + $operator) . "\n\n"; + } elsif ($operator = $osmtags->{operatorguess}) { + $str .= sprintf(_("Road operator for this named road (derived from road reference number and type): %s"), + $operator) . "\n\n"; + } + } + } + return $str; +} + +1; + |