diff options
author | Brion Vibber <brion@pobox.com> | 2010-09-15 14:09:23 -0700 |
---|---|---|
committer | Brion Vibber <brion@pobox.com> | 2010-09-15 14:10:18 -0700 |
commit | 95b534c5cee56602dffcc11121908c0f518cab6d (patch) | |
tree | 0a6a3cab7b1f6f881c51101bff7742dcb1d05cb6 /plugins/GeonamesPlugin.php | |
parent | 2d4c0f9a47f6534c578a37abe79670cec699caae (diff) |
Move standalone plugin files into subdirectories so they can have locale data, etc added to them at leisure.
Diffstat (limited to 'plugins/GeonamesPlugin.php')
-rw-r--r-- | plugins/GeonamesPlugin.php | 510 |
1 files changed, 0 insertions, 510 deletions
diff --git a/plugins/GeonamesPlugin.php b/plugins/GeonamesPlugin.php deleted file mode 100644 index 3815a31fa..000000000 --- a/plugins/GeonamesPlugin.php +++ /dev/null @@ -1,510 +0,0 @@ -<?php -/** - * StatusNet, the distributed open-source microblogging tool - * - * Plugin to convert string locations to Geonames IDs and vice versa - * - * PHP version 5 - * - * LICENCE: This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @category Action - * @package StatusNet - * @author Evan Prodromou <evan@status.net> - * @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 <evan@status.net> - * @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 LOCATION_NS = 1; - - public $host = 'ws.geonames.org'; - public $username = null; - public $token = null; - public $expiry = 7776000; // 90-day expiry - public $timeout = 2; // Web service timeout in seconds. - public $timeoutWindow = 60; // Further lookups in this process will be disabled for N seconds after a timeout. - public $cachePrefix = null; // Optional shared memcache prefix override - // to share lookups between local instances. - - protected $lastTimeout = null; // timestamp of last web service timeout - - /** - * 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) - { - $loc = $this->getCache(array('name' => $name, - 'language' => $language)); - - if ($loc !== false) { - $location = $loc; - return false; - } - - try { - $geonames = $this->getGeonames('search', - array('maxRows' => 1, - 'q' => $name, - 'lang' => $language, - 'type' => 'xml')); - } catch (Exception $e) { - $this->log(LOG_WARNING, "Error for $name: " . $e->getMessage()); - return true; - } - - if (count($geonames) == 0) { - // no results - $this->setCache(array('name' => $name, - 'language' => $language), - null); - return true; - } - - $n = $geonames[0]; - - $location = new Location(); - - $location->lat = $this->canonical($n->lat); - $location->lon = $this->canonical($n->lng); - $location->names[$language] = (string)$n->name; - $location->location_id = (string)$n->geonameId; - $location->location_ns = self::LOCATION_NS; - - $this->setCache(array('name' => $name, - 'language' => $language), - $location); - - // handled, don't continue processing! - return false; - } - - /** - * 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::LOCATION_NS) { - // It's not one of our IDs... keep processing - return true; - } - - $loc = $this->getCache(array('id' => $id)); - - if ($loc !== false) { - $location = $loc; - return false; - } - - try { - $geonames = $this->getGeonames('hierarchy', - array('geonameId' => $id, - 'lang' => $language)); - } catch (Exception $e) { - $this->log(LOG_WARNING, "Error for ID $id: " . $e->getMessage()); - return false; - } - - $parts = array(); - - foreach ($geonames as $level) { - if (in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { - $parts[] = (string)$level->name; - } - } - - $last = $geonames[count($geonames)-1]; - - if (!in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { - $parts[] = (string)$last->name; - } - - $location = new Location(); - - $location->location_id = (string)$last->geonameId; - $location->location_ns = self::LOCATION_NS; - $location->lat = $this->canonical($last->lat); - $location->lon = $this->canonical($last->lng); - - $location->names[$language] = implode(', ', array_reverse($parts)); - - $this->setCache(array('id' => (string)$last->geonameId), - $location); - - // 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) - { - // Make sure they're canonical - - $lat = $this->canonical($lat); - $lon = $this->canonical($lon); - - $loc = $this->getCache(array('lat' => $lat, - 'lon' => $lon)); - - if ($loc !== false) { - $location = $loc; - return false; - } - - try { - $geonames = $this->getGeonames('findNearbyPlaceName', - array('lat' => $lat, - 'lng' => $lon, - 'lang' => $language)); - } catch (Exception $e) { - $this->log(LOG_WARNING, "Error for coords $lat, $lon: " . $e->getMessage()); - return true; - } - - if (count($geonames) == 0) { - // no results - $this->setCache(array('lat' => $lat, - 'lon' => $lon), - null); - return true; - } - - $n = $geonames[0]; - - $parts = array(); - - $location = new Location(); - - $parts[] = (string)$n->name; - - if (!empty($n->adminName1)) { - $parts[] = (string)$n->adminName1; - } - - if (!empty($n->countryName)) { - $parts[] = (string)$n->countryName; - } - - $location->location_id = (string)$n->geonameId; - $location->location_ns = self::LOCATION_NS; - $location->lat = $this->canonical($n->lat); - $location->lon = $this->canonical($n->lng); - - $location->names[$language] = implode(', ', $parts); - - $this->setCache(array('lat' => $lat, - 'lon' => $lon), - $location); - - // Success! We handled it, so no further processing - - return false; - } - - /** - * 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::LOCATION_NS) { - // It's not one of our IDs... keep processing - return true; - } - - $id = $location->location_id; - - $n = $this->getCache(array('id' => $id, - 'language' => $language)); - - if ($n !== false) { - $name = $n; - return false; - } - - try { - $geonames = $this->getGeonames('hierarchy', - array('geonameId' => $id, - 'lang' => $language)); - } catch (Exception $e) { - $this->log(LOG_WARNING, "Error for ID $id: " . $e->getMessage()); - return false; - } - - if (count($geonames) == 0) { - $this->setCache(array('id' => $id, - 'language' => $language), - null); - return false; - } - - $parts = array(); - - foreach ($geonames as $level) { - if (in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { - $parts[] = (string)$level->name; - } - } - - $last = $geonames[count($geonames)-1]; - - if (!in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { - $parts[] = (string)$last->name; - } - - if (count($parts)) { - $name = implode(', ', array_reverse($parts)); - $this->setCache(array('id' => $id, - 'language' => $language), - $name); - } - - return false; - } - - /** - * Human-readable URL for a location - * - * Given a location, we try to retrieve a geonames.org URL. - * - * @param Location $location Location to get the url for - * @param string &$url Place to put the url - * - * @return boolean whether to continue - */ - - function onLocationUrl($location, &$url) - { - if ($location->location_ns != self::LOCATION_NS) { - // It's not one of our IDs... keep processing - return true; - } - - $url = 'http://www.geonames.org/' . $location->location_id; - - // it's been filled, so don't process further. - return false; - } - - /** - * Machine-readable name for a location - * - * Given a location, we try to retrieve a geonames.org URL. - * - * @param Location $location Location to get the url for - * @param string &$url Place to put the url - * - * @return boolean whether to continue - */ - - function onLocationRdfUrl($location, &$url) - { - if ($location->location_ns != self::LOCATION_NS) { - // It's not one of our IDs... keep processing - return true; - } - - $url = 'http://sws.geonames.org/' . $location->location_id . '/'; - - // it's been filled, so don't process further. - return false; - } - - function getCache($attrs) - { - $c = common_memcache(); - - if (empty($c)) { - return null; - } - - $key = $this->cacheKey($attrs); - - $value = $c->get($key); - - return $value; - } - - function setCache($attrs, $loc) - { - $c = common_memcache(); - - if (empty($c)) { - return null; - } - - $key = $this->cacheKey($attrs); - - $result = $c->set($key, $loc, 0, time() + $this->expiry); - - return $result; - } - - function cacheKey($attrs) - { - $key = 'geonames:' . - implode(',', array_keys($attrs)) . ':'. - common_keyize(implode(',', array_values($attrs))); - if ($this->cachePrefix) { - return $this->cachePrefix . ':' . $key; - } else { - return common_cache_key($key); - } - } - - function wsUrl($method, $params) - { - if (!empty($this->username)) { - $params['username'] = $this->username; - } - - if (!empty($this->token)) { - $params['token'] = $this->token; - } - - $str = http_build_query($params, null, '&'); - - return 'http://'.$this->host.'/'.$method.'?'.$str; - } - - function getGeonames($method, $params) - { - if ($this->lastTimeout && (time() - $this->lastTimeout < $this->timeoutWindow)) { - throw new Exception("skipping due to recent web service timeout"); - } - - $client = HTTPClient::start(); - $client->setConfig('connect_timeout', $this->timeout); - $client->setConfig('timeout', $this->timeout); - - try { - $result = $client->get($this->wsUrl($method, $params)); - } catch (Exception $e) { - common_log(LOG_ERR, __METHOD__ . ": " . $e->getMessage()); - $this->lastTimeout = time(); - throw $e; - } - - if (!$result->isOk()) { - throw new Exception("HTTP error code " . $result->getStatus()); - } - - $body = $result->getBody(); - - if (empty($body)) { - throw new Exception("Empty HTTP body in response"); - } - - // This will throw an exception if the XML is mal-formed - - $document = new SimpleXMLElement($body); - - // No children, usually no results - - $children = $document->children(); - - if (count($children) == 0) { - return array(); - } - - if (isset($document->status)) { - throw new Exception("Error #".$document->status['value']." ('".$document->status['message']."')"); - } - - // Array of elements, >0 elements - - return $document->geoname; - } - - function onPluginVersion(&$versions) - { - $versions[] = array('name' => 'Geonames', - 'version' => STATUSNET_VERSION, - 'author' => 'Evan Prodromou', - 'homepage' => 'http://status.net/wiki/Plugin:Geonames', - 'rawdescription' => - _m('Uses <a href="http://geonames.org/">Geonames</a> service to get human-readable '. - 'names for locations based on user-provided lat/long pairs.')); - return true; - } - - function canonical($coord) - { - $coord = rtrim($coord, "0"); - $coord = rtrim($coord, "."); - - return $coord; - } -} |