From 9c983c383029c9aaa7d0cf04a5fc9973d514dff7 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Oct 2009 15:44:36 -0400 Subject: extract Geonames stuff to a plugin --- plugins/GeonamesPlugin.php | 281 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 plugins/GeonamesPlugin.php (limited to 'plugins/GeonamesPlugin.php') diff --git a/plugins/GeonamesPlugin.php b/plugins/GeonamesPlugin.php new file mode 100644 index 000000000..934e998c7 --- /dev/null +++ b/plugins/GeonamesPlugin.php @@ -0,0 +1,281 @@ +. + * + * @category Action + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Plugin to convert string locations to Geonames IDs and vice versa + * + * This handles most of the events that Location class emits. It uses + * the geonames.org Web service to convert names like 'Montreal, Quebec, Canada' + * into IDs and lat/lon pairs. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @seeAlso Location + */ + +class GeonamesPlugin extends Plugin +{ + const NAMESPACE = 1; + + /** + * convert a name into a Location object + * + * @param string $name Name to convert + * @param string $language ISO code for anguage the name is in + * @param Location &$location Location object (may be null) + * + * @return boolean whether to continue (results in $location) + */ + + function onLocationFromName($name, $language, &$location) + { + $client = HTTPClient::start(); + + // XXX: break down a name by commas, narrow by each + + $str = http_build_query(array('maxRows' => 1, + 'q' => $name, + 'lang' => $language, + 'type' => 'json')); + + $result = $client->get('http://ws.geonames.org/search?'.$str); + + if ($result->code == "200") { + $rj = json_decode($result->body); + if (count($rj['geonames']) > 0) { + $n = $rj['geonames'][0]; + + $location = new Location(); + + $location->lat = $n['lat']; + $location->lon = $n['lng']; + $location->names[$language] = $n['name']; + $location->location_id = $n['geonameId']; + $location->location_ns = self::NAMESPACE; + + // handled, don't continue processing! + return false; + } + } + + // Continue processing; we don't have the answer + return true; + } + + /** + * convert an id into a Location object + * + * @param string $id Name to convert + * @param string $ns Name to convert + * @param string $language ISO code for language for results + * @param Location &$location Location object (may be null) + * + * @return boolean whether to continue (results in $location) + */ + + function onLocationFromId($id, $ns, $language, &$location) + { + if ($ns != self::NAMESPACE) { + // It's not one of our IDs... keep processing + return true; + } + + $client = HTTPClient::start(); + + $str = http_build_query(array('geonameId' => $id, + 'lang' => $language)); + + $result = $client->get('http://ws.geonames.org/hierarchyJSON?'.$str); + + if ($result->code == "200") { + + $rj = json_decode($result->body); + + if (count($rj['geonames']) > 0) { + + $parts = array(); + + foreach ($rj['geonames'] as $level) { + if (in_array($level['fcode'], array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = $level['name']; + } + } + + $last = $rj['geonames'][count($rj['geonames'])-1]; + + if (!in_array($level['fcode'], array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = $last['name']; + } + + $location = new Location(); + + $location->location_id = $last['geonameId']; + $location->location_ns = self::NAMESPACE; + $location->lat = $last['lat']; + $location->lon = $last['lng']; + $location->names[$language] = implode(', ', array_reverse($parts)); + } + } + + // We're responsible for this NAMESPACE; nobody else + // can resolve it + + return false; + } + + /** + * convert a lat/lon pair into a Location object + * + * Given a lat/lon, we try to find a Location that's around + * it or nearby. We prefer populated places (cities, towns, villages). + * + * @param string $lat Latitude + * @param string $lon Longitude + * @param string $language ISO code for language for results + * @param Location &$location Location object (may be null) + * + * @return boolean whether to continue (results in $location) + */ + + function onLocationFromLatLon($lat, $lon, $language, &$location) + { + $client = HTTPClient::start(); + + $str = http_build_query(array('lat' => $lat, + 'lng' => $lon, + 'lang' => $language)); + + $result = + $client->get('http://ws.geonames.org/findNearbyPlaceNameJSON?'.$str); + + if ($result->code == "200") { + + $rj = json_decode($result->body); + + if (count($rj['geonames']) > 0) { + + $n = $rj['geonames'][0]; + + $parts = array(); + + $location = new Location(); + + $parts[] = $n['name']; + + if (!empty($n['adminName1'])) { + $parts[] = $n['adminName1']; + } + + if (!empty($n['countryName'])) { + $parts[] = $n['countryName']; + } + + $location->location_id = $n['geonameId']; + $location->location_ns = self::NAMESPACE; + $location->lat = $lat; + $location->lon = $lon; + + $location->names[$language] = implode(', ', $parts); + + // Success! We handled it, so no further processing + + return false; + } + } + + // For some reason we don't know, so pass. + + return true; + } + + /** + * Human-readable name for a location + * + * Given a location, we try to retrieve a human-readable name + * in the target language. + * + * @param Location $location Location to get the name for + * @param string $language ISO code for language to find name in + * @param string &$name Place to put the name + * + * @return boolean whether to continue + */ + + function onLocationNameLanguage($location, $language, &$name) + { + if ($location->location_ns != self::NAMESPACE) { + // It's not one of our IDs... keep processing + return true; + } + + $client = HTTPClient::start(); + + $str = http_build_query(array('geonameId' => $id, + 'lang' => $language)); + + $result = $client->get('http://ws.geonames.org/hierarchyJSON?'.$str); + + if ($result->code == "200") { + + $rj = json_decode($result->body); + + if (count($rj['geonames']) > 0) { + + $parts = array(); + + foreach ($rj['geonames'] as $level) { + if (in_array($level['fcode'], array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = $level['name']; + } + } + + $last = $rj['geonames'][count($rj['geonames'])-1]; + + if (!in_array($level['fcode'], array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = $last['name']; + } + + if (count($parts)) { + $name = implode(', ', array_reverse($parts)); + return false; + } + } + } + + return true; + } +} -- cgit v1.2.3-54-g00ecf