aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Kristensen <info@jonkri.com>2014-01-25 18:37:58 +0100
committerMatthew Somerville <matthew-github@dracos.co.uk>2014-03-11 16:54:08 +0000
commit331864878a549028ceff77eb45b30cda952f26fb (patch)
treee37fc13aa289ba678d811c03b5894d20d7977f39
parent12cbe993e2ec44fdf6e770ec70ce92f9d20e76a6 (diff)
Move Swedish changes from Geocode/OSM.pm to Geocode/FixaMinGata.pm
-rw-r--r--perllib/FixMyStreet/Cobrand/FixaMinGata.pm2
-rw-r--r--perllib/FixMyStreet/Geocode/FixaMinGata.pm204
-rw-r--r--perllib/FixMyStreet/Geocode/OSM.pm34
3 files changed, 214 insertions, 26 deletions
diff --git a/perllib/FixMyStreet/Cobrand/FixaMinGata.pm b/perllib/FixMyStreet/Cobrand/FixaMinGata.pm
index 57cdb22f7..f17f7716f 100644
--- a/perllib/FixMyStreet/Cobrand/FixaMinGata.pm
+++ b/perllib/FixMyStreet/Cobrand/FixaMinGata.pm
@@ -6,7 +6,7 @@ use warnings;
use Carp;
use mySociety::MaPit;
-use FixMyStreet::Geocode::OSM;
+use FixMyStreet::Geocode::FixaMinGata;
use DateTime;
diff --git a/perllib/FixMyStreet/Geocode/FixaMinGata.pm b/perllib/FixMyStreet/Geocode/FixaMinGata.pm
new file mode 100644
index 000000000..2db25f504
--- /dev/null
+++ b/perllib/FixMyStreet/Geocode/FixaMinGata.pm
@@ -0,0 +1,204 @@
+#!/usr/bin/perl
+#
+# FixMyStreet:Geocode::FixaMinGata
+# OpenStreetmap forward and reverse geocoding for FixMyStreet.
+#
+# Copyright (c) 2011 Petter Reinholdtsen. Some rights reserved.
+# Email: pere@hungry.com
+
+# This module is a slightly derived version of OSM.pm.
+
+# As of January 2014, the FixaMinGata developers are considering to make further
+# changes related to OSM, so it's probably best to keep this module separate
+# from the OSM module for now.
+
+package FixMyStreet::Geocode::FixaMinGata;
+
+use warnings;
+use strict;
+use Data::Dumper;
+
+use Digest::MD5 qw(md5_hex);
+use Encode;
+use File::Slurp;
+use File::Path ();
+use LWP::Simple qw($ua);
+use Memcached;
+use XML::Simple;
+use mySociety::Locale;
+
+my $osmapibase = "http://www.openstreetmap.org/api/";
+my $nominatimbase = "http://nominatim.openstreetmap.org/";
+
+# string STRING CONTEXT
+# Looks up on Nominatim, 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);
+
+ $s = FixMyStreet::Geocode::escape($s);
+ # $s .= '+' . $params->{town} if $params->{town} and $s !~ /$params->{town}/i;
+
+ my $url = "${nominatimbase}search?";
+ my %query_params = (
+ q => $s,
+ format => 'json',
+ addressdetails => 1,
+ limit => 20,
+ #'accept-language' => '',
+ email => 'info' . chr(64) . 'morus.se',
+ );
+ # $query_params{viewbox} = $params->{bounds}[1] . ',' . $params->{bounds}[2] . ',' . $params->{bounds}[3] . ',' . $params->{bounds}[0]
+ # if $params->{bounds};
+ $query_params{countrycodes} = $params->{country}
+ if $params->{country};
+ $url .= join('&', map { "$_=$query_params{$_}" } keys %query_params);
+
+ my $cache_dir = FixMyStreet->config('GEO_CACHE') . 'osm/';
+ my $cache_file = $cache_dir . md5_hex($url);
+ my $js;
+ if (-s $cache_file) {
+ $js = File::Slurp::read_file($cache_file);
+ } else {
+ $ua->timeout(15);
+ $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 find that location.') };
+ }
+
+ $js = JSON->new->utf8->allow_nonref->decode($js);
+
+ my ( %locations, $error, @valid_locations, $latitude, $longitude );
+ foreach (@$js) {
+ # These co-ordinates are output as query parameters in a URL, make sure they have a "."
+ next if $_->{class} eq "boundary";
+
+ my @s = split(/,/, $_->{display_name});
+
+ my $address = join(",", @s[0,1,2]);
+
+ $locations{$address} = [$_->{lat}, $_->{lon}];
+ }
+
+ my ($key) = keys %locations;
+
+ return { latitude => $locations{$key}[0], longitude => $locations{$key}[1] } if scalar keys %locations == 1;
+ return { error => _('Sorry, we could not find that location.') } if scalar keys %locations == 0;
+
+ foreach $key (keys %locations) {
+ ( $latitude, $longitude ) = ($locations{$key}[0], $locations{$key}[1]);
+ mySociety::Locale::in_gb_locale {
+ push (@$error, {
+ address => $key,
+ latitude => sprintf('%0.6f', $latitude),
+ longitude => sprintf('%0.6f', $longitude)
+ });
+ };
+ }
+
+ return { error => $error };
+}
+
+sub reverse_geocode {
+ my ($latitude, $longitude, $zoom) = @_;
+ my $url =
+ "${nominatimbase}reverse?format=xml&zoom=$zoom&lat=$latitude&lon=$longitude";
+ my $key = "OSM:reverse_geocode:$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);
+}
+
+# A better alternative might be
+# http://www.geonames.org/maps/osm-reverse-geocoder.html#findNearbyStreetsOSM
+sub get_nearest_road_tags {
+ my ( $cobrand, $latitude, $longitude ) = @_;
+ my $inforef = reverse_geocode($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});
+ unless ( exists $osmtags->{operator} ) {
+ $osmtags->{operatorguess} = $cobrand->guess_road_operator( $osmtags );
+ }
+ return $osmtags;
+ }
+ return undef;
+}
+
+sub closest_road_text {
+ my ( $cobrand, $latitude, $longitude ) = @_;
+ my $str = '';
+ my $osmtags = get_nearest_road_tags( $cobrand, $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;
+
diff --git a/perllib/FixMyStreet/Geocode/OSM.pm b/perllib/FixMyStreet/Geocode/OSM.pm
index f032a97b3..fd14b0acc 100644
--- a/perllib/FixMyStreet/Geocode/OSM.pm
+++ b/perllib/FixMyStreet/Geocode/OSM.pm
@@ -10,7 +10,6 @@ package FixMyStreet::Geocode::OSM;
use warnings;
use strict;
-use Data::Dumper;
use Digest::MD5 qw(md5_hex);
use Encode;
@@ -35,19 +34,17 @@ sub string {
my $params = $c->cobrand->disambiguate_location($s);
$s = FixMyStreet::Geocode::escape($s);
- # $s .= '+' . $params->{town} if $params->{town} and $s !~ /$params->{town}/i;
+ $s .= '+' . $params->{town} if $params->{town} and $s !~ /$params->{town}/i;
my $url = "${nominatimbase}search?";
my %query_params = (
q => $s,
format => 'json',
- addressdetails => 1,
- limit => 20,
#'accept-language' => '',
- email => 'info' . chr(64) . 'morus.se',
+ email => 'support' . chr(64) . 'fixmystreet.com',
);
- # $query_params{viewbox} = $params->{bounds}[1] . ',' . $params->{bounds}[2] . ',' . $params->{bounds}[3] . ',' . $params->{bounds}[0]
- # if $params->{bounds};
+ $query_params{viewbox} = $params->{bounds}[1] . ',' . $params->{bounds}[2] . ',' . $params->{bounds}[3] . ',' . $params->{bounds}[0]
+ if $params->{bounds};
$query_params{countrycodes} = $params->{country}
if $params->{country};
$url .= join('&', map { "$_=$query_params{$_}" } keys %query_params);
@@ -71,34 +68,21 @@ sub string {
$js = JSON->new->utf8->allow_nonref->decode($js);
- my ( %locations, $error, @valid_locations, $latitude, $longitude );
+ my ( $error, @valid_locations, $latitude, $longitude );
foreach (@$js) {
# These co-ordinates are output as query parameters in a URL, make sure they have a "."
- next if $_->{class} eq "boundary";
-
- my @s = split(/,/, $_->{display_name});
-
- my $address = join(",", @s[0,1,2]);
-
- $locations{$address} = [$_->{lat}, $_->{lon}];
- }
-
- my ($key) = keys %locations;
-
- return { latitude => $locations{$key}[0], longitude => $locations{$key}[1] } if scalar keys %locations == 1;
- return { error => _('Sorry, we could not find that location.') } if scalar keys %locations == 0;
-
- foreach $key (keys %locations) {
- ( $latitude, $longitude ) = ($locations{$key}[0], $locations{$key}[1]);
+ ( $latitude, $longitude ) = ( $_->{lat}, $_->{lon} );
mySociety::Locale::in_gb_locale {
push (@$error, {
- address => $key,
+ address => $_->{display_name},
latitude => sprintf('%0.6f', $latitude),
longitude => sprintf('%0.6f', $longitude)
});
};
+ push (@valid_locations, $_);
}
+ return { latitude => $latitude, longitude => $longitude } if scalar @valid_locations == 1;
return { error => $error };
}