X-Git-Url: http://git.polytechnique.org/?a=blobdiff_plain;f=classes%2Fgmapsgeocoder.php;h=bea3a52c94cb8a02fad363e243f4edb045e09a99;hb=b9c627802fa9dd4e93666b8d95d325a259db3ec6;hp=834702690cd1acfb0cb3d4a96a503356ea4b680b;hpb=1c4a1d0a32c52851e9ffbbf585bb206c9a472dc9;p=platal.git diff --git a/classes/gmapsgeocoder.php b/classes/gmapsgeocoder.php index 8347026..bea3a52 100644 --- a/classes/gmapsgeocoder.php +++ b/classes/gmapsgeocoder.php @@ -1,6 +1,6 @@ '300x100', + 'markers' => 'color:' . $color . '|' . $latitude . ',' . $longitude, + 'zoom' => '12', + 'sensor' => 'false' + ); + + return Platal::globals()->maps->static_map . '?' . http_build_query($parameters, '', $separator); + } + + public function getGeocodedAddress(Address $address, $defaultLanguage = null, $forceLanguage = false) { $this->prepareAddress($address); $textAddress = $this->getTextToGeocode($address->text); + if (is_null($defaultLanguage)) { + $defaultLanguage = Platal::globals()->geocoder->gmaps_language; + } // Try to geocode the full address. - if (($geocodedData = $this->getPlacemarkForAddress($textAddress))) { - $this->getUpdatedAddress($address, $geocodedData, null); + $address->geocoding_calls = 1; + if (($geocodedData = $this->getPlacemarkForAddress($textAddress, $defaultLanguage))) { + $this->getUpdatedAddress($address, $geocodedData, null, $forceLanguage); return; } @@ -55,66 +69,62 @@ class GMapsGeocoder extends Geocoder { for ($i = max(1, $linesCount - self::MAX_GMAPS_RPC_CALLS + 1); $i < $linesCount; ++$i) { $extraLines = implode("\n", array_slice($addressLines, 0, $i)); $toGeocode = implode("\n", array_slice($addressLines, $i)); - if (($geocodedData = $this->getPlacemarkForAddress($toGeocode))) { - $this->getUpdatedAddress($address, $geocodedData, $extraLines); + ++$address->geocoding_calls; + if (($geocodedData = $this->getPlacemarkForAddress($toGeocode, $defaultLanguage))) { + $this->getUpdatedAddress($address, $geocodedData, $extraLines, $forceLanguage); return; } } } - public function stripGeocodingFromAddress(Address &$address) { - $address->geocodedText = null; - $address->geocodedPostalText = null; - $address->geoloc_choice = null; - $address->countryId = null; - $address->country = null; - $address->administrativeAreaName = null; - $address->subAdministrativeAreaName = null; - $address->localityName = null; - $address->thoroughfareName = null; - $address->postalCode = null; - $address->accuracy = 0; + public function stripGeocodingFromAddress(Address $address) { + $address->formatted_address = ''; + $address->types = ''; + $address->latitude = null; + $address->longitude = null; + $address->southwest_latitude = null; + $address->southwest_longitude = null; + $address->northeast_latitude = null; + $address->northeast_longitude = null; + $address->location_type = null; + $address->partial_match = false; } // Updates the address with the geocoded information from Google Maps. Also // cleans up the final informations. - private function getUpdatedAddress(Address &$address, array $geocodedData, $extraLines) { - $this->fillAddressWithGeocoding($address, $geocodedData); - $this->formatAddress($address, $extraLines); + private function getUpdatedAddress(Address $address, array $geocodedData, $extraLines, $forceLanguage) { + $this->fillAddressWithGeocoding($address, $geocodedData, false); + $this->formatAddress($address, $extraLines, $forceLanguage); } // Retrieves the Placemark object (see #getPlacemarkFromJson()) for the @p // address, by querying the Google Maps API. Returns the array on success, // and null otherwise. - private function getPlacemarkForAddress($address) { - $url = $this->getGeocodingUrl($address); + private function getPlacemarkForAddress($address, $defaultLanguage) { + $url = $this->getGeocodingUrl($address, $defaultLanguage); $geoData = $this->getGeoJsonFromUrl($url); - return ($geoData ? $this->getPlacemarkFromJson($geoData) : null); + return ($geoData ? $this->getPlacemarkFromJson($geoData, $url) : null); } // Prepares address to be geocoded - private function prepareAddress(Address &$address) { + private function prepareAddress(Address $address) { $address->text = preg_replace('/\s*\n\s*/m', "\n", trim($address->text)); - $address->postalText = $this->getPostalAddress($address->text); } // Builds the Google Maps geocoder url to fetch information about @p address. // Returns the built url. - private function getGeocodingUrl($address) { + private function getGeocodingUrl($address, $defaultLanguage) { global $globals; $parameters = array( - 'key' => $globals->geocoder->gmaps_key, - 'sensor' => 'false', // The queried address wasn't obtained from a GPS sensor. - 'hl' => 'fr', // Output langage. - 'oe' => 'utf8', // Output encoding. - 'output' => 'json', // Output format. - 'gl' => 'fr', // Location preferences (addresses are in France by default). - 'q' => $address, // The queries address. + 'language' => $defaultLanguage, + 'region' => $globals->geocoder->gmaps_region, + 'sensor' => 'false', // The queried address wasn't obtained from a GPS sensor. + 'address' => $address, // The queries address. ); - return $globals->geocoder->gmaps_url . '?' . http_build_query($parameters); + return $globals->geocoder->gmaps_url . 'json?' . http_build_query($parameters); } // Fetches JSON-encoded data from a Google Maps API url, and decode them. @@ -151,180 +161,69 @@ class GMapsGeocoder extends Geocoder { } // Extracts the most appropriate placemark from the JSON data fetched from - // Google Maps. Returns a Placemark array on success, and null otherwise. See - // http://code.google.com/apis/maps/documentation/services.html#Geocoding_Structured - // for details on the Placemark structure. - private function getPlacemarkFromJson(array $data) { - // Check for geocoding failures. - if (!isset($data['Status']['code']) || $data['Status']['code'] != 200) { - // TODO: handle non-200 codes in a better way, since the code might - // indicate a temporary error on Google's side. - return null; - } - - // Check that at least one placemark was found. - if (count($data['Placemark']) == 0) { + // Google Maps. Returns a Placemark array on success, and null otherwise. + // http://code.google.com/apis/maps/documentation/geocoding/#StatusCodes + private function getPlacemarkFromJson(array $data, $url) { + // Check for geocoding status. + $status = $data['status']; + + // If no result, return null. + if ($status == 'ZERO_RESULTS') { return null; } - // Extract the placemark with the best accuracy. This is not always the - // best result (since the same address may yield two different placemarks). - $result = $data['Placemark'][0]; - foreach ($data['Placemark'] as $place) { - if ($place['AddressDetails']['Accuracy'] > $result['AddressDetails']['Accuracy']) { - $result = $place; - } + // If there are results return the first one. + if ($status == 'OK') { + return $data['results'][0]; } - return $result; + // Report the error. + $mailer = new PlMailer('profile/geocoding.mail.tpl'); + $mailer->assign('status', $status); + $mailer->assign('url', $url); + $mailer->send(); + return null; } // Fills the address with the geocoded data - private function fillAddressWithGeocoding(Address &$address, $geocodedData) { - // The geocoded address three is - // Country -> AdministrativeArea -> SubAdministrativeArea -> Locality -> Thoroughfare - // with all the possible shortcuts - // The address is formatted as xAL, or eXtensible Address Language, an international - // standard for address formatting. - // xAL documentation: http://www.oasis-open.org/committees/ciq/ciq.html#6 - $address->geocodedText = str_replace(', ', "\n", $geocodedData['address']); - if (isset($geocodedData['AddressDetails']['Accuracy'])) { - $address->accuracy = $geocodedData['AddressDetails']['Accuracy']; - } - - $currentPosition = $geocodedData['AddressDetails']; - if (isset($currentPosition['Country'])) { - $currentPosition = $currentPosition['Country']; - $address->countryId = $currentPosition['CountryNameCode']; - $address->country = $currentPosition['CountryName']; - } - if (isset($currentPosition['AdministrativeArea'])) { - $currentPosition = $currentPosition['AdministrativeArea']; - $address->administrativeAreaName = $currentPosition['AdministrativeAreaName']; - } - if (isset($currentPosition['SubAdministrativeArea'])) { - $currentPosition = $currentPosition['SubAdministrativeArea']; - $address->subAdministrativeAreaName = $currentPosition['SubAdministrativeAreaName']; - } - if (isset($currentPosition['Locality'])) { - $currentPosition = $currentPosition['Locality']; - $address->localityName = $currentPosition['LocalityName']; - } - if (isset($currentPosition['Thoroughfare'])) { - $address->thoroughfareName = $currentPosition['Thoroughfare']['ThoroughfareName']; - } - if (isset($currentPosition['PostalCode'])) { - $address->postalCode = $currentPosition['PostalCode']['PostalCodeNumber']; - } - - // Gets coordinates. - if (isset($geocodedData['Point']['coordinates'][0])) { - $address->latitude = $geocodedData['Point']['coordinates'][0]; - } - if (isset($geocodedData['Point']['coordinates'][1])) { - $address->longitude = $geocodedData['Point']['coordinates'][1]; - } - if (isset($geocodedData['ExtendedData']['LatLonBox']['north'])) { - $address->north = $geocodedData['ExtendedData']['LatLonBox']['north']; - } - if (isset($geocodedData['ExtendedData']['LatLonBox']['south'])) { - $address->south = $geocodedData['ExtendedData']['LatLonBox']['south']; - } - if (isset($geocodedData['ExtendedData']['LatLonBox']['east'])) { - $address->east = $geocodedData['ExtendedData']['LatLonBox']['east']; - } - if (isset($geocodedData['ExtendedData']['LatLonBox']['west'])) { - $address->west = $geocodedData['ExtendedData']['LatLonBox']['west']; - } + private function fillAddressWithGeocoding(Address $address, $geocodedData, $isLocal) { + $address->types = implode(',', $geocodedData['types']); + $address->formatted_address = $geocodedData['formatted_address']; + $address->components = $geocodedData['address_components']; + $address->latitude = $geocodedData['geometry']['location']['lat']; + $address->longitude = $geocodedData['geometry']['location']['lng']; + $address->southwest_latitude = $geocodedData['geometry']['viewport']['southwest']['lat']; + $address->southwest_longitude = $geocodedData['geometry']['viewport']['southwest']['lng']; + $address->northeast_latitude = $geocodedData['geometry']['viewport']['northeast']['lat']; + $address->northeast_longitude = $geocodedData['geometry']['viewport']['northeast']['lng']; + $address->location_type = $geocodedData['geometry']['location_type']; + $address->partial_match = isset($geocodedData['partial_match']) ? true : false; } // Formats the text of the geocoded address using the unused data and // compares it to the given address. If they are too different, the user // will be asked to choose between them. - private function formatAddress(Address &$address, $extraLines) { - $same = true; - if ($extraLines) { - $address->geocodedText = $extraLines . "\n" . $address->geocodedText; - } - $address->geocodedPostalText = $this->getPostalAddress($address->geocodedText); - $geoloc = strtoupper(preg_replace(array("/[0-9,\"'#~:;_\- ]/", "/\r\n/"), - array('', "\n"), $address->geocodedText)); - $text = strtoupper(preg_replace(array("/[0-9,\"'#~:;_\- ]/", "/\r\n/"), - array('', "\n"), $address->text)); - $arrayGeoloc = explode("\n", $geoloc); - $arrayText = explode("\n", $text); - $countGeoloc = count($arrayGeoloc); - $countText = count($arrayText); - - $totalDistance = 0; - if (($countText > $countGeoloc) || ($countText < $countGeoloc - 1) - || (($countText == $countGeoloc - 1) - && ($arrayText[$countText - 1] == strtoupper($address->country)))) { - $same = false; - } else { - for ($i = 0; $i < $countGeoloc && $i < $countText; ++$i) { - $lineDistance = levenshtein($arrayText[$i], trim($arrayGeoloc[$i])); - $totalDistance += $lineDistance; - if ($lineDistance > self::MAX_LINE_DISTANCE || $totalDistance > self::MAX_TOTAL_DISTANCE) { - $same = false; + private function formatAddress(Address $address, $extraLines, $forceLanguage) + { + /* XXX: Check how to integrate this in the new geocoding system. + if (!$forceLanguage) { + $languages = XDB::fetchOneCell('SELECT IF(ISNULL(gc1.belongsTo), gl1.language, gl2.language) + FROM geoloc_countries AS gc1 + INNER JOIN geoloc_languages AS gl1 ON (gc1.iso_3166_1_a2 = gl1.iso_3166_1_a2) + LEFT JOIN geoloc_countries AS gc2 ON (gc1.belongsTo = gc2.iso_3166_1_a2) + LEFT JOIN geoloc_languages AS gl2 ON (gc2.iso_3166_1_a2 = gl2.iso_3166_1_a2) + WHERE gc1.iso_3166_1_a2 = {?}', + $address->countryId); + $toGeocode = substr($address->text, strlen($extraLines)); + foreach (explode(',', $languages) as $language) { + if ($language != Platal::globals()->geocoder->gmaps_language) { + $geocodedData = $this->getPlacemarkForAddress($toGeocode, $language); + $this->fillAddressWithGeocoding($address, $geocodedData, true); break; } } - } - - if ($same) { - $address->geocodedText = null; - $address->geocodedPostalText = null; - } else { - $address->geocodedText = str_replace("\n", "\r\n", $address->geocodedText); - $address->geocodedPostalText = str_replace("\n", "\r\n", $address->geocodedPostalText); - } + }*/ $address->text = str_replace("\n", "\r\n", $address->text); - $address->postalText = str_replace("\n", "\r\n", $address->postalText); - } - - // Returns the address formated for postal use. - // The main rules are (cf AFNOR XPZ 10-011): - // -everything in upper case; - // -if there are more then than 38 characters in a line, split it; - // -if there are more then than 32 characters in the description of the "street", use abbreviations. - private function getPostalAddress($text) { - static $abbreviations = array( - 'IMPASSE' => 'IMP', - 'RUE' => 'R', - 'AVENUE' => 'AV', - 'BOULEVARD' => 'BVD', - 'ROUTE' => 'R', - 'STREET' => 'ST', - 'ROAD' => 'RD', - ); - - $text = strtoupper($text); - $arrayText = explode("\n", $text); - $postalText = ''; - - foreach ($arrayText as $i => $line) { - $postalText .= (($i == 0) ? '' : "\n"); - if (($length = strlen($line)) > 32) { - $words = explode(' ', $line); - $count = 0; - foreach ($words as $word) { - if (isset($abbreviations[$word])) { - $word = $abbreviations[$word]; - } - if ($count + ($wordLength = strlen($word)) <= 38) { - $postalText .= (($count == 0) ? '' : ' ') . $word; - $count += (($count == 0) ? 0 : 1) + $wordLength; - } else { - $postalText .= "\n" . $word; - $count = strlen($word); - } - } - } else { - $postalText .= $line; - } - } - return $postalText; } // Trims the name of the real country if it contains an ISO 3166-1 non-country @@ -332,7 +231,7 @@ class GMapsGeocoder extends Geocoder { // all non-country items of ISO 3166-1. private function getTextToGeocode($text) { - $res = XDB::iterator('SELECT country, countryFR + $res = XDB::iterator('SELECT countryEn, country FROM geoloc_countries WHERE belongsTo IS NOT NULL'); $countries = array(); @@ -344,10 +243,12 @@ class GMapsGeocoder extends Geocoder { $countLines = count($textLines); $needle = strtoupper(trim($textLines[$countLines - 2])); $isPseudoCountry = false; - foreach ($countries as $country) { - if (strtoupper($country) == $needle) { - $isPseudoCountry = true; - break; + if ($needle) { + foreach ($countries as $country) { + if (strtoupper($country) === $needle) { + $isPseudoCountry = true; + break; + } } }