Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #1252 from ditorelo/794

Adding Nominatin as a geocoder option (Issue #794)
  • Loading branch information...
commit 6f555a097880f6424c80dad894d7af3a9cbef662 2 parents 1f4734b + 3bead62
@rjmackay rjmackay authored
View
6 application/config/map.php
@@ -80,3 +80,9 @@
+/**
+ * Determines which geocoding engine to use
+ * Options are google or nominatim
+ */
+
+$config['geocode'] = "nominatim";
View
300 application/helpers/geocode.php
@@ -0,0 +1,300 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Geocode helper class
+ *
+ * Portions of this class credited to: zzolo, phayes, tmcw, brynbellomy, bdragon
+ *
+ * @package Geocode
+ * @author Ushahidi Team
+ * @copyright (c) 2008 Ushahidi Team
+ * @license http://www.ushahidi.com/license.html
+ */
+
+
+class geocode_Core {
+
+ /**
+ * Geocoding function. Will receive an address and call service configured on map.geocode config
+ *
+ * @param string Address
+ * @return string[]|boolean Location information [country, country_id, location_name, latitude, longitude], FALSE if unsucessful
+ *
+ */
+ static public function geocode($address = NULL) {
+ $service = Kohana::config('map.geocode');
+
+ if ($address)
+ {
+ if (! method_exists('geocode_Core', $service)) {
+ throw new Kohana_Exception("'" . $service . "' is not a valid geocode service");
+ return FALSE;
+ }
+
+ return self::$service($address);
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+ /**
+ * Geocoding function that uses nominatin engine.
+ *
+ * @param string Address
+ * @return string[]|boolean Location information [country, country_id, location_name, latitude, longitude], FALSE if unsucessful
+ *
+ */
+ static public function nominatim($address) {
+ $payload = FALSE;
+ $result = FALSE;
+
+ $params = array(
+ "format" => "json",
+ "addressdetails" => 1,
+ "accept-language" => "en_US", // force country names to come back as english,
+ "q" => $address,
+ "zoom" => 200
+ );
+
+ $url = "http://nominatim.openstreetmap.org/search/?" . http_build_query($params);
+
+ $url_request = new HttpClient($url);
+
+ if ($result = $url_request->execute())
+ {
+ $payload = json_decode($result);
+ }
+ else
+ {
+ Kohana::log('error', "Geocode - Nominatin\n" . $url_request->get_error_msg());
+ }
+
+ // TODO: Nomaninatins documentation on error returning is poor - this could be improved to have meaningful error messages
+ if (!$payload || count($payload) == 0)
+ {
+ return FALSE;
+ }
+
+ $result = array_pop($payload);
+
+ $country_name = isset($result->address->country) ? $result->address->country : $result->display_name;
+
+ $country = self::getCountryId($country_name);
+
+ // if we can't find the country by name, try finding it by code
+ if ($country == 0 && isset($result->address->country_code))
+ {
+ $country = self::getCountryIdByCode($result->address->country_code);
+ }
+
+ $geocodes = array(
+ 'country' => $country_name,
+ 'country_id' => $country,
+ 'location_name' => $result->display_name,
+ 'latitude' => $result->lat,
+ 'longitude' => $result->lon
+ );
+
+ return $geocodes;
+ }
+
+ /**
+ * Geocoding function that uses google engine.
+ *
+ * @param string Address
+ * @return string[]|boolean Location information [country, country_id, location_name, latitude, longitude], FALSE if unsucessful
+ *
+ */
+ static public function google($address) {
+ $payload = FALSE;
+
+ $url = Kohana::config('config.external_site_protocol').'://maps.google.com/maps/api/geocode/json?sensor=false&address='.rawurlencode($address);
+ $result = FALSE;
+
+ $url_request = new HttpClient($url);
+
+ if ($result = $url_request->execute())
+ {
+ $payload = json_decode($result);
+ }
+ else
+ {
+ Kohana::log('error', "Geocode - Google\n" . $url_request->get_error_msg());
+ }
+
+ // Verify that the request succeeded
+ if (! isset($payload->status)) return FALSE;
+ if ($payload->status != 'OK')
+ {
+ if ($payload->status != 'ZERO_RESULTS')
+ {
+ // logs anything different from OK or ZERO_RESULTS
+ Kohana::log('error', "Geocode - Google: " . $payload->status);
+ }
+
+ return FALSE;
+ }
+
+ // Convert the Geocoder's results to an array
+ $all_components = json_decode(json_encode($payload->results), TRUE);
+
+ $result = array_pop($all_components);
+ $location = $result['geometry']['location'];
+
+ // Find the country
+ $address_components = $result['address_components'];
+ $country_name = NULL;
+ foreach ($address_components as $component)
+ {
+ if (in_array('country', $component['types']))
+ {
+ $country_name = $component['long_name'];
+ break;
+ }
+ }
+
+ // If no country has been found, use the formatted address
+ if (empty($country_name))
+ {
+ $country_name = $result['formatted_address'];
+ }
+
+ $geocodes = array(
+ 'country' => $country_name,
+ 'country_id' => self::getCountryId($country_name),
+ 'location_name' => $result['formatted_address'],
+ 'latitude' => $location['lat'],
+ 'longitude' => $location['lng']
+ );
+
+ return $geocodes;
+ }
+
+ /**
+ * Finds country on deployment database by name
+ * @param string Country Name
+ * @return int Country Id if exists, 0 if not
+ *
+ */
+ static function getCountryId($country_name) {
+ // Grab country_id
+ $country = Country_Model::get_country_by_name($country_name);
+ return ( ! empty($country) AND $country->loaded)? $country->id : 0;
+ }
+
+ /**
+ * Finds country on deployment database by code
+ * @param string Country Name
+ * @return int Country Id if exists, 0 if not
+ *
+ */
+ static function getCountryIdByCode($country_code) {
+ // Grab country_id
+ $country = Country_Model::get_country_by_code($country_code);
+ return ( ! empty($country) AND $country->loaded)? $country->id : 0;
+ }
+
+
+ /**
+ * Reverse Geocode a point
+ *
+ * @author
+ * @param double $latitude
+ * @param double $longitude
+ * @return string closest approximation of the point as a display name
+ */
+ static function reverseGeocode($latitude, $longitude) {
+ if ($latitude && $longitude)
+ {
+ $function = "reverse" . ucfirst($service);
+
+ if (! method_exists('geocode_Core', $function)) {
+ throw new Kohana_Exception("'" . $service . "' is not a valid geocode service");
+ return FALSE;
+ }
+
+ return self::$service($address);
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+ /**
+ * Reverse Geocode a point using Nominatin
+ *
+ * @author
+ * @param double $latitude
+ * @param double $longitude
+ * @return string closest approximation of the point as a display name
+ */
+ static function reverseNominatim($lat, $lng) {
+ if ($lat && $lng)
+ {
+ $url = 'http://nominatim.openstreetmap.org/reverse?format=json&lat=' . $lat . '&lon=' . $lng;
+
+ $request = new HttpClient($url);
+ if ( ! $json = $request->execute()) {
+ Kohana::log('error', "Geocode - reverseNominatin\n" . $url_request->get_error_msg());
+
+ return FALSE;
+ }
+
+ $location = json_decode($json, FALSE);
+
+ return $location->display_name;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+ /**
+ * Reverse Geocode a point using Google Geocode
+ *
+ * @author
+ * @param double $latitude
+ * @param double $longitude
+ * @return string closest approximation of the point as a display name
+ */
+ static function reverseGoogle($lat, $lng) {
+ if ($lat && $lng)
+ {
+ $url = Kohana::config('config.external_site_protocol') . '://maps.googleapis.com/maps/api/geocode/json?sensor=false&latlng=' . $lat . "," . $lng;
+
+ $request = new HttpClient($url);
+ if ( ! $json = $request->execute()) {
+ Kohana::log('error', "Geocode - reverseGoogle\n" . $url . "\n" . $request->get_error_msg());
+
+ return FALSE;
+ }
+
+ $location = json_decode($json);
+
+ if ($location->status != 'OK')
+ {
+ // logs anything different from OK
+ Kohana::log('error', "Geocode - reverseGoogle: " . $location->status . " - " . $location->error_message);
+
+ return FALSE;
+ }
+
+ if (count($location->results) == 0)
+ {
+ return FALSE;
+ }
+
+ return $location->results[0]->formatted_address;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+
+
+}
View
75 application/helpers/map.php
@@ -467,64 +467,7 @@ public static function base($layer_name = NULL)
*/
public static function geocode($address = NULL)
{
- if ($address)
- {
- $payload = FALSE;
-
- $url = Kohana::config('config.external_site_protocol').'://maps.google.com/maps/api/geocode/json?sensor=false&address='.rawurlencode($address);
- $result = FALSE;
-
- $url_request = new HttpClient($url);
-
- if ($result = $url_request->execute())
- {
- $payload = json_decode($result);
- }
-
- // Verify that the request succeeded
- if (! isset($payload->status)) return FALSE;
- if ($payload->status != 'OK') return FALSE;
-
- // Convert the Geocoder's results to an array
- $all_components = json_decode(json_encode($payload->results), TRUE);
- $location = $all_components[0]['geometry']['location'];
-
- // Find the country
- $address_components = $all_components[0]['address_components'];
- $country_name = NULL;
- foreach ($address_components as $component)
- {
- if (in_array('country', $component['types']))
- {
- $country_name = $component['long_name'];
- break;
- }
- }
-
- // If no country has been found, use the formatted address
- if (empty($country_name))
- {
- $country_name = $all_components[0]['formatted_address'];
- }
-
- // Grab country_id
- $country = Country_Model::get_country_by_name($country_name);
- $country_id = ( ! empty($country) AND $country->loaded)? $country->id : 0;
-
- $geocodes = array(
- 'country' => $country_name,
- 'country_id' => $country_id,
- 'location_name' => $all_components[0]['formatted_address'],
- 'latitude' => $location['lat'],
- 'longitude' => $location['lng']
- );
-
- return $geocodes;
- }
- else
- {
- return FALSE;
- }
+ return geocode::geocode($address);
}
/**
@@ -537,21 +480,7 @@ public static function geocode($address = NULL)
*/
public static function reverse_geocode($latitude,$longitude)
{
- if ($latitude AND $longitude)
- {
- $url = 'http://nominatim.openstreetmap.org/reverse?format=json&lat=' . $latitude . '&lon=' . $longitude;
-
- $request = new HttpClient($url);
- $json = $request->execute();
-
- $location = json_decode($json, false);
-
- return $location->display_name;
- }
- else
- {
- return false;
- }
+ return geocode::reverseGeocode($latitude, $longitude);
}
/**
View
16 application/models/country.php
@@ -52,6 +52,22 @@ public static function get_country_by_name($country_name)
return ($country->loaded)? $country : NULL;
}
+
+ /**
+ * Given a country code, returns a country model object reference. Country codes are unique and ISO based
+ *
+ * @param string $country_code The two letter country code
+ * @return mixed ORM reference if country exists, FALSE otherwise
+ */
+ public static function get_country_by_code($country_code)
+ {
+ // Find the country with the specified name
+ $country = self::factory('country')->where('iso', $country_code)->find();
+
+ // Return
+ return ($country->loaded)? $country : NULL;
+
+ }
/**
* Returns a key=>value array of the list of countries in the database
View
47 tests/phpunit/classes/helpers/Geocode_Test.php
@@ -0,0 +1,47 @@
+<?php
+class Geocode_Helper_Test extends PHPUnit_Framework_TestCase {
+
+
+ public function setUp()
+ {
+ // Test location - Wanaka, NZ
+ $this->lat = -44.696736;
+ $this->lng = 169.131646;
+ }
+
+
+ /**
+ * Tests geocoder using google functions
+ *
+ * @test
+ */
+ public function testGoogleGeocoder()
+ {
+ // reverse geocode
+ $address = geocode::reverseGoogle($this->lat, $this->lng);
+ $this->assertTrue($address !== FALSE);
+
+ // geocode
+ $result = geocode::google($address);
+ $this->assertEquals($result["latitude"], $this->lat, null, 0.01);
+ $this->assertEquals($result["longitude"], $this->lng, null, 0.01);
+ }
+
+ /**
+ * Tests geocoder using google functions
+ *
+ * @test
+ */
+ public function testNominatinGeocoder()
+ {
+ // reverse geocode
+ $address = geocode::reverseNominatim($this->lat, $this->lng);
+ $this->assertTrue($address !== FALSE);
+
+ // geocode
+ $result = geocode::nominatim($address);
+ $this->assertEquals($result["latitude"], $this->lat, null, 0.01);
+ $this->assertEquals($result["longitude"], $this->lng, null, 0.01);
+ }
+
+}
Please sign in to comment.
Something went wrong with that request. Please try again.