X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=plugins%2FGeonamesPlugin.php;h=3815a31fa681d90ed8b1fb8782f4791918bd18f4;hb=338aa4bf1d36e11a354c67796509c6d1fec2aac2;hp=a750f1242603bbf1857d2d28ab793cfa2747f02f;hpb=f6bf9529805cd58fdd1671dd9b133bde05e8ae87;p=quix0rs-gnu-social.git diff --git a/plugins/GeonamesPlugin.php b/plugins/GeonamesPlugin.php index a750f12426..3815a31fa6 100644 --- a/plugins/GeonamesPlugin.php +++ b/plugins/GeonamesPlugin.php @@ -55,6 +55,12 @@ class GeonamesPlugin extends Plugin 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 @@ -71,45 +77,46 @@ class GeonamesPlugin extends Plugin $loc = $this->getCache(array('name' => $name, 'language' => $language)); - if (!empty($loc)) { + if ($loc !== false) { $location = $loc; return false; } - $client = HTTPClient::start(); - - // XXX: break down a name by commas, narrow by each - - $result = $client->get($this->wsUrl('search', - array('maxRows' => 1, - 'q' => $name, - 'lang' => $language, - 'type' => 'json'))); + 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 ($result->isOk()) { - $rj = json_decode($result->getBody()); - if (count($rj->geonames) > 0) { - $n = $rj->geonames[0]; + if (count($geonames) == 0) { + // no results + $this->setCache(array('name' => $name, + 'language' => $language), + null); + return true; + } - $location = new Location(); + $n = $geonames[0]; - $location->lat = $n->lat; - $location->lon = $n->lng; - $location->names[$language] = $n->name; - $location->location_id = $n->geonameId; - $location->location_ns = self::LOCATION_NS; + $location = new Location(); - $this->setCache(array('name' => $name, - 'language' => $language), - $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; - // handled, don't continue processing! - return false; - } - } + $this->setCache(array('name' => $name, + 'language' => $language), + $location); - // Continue processing; we don't have the answer - return true; + // handled, don't continue processing! + return false; } /** @@ -132,51 +139,47 @@ class GeonamesPlugin extends Plugin $loc = $this->getCache(array('id' => $id)); - if (!empty($loc)) { + if ($loc !== false) { $location = $loc; return false; } - $client = HTTPClient::start(); - - $result = $client->get($this->wsUrl('hierarchyJSON', - array('geonameId' => $id, - 'lang' => $language))); - - if ($result->isOk()) { - - $rj = json_decode($result->getBody()); + 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($rj->geonames) > 0) { + $parts = array(); - $parts = array(); + foreach ($geonames as $level) { + if (in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = (string)$level->name; + } + } - foreach ($rj->geonames as $level) { - if (in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { - $parts[] = $level->name; - } - } + $last = $geonames[count($geonames)-1]; - $last = $rj->geonames[count($rj->geonames)-1]; + if (!in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = (string)$last->name; + } - if (!in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { - $parts[] = $last->name; - } + $location = new Location(); - $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->location_id = $last->geonameId; - $location->location_ns = self::LOCATION_NS; - $location->lat = $last->lat; - $location->lon = $last->lng; - $location->names[$language] = implode(', ', array_reverse($parts)); + $location->names[$language] = implode(', ', array_reverse($parts)); - $this->setCache(array('id' => $last->geonameId), - $location); - } - } + $this->setCache(array('id' => (string)$last->geonameId), + $location); - // We're responsible for this NAMESPACE; nobody else + // We're responsible for this namespace; nobody else // can resolve it return false; @@ -198,67 +201,67 @@ class GeonamesPlugin extends Plugin function onLocationFromLatLon($lat, $lon, $language, &$location) { - $lat = rtrim($lat, "0"); - $lon = rtrim($lon, "0"); + // Make sure they're canonical + + $lat = $this->canonical($lat); + $lon = $this->canonical($lon); $loc = $this->getCache(array('lat' => $lat, 'lon' => $lon)); - if (!empty($loc)) { + if ($loc !== false) { $location = $loc; return false; } - $client = HTTPClient::start(); - - $result = - $client->get($this->wsUrl('findNearbyPlaceNameJSON', - array('lat' => $lat, - 'lng' => $lon, - 'lang' => $language))); - - if ($result->isOk()) { - - $rj = json_decode($result->getBody()); - - if (count($rj->geonames) > 0) { - - $n = $rj->geonames[0]; + 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; + } - $parts = array(); + if (count($geonames) == 0) { + // no results + $this->setCache(array('lat' => $lat, + 'lon' => $lon), + null); + return true; + } - $location = new Location(); + $n = $geonames[0]; - $parts[] = $n->name; + $parts = array(); - if (!empty($n->adminName1)) { - $parts[] = $n->adminName1; - } + $location = new Location(); - if (!empty($n->countryName)) { - $parts[] = $n->countryName; - } + $parts[] = (string)$n->name; - $location->location_id = $n->geonameId; - $location->location_ns = self::LOCATION_NS; - $location->lat = $lat; - $location->lon = $lon; + if (!empty($n->adminName1)) { + $parts[] = (string)$n->adminName1; + } - $location->names[$language] = implode(', ', $parts); + if (!empty($n->countryName)) { + $parts[] = (string)$n->countryName; + } - $this->setCache(array('lat' => $lat, - 'lon' => $lon), - $location); + $location->location_id = (string)$n->geonameId; + $location->location_ns = self::LOCATION_NS; + $location->lat = $this->canonical($n->lat); + $location->lon = $this->canonical($n->lng); - // Success! We handled it, so no further processing + $location->names[$language] = implode(', ', $parts); - return false; - } - } + $this->setCache(array('lat' => $lat, + 'lon' => $lon), + $location); - // For some reason we don't know, so pass. + // Success! We handled it, so no further processing - return true; + return false; } /** @@ -281,55 +284,58 @@ class GeonamesPlugin extends Plugin return true; } - $n = $this->getCache(array('id' => $location->location_id, + $id = $location->location_id; + + $n = $this->getCache(array('id' => $id, 'language' => $language)); - if (!empty($n)) { + if ($n !== false) { $name = $n; return false; } - $client = HTTPClient::start(); - - $result = $client->get($this->wsUrl('hierarchyJSON', - array('geonameId' => $location->location_id, - 'lang' => $language))); - - if ($result->isOk()) { - - $rj = json_decode($result->getBody()); + 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($rj->geonames) > 0) { + if (count($geonames) == 0) { + $this->setCache(array('id' => $id, + 'language' => $language), + null); + return false; + } - $parts = array(); + $parts = array(); - foreach ($rj->geonames as $level) { - if (in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { - $parts[] = $level->name; - } - } + foreach ($geonames as $level) { + if (in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = (string)$level->name; + } + } - $last = $rj->geonames[count($rj->geonames)-1]; + $last = $geonames[count($geonames)-1]; - if (!in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { - $parts[] = $last->name; - } + 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' => $location->location_id, - 'language' => $language), - $name); - return false; - } - } + if (count($parts)) { + $name = implode(', ', array_reverse($parts)); + $this->setCache(array('id' => $id, + 'language' => $language), + $name); } - return true; + return false; } /** - * Human-readable name for a location + * Human-readable URL for a location * * Given a location, we try to retrieve a geonames.org URL. * @@ -370,7 +376,7 @@ class GeonamesPlugin extends Plugin return true; } - $url = 'http://sw.geonames.org/' . $location->location_id . '/'; + $url = 'http://sws.geonames.org/' . $location->location_id . '/'; // it's been filled, so don't process further. return false; @@ -408,9 +414,14 @@ class GeonamesPlugin extends Plugin function cacheKey($attrs) { - return common_cache_key('geonames:'. - implode(',', array_keys($attrs)) . ':'. - common_keyize(implode(',', array_values($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) @@ -423,8 +434,77 @@ class GeonamesPlugin extends Plugin $params['token'] = $this->token; } - $str = http_build_query($params); + $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 Geonames 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; + } }