Changes geocoding engine to gmaps v3.
authorStéphane Jacob <sj@m4x.org>
Fri, 6 May 2011 08:56:14 +0000 (10:56 +0200)
committerStéphane Jacob <sj@m4x.org>
Tue, 10 May 2011 15:44:41 +0000 (17:44 +0200)
Signed-off-by: Stéphane Jacob <sj@m4x.org>
classes/address.php
classes/geocoder.php
classes/gmapsgeocoder.php
configs/mails.conf
configs/platal.ini
templates/geoloc/form.address.tpl
templates/profile/geocoding.mail.tpl
upgrade/1.1.2/01_geocoding.sql [new file with mode: 0644]

index fb4eb44..1b15158 100644 (file)
@@ -296,26 +296,20 @@ class Address
     public $id = 0;
 
     // Geocoding fields.
-    public $accuracy = 0;
     public $text = '';
     public $postalText = '';
-    public $postalCode = null;
-    public $localityId = null;
-    public $subAdministrativeAreaId = null;
-    public $administrativeAreaId = null;
-    public $localityName = null;
-    public $subAdministrativeAreaName = null;
-    public $administrativeAreaName = null;
-    public $localityNameLocal = null;
-    public $subAdministrativeAreaNameLocal = null;
-    public $administrativeAreaNameLocal = null;
-    public $countryId = null;
+    public $types = '';
+    public $formatted_address = '';
+    public $components = array();
     public $latitude = null;
     public $longitude = null;
-    public $north = null;
-    public $south = null;
-    public $east = null;
-    public $west = null;
+    public $southwest_latitude = null;
+    public $southwest_longitude = null;
+    public $northeast_latitude = null;
+    public $northeast_longitude = null;
+    public $location_type = '';
+    public $partial_match = false;
+    public $componentsIds = '';
 
     // Database's field required for both 'home' and 'job' addresses.
     public $pub = 'ax';
@@ -557,22 +551,18 @@ class Address
         // country, then apply corresponding formatting or translate country
         // into default language.
         $count = count($arrayText);
-        if (in_array(strtoupper($this->countryId), Address::$formattings)) {
-            $text = call_user_func(array($this, 'formatPostalAddress' . strtoupper($this->countryId)), $arrayText);
+        list($countryId, $country) = XDB::fetchOneRow('SELECT  gc.iso_3166_1_a2, gc.country
+                                                         FROM  geoloc_countries AS gc
+                                                   INNER JOIN  geoloc_languages AS gl ON (gc.iso_3166_1_a2 = gl.iso_3166_1_a2)
+                                                        WHERE  gl.countryPlain = {?} OR gc.countryPlain = {?}',
+                                                      $arrayText[$count - 1], $arrayText[$count - 1]);
+        if (is_null($countryId)) {
+            $text = $this->formatPostalAddressFR($arrayText);
+        } elseif (in_array(strtoupper($countryId), Address::$formattings)) {
+            $text = call_user_func(array($this, 'formatPostalAddress' . strtoupper($countryId)), $arrayText);
         } else {
-            list($countryId, $country) = XDB::fetchOneRow('SELECT  gc.iso_3166_1_a2, gc.country
-                                                             FROM  geoloc_countries AS gc
-                                                       INNER JOIN  geoloc_languages AS gl ON (gc.iso_3166_1_a2 = gl.iso_3166_1_a2)
-                                                            WHERE  gc.iso_3166_1_a2 = {?} OR gl.countryPlain = {?} OR gc.countryPlain = {?}',
-                                                          $this->countryId, $arrayText[$count - 1], $arrayText[$count - 1]);
-            if (is_null($countryId)) {
-                $text = $this->formatPostalAddressFR($arrayText);
-            } elseif (in_array(strtoupper($countryId), Address::$formattings)) {
-                $text = call_user_func(array($this, 'formatPostalAddress' . strtoupper($countryId)), $arrayText);
-            } else {
-                $arrayText[$count - 1] = mb_strtoupper(replace_accent($country));
-                $text = implode("\n", $arrayText);
-            }
+            $arrayText[$count - 1] = mb_strtoupper(replace_accent($country));
+            $text = implode("\n", $arrayText);
         }
 
         $this->postalText = $text;
@@ -596,12 +586,12 @@ class Address
             $gmapsGeocoder = new GMapsGeocoder();
             $gmapsGeocoder->getGeocodedAddress($this);
         }
-        foreach (array('administrativeArea', 'subAdministrativeArea', 'locality') as $area) {
-            Geocoder::getAreaId($this, $area);
-        }
-        if ($this->countryId == '') {
-            $this->countryId = null;
+
+        $componants = array();
+        foreach ($this->components as $component) {
+            $componants[] = Geocoder::getComponentId($component);
         }
+        $this->componentsIds = implode(',', $componants);
 
         return true;
     }
@@ -609,29 +599,19 @@ class Address
     public function toFormArray()
     {
         $address = array(
-            'accuracy'                       => $this->accuracy,
-            'text'                           => $this->text,
-            'postalText'                     => $this->postalText,
-            'postalCode'                     => $this->postalCode,
-            'localityId'                     => $this->localityId,
-            'subAdministrativeAreaId'        => $this->subAdministrativeAreaId,
-            'administrativeAreaId'           => $this->administrativeAreaId,
-            'countryId'                      => $this->countryId,
-            'localityName'                   => $this->localityName,
-            'subAdministrativeAreaName'      => $this->subAdministrativeAreaName,
-            'administrativeAreaName'         => $this->administrativeAreaName,
-            'localityNameLocal'              => $this->localityNameLocal,
-            'subAdministrativeAreaNameLocal' => $this->subAdministrativeAreaNameLocal,
-            'administrativeAreaNameLocal'    => $this->administrativeAreaNameLocal,
-            'latitude'                       => $this->latitude,
-            'longitude'                      => $this->longitude,
-            'north'                          => $this->north,
-            'south'                          => $this->south,
-            'east'                           => $this->east,
-            'west'                           => $this->west,
-            'error'                          => $this->error,
-            'changed'                        => $this->changed,
-            'removed'                        => $this->removed,
+            'text'                => $this->text,
+            'postalText'          => $this->postalText,
+            'types'               => $this->types,
+            'formatted_address'   => $this->formatted_address,
+            'latitude'            => $this->latitude,
+            'longitude'           => $this->longitude,
+            'southwest_latitude'  => $this->southwest_latitude,
+            'southwest_longitude' => $this->southwest_longitude,
+            'northeast_latitude'  => $this->northeast_latitude,
+            'northeast_longitude' => $this->northeast_longitude,
+            'location_type'       => $this->location_type,
+            'partial_match'       => $this->partial_match,
+            'componentsIds'       => $this->componentsIds
         );
 
         if ($this->type == self::LINK_PROFILE || $this->type == self::LINK_JOB) {
@@ -639,7 +619,6 @@ class Address
         }
         if ($this->type == self::LINK_PROFILE) {
             static $flags = array('current', 'temporary', 'secondary', 'mail', 'cedex', 'deliveryIssue');
-
             foreach ($flags as $flag) {
                 $address[$flag] = $this->flags->hasFlag($flag);
             }
@@ -698,19 +677,22 @@ class Address
     public function save()
     {
         if (!$this->isEmpty()) {
-            XDB::execute('INSERT IGNORE INTO  profile_addresses (pid, jobid, groupid, type, id, flags, accuracy,
-                                                                 text, postalText, postalCode, localityId,
-                                                                 subAdministrativeAreaId, administrativeAreaId,
-                                                                 countryId, latitude, longitude, pub, comment,
-                                                                 north, south, east, west)
-                                      VALUES  ({?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?},
-                                               {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?})',
-                         $this->pid, $this->jobid, $this->groupid, $this->type, $this->id, $this->flags, $this->accuracy,
-                         $this->text, $this->postalText, $this->postalCode, $this->localityId,
-                         $this->subAdministrativeAreaId, $this->administrativeAreaId,
-                         $this->countryId, $this->latitude, $this->longitude,
-                         $this->pub, $this->comment,
-                         $this->north, $this->south, $this->east, $this->west);
+            XDB::execute('INSERT IGNORE INTO  profile_addresses (pid, jobid, groupid, type, id, flags, text, postalText, pub, comment,
+                                                                 types, formatted_address, location_type, partial_match, latitude, longitude,
+                                                                 southwest_latitude, southwest_longitude, northeast_latitude, northeast_longitude)
+                                      VALUES  ({?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?},
+                                               {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?})',
+                         $this->pid, $this->jobid, $this->groupid, $this->type, $this->id, $this->flags, $this->text, $this->postalText, $this->pub, $this->comment,
+                         $this->types, $this->formatted_address, $this->location_type, $this->partial_match, $this->latitude, $this->longitude,
+                         $this->southwest_latitude, $this->southwest_longitude, $this->northeast_latitude, $this->northeast_longitude);
+
+            if ($this->componentsIds) {
+                foreach (explode(',', $this->componentsIds) as $component_id) {
+                    XDB::execute('INSERT IGNORE INTO  profile_addresses_components (pid, jobid, groupid, type, id, component_id)
+                                              VALUES  ({?}, {?}, {?}, {?}, {?}, {?})',
+                                 $this->pid, $this->jobid, $this->groupid, $this->type, $this->id, $component_id);
+                }
+            }
 
             if ($this->type == self::LINK_PROFILE) {
                 Phone::savePhones($this->phones, $this->pid, Phone::LINK_ADDRESS, $this->id);
@@ -875,22 +857,15 @@ class AddressIterator implements PlIterator
         if (count($pubs) != 0) {
             $where[] = XDB::format('(pa.pub IN {?})', $pubs);
         }
-        $sql = 'SELECT  pa.pid, pa.jobid, pa.type, pa.id, pa.flags,
-                        pa.accuracy, pa.text, pa.postalText, pa.postalCode,
-                        pa.localityId, pa.subAdministrativeAreaId,
-                        pa.administrativeAreaId, pa.countryId,
-                        pa.latitude, pa.longitude, pa.north, pa.south, pa.east, pa.west,
-                        pa.pub, pa.comment,
-                        gl.name AS locality, gl.nameLocal AS localityLocal,
-                        gs.name AS subAdministrativeArea, gs.nameLocal AS subAdministrativeAreaLocal,
-                        ga.name AS administrativeArea, ga.nameLocal AS administrativeAreaLocal,
-                        gc.country
-                  FROM  profile_addresses             AS pa
-             LEFT JOIN  geoloc_localities             AS gl ON (gl.id = pa.localityId)
-             LEFT JOIN  geoloc_administrativeareas    AS ga ON (ga.id = pa.administrativeAreaId)
-             LEFT JOIN  geoloc_subadministrativeareas AS gs ON (gs.id = pa.subAdministrativeAreaId)
-             LEFT JOIN  geoloc_countries              AS gc ON (gc.iso_3166_1_a2 = pa.countryId)
+        $sql = 'SELECT  pa.pid, pa.jobid, pa.groupid, pa.type, pa.id, pa.flags, pa.text, pa.postalText, pa.pub, pa.comment,
+                        pa.types, pa.formatted_address, pa.location_type, pa.partial_match, pa.latitude, pa.longitude,
+                        pa.southwest_latitude, pa.southwest_longitude, pa.northeast_latitude, pa.northeast_longitude,
+                        GROUP_CONCAT(DISTINCT pc.component_id SEPARATOR \',\') AS componentsIds
+                  FROM  profile_addresses            AS pa
+             LEFT JOIN  profile_addresses_components AS pc ON (pa.pid = pc.pid AND pa.jobid = pc.jobid AND pa.groupid = pc.groupid
+                                                               AND pa.type = pc.type AND pa.id = pc.id)
                  ' . ((count($where) > 0) ? 'WHERE  ' . implode(' AND ', $where) : '') . '
+              GROUP BY  pa.pid, pa.jobid, pa.groupid, pa.type, pa.id
               ORDER BY  pa.pid, pa.jobid, pa.id';
         $this->dbiter = XDB::iterator($sql);
     }
index 825b920..f404304 100644 (file)
@@ -29,54 +29,26 @@ abstract class Geocoder {
     // Cleans the address from its geocoded data
     abstract public function stripGeocodingFromAddress(Address $address);
 
-    // Updates geoloc_administrativeareas, geoloc_subadministrativeareas and
-    // geoloc_localities databases with new geocoded data and returns the
-    // corresponding id.
-    static public function getAreaId(Address $address, $area)
+    // Updates profile_addresses_components_enum, if needed, with new
+    // geocoded data and returns the corresponding id.
+    static public function getComponentId(array $component)
     {
-        static $databases = array(
-            'administrativeArea'    => 'geoloc_administrativeareas',
-            'subAdministrativeArea' => 'geoloc_subadministrativeareas',
-            'locality'              => 'geoloc_localities',
-        );
-        static $extras = array(
-            'subAdministrativeArea' => array(
-                'field' => 'administrativearea',
-                'name'  => 'administrativeAreaName'
-            )
-        );
-
-        $areaName = $area . 'Name';
-        $areaNameLocal = $areaName . 'Local';
-        $areaId = $area . 'Id';
-        if (!is_null($address->$areaName) && isset($databases[$area])) {
-            $extra = (isset($extras[$area]) ? $extras[$area]['administrativeAreaName'] : false);
+        $where_types = array();
+        foreach ($component['types'] as $type) {
+            $where_types[] = XDB::format('FIND_IN_SET({?}, types)', $type);
+        }
 
-            $res = XDB::query('SELECT  id, nameLocal
-                                 FROM  ' . $databases[$area] . '
-                                WHERE  name = {?}',
-                              $address->$areaName);
-            if ($res->numRows() == 0) {
-                XDB::execute('INSERT INTO  ' . $databases[$area] . ' (name, nameLocal, country' .
-                                           ($extra ? ', ' . $extras[$area]['field'] : '') . ')
-                                   VALUES  ({?}, {?}, {?}' . ($extra ? ', {?}' : '') . ')',
-                             $address->$areaName, $address->$areaNameLocal, $address->countryId,
-                             ($extra ? $address->$extra : null));
-                $address->$areaId = XDB::insertId();
-            } else {
-                // XXX: remove this once all areas have both nameLocal and name.
-                list($id, $name) = $res->fetchOneRow();
-                if (is_null($name) && !is_null($address->$areaNameLocal)) {
-                    XDB::execute('UPDATE  ' . $databases[$area] . '
-                                     SET  nameLocal = {?}
-                                   WHERE  id = {?}',
-                                 $address->$areaNameLocal, $id);
-                }
-                $address->$areaId = $id;
-            }
-        } elseif (empty($address->$areaId)) {
-            $address->$areaId = null;
+        $id = XDB::fetchOneCell('SELECT  id
+                                   FROM  profile_addresses_components_enum
+                                  WHERE  short_name = {?} AND long_name = {?} AND ' . implode(' AND ', $where_types),
+                                $component['short_name'], $component['long_name']);
+        if (is_null($id)) {
+            XDB::execute('INSERT INTO  profile_addresses_components_enum (short_name, long_name, types)
+                               VALUES  ({?}, {?}, {?})',
+                         $component['short_name'], $component['long_name'], implode(',', $component['types']));
+            $id = XDB::insertId();
         }
+        return $id;
     }
 
     // Returns the part of the text preceeding the line with the postal code
@@ -101,24 +73,6 @@ abstract class Geocoder {
         }
         return $firstLines;
     }
-
-    // Returns the number of non geocoded addresses for a user.
-    static public function countNonGeocoded($pid, $jobid = null, $type = Address::LINK_PROFILE)
-    {
-        $where = array();
-        if (!is_null($pid)) {
-            $where[] = XDB::format('pid = {?}', $pid);
-        }
-        if (!is_null($jobid)) {
-            $where[] = XDB::format('jobid = {?}', $jobid);
-        }
-        $where[] = XDB::format('FIND_IN_SET({?}, type) AND accuracy = 0', $type);
-        $res = XDB::query('SELECT  COUNT(*)
-                             FROM  profile_addresses
-                            WHERE  ' . implode(' AND ', $where),
-                          $pid);
-        return $res->fetchOneCell();
-    }
 }
 
 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
index ec42adf..9378c1b 100644 (file)
  *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA                *
  ***************************************************************************/
 
-// Implementation of a Geocoder using the Google Maps API. Please refer to
-// the following links for details:
-// http://code.google.com/apis/maps/documentation/services.html#Geocoding
-// http://code.google.com/intl/en/apis/maps/documentation/geocoding/
-// http://code.google.com/apis/maps/documentation/reference.html#GGeoAddressAccuracy
+// Implementation of a Geocoder using the Google Maps API v3. Please refer
+// to the following link for details:
+// http://code.google.com/apis/maps/documentation/geocoding/
 //
-// It requires the properties gmaps_key and gmaps_url to be defined in section
-// Geocoder in plat/al's configuration (platal.ini & platal.conf).
+// It requires the properties gmaps_url to be defined in section Geocoder
+// in plat/al's configuration (platal.ini & platal.conf).
 class GMapsGeocoder extends Geocoder {
 
     // Maximum number of Geocoding calls to the Google Maps API.
     const MAX_GMAPS_RPC_CALLS = 5;
-    // Maximum levenshtein distance authorized between input and geocoded text in a single line.
-    const MAX_LINE_DISTANCE = 5;
-    // Maximum levenshtein distance authorized between input and geocoded text in the whole text.
-    const MAX_TOTAL_DISTANCE = 6;
 
     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_hl;
+            $defaultLanguage = Platal::globals()->geocoder->gmaps_language;
         }
 
         // Try to geocode the full address.
@@ -66,16 +60,16 @@ class GMapsGeocoder extends Geocoder {
     }
 
     public function stripGeocodingFromAddress(Address $address) {
-        $address->geocodedText = 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;
+        $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
@@ -92,7 +86,7 @@ class GMapsGeocoder extends Geocoder {
         $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
@@ -106,16 +100,13 @@ class GMapsGeocoder extends Geocoder {
         global $globals;
 
         $parameters = array(
-            'key'    => $globals->geocoder->gmaps_key,
-            'sensor' => 'false',   // The queried address wasn't obtained from a GPS sensor.
-            'hl'     => $defaultLanguage,
-            'oe'     => 'utf8',    // Output encoding.
-            'output' => 'json',    // Output format.
-            'gl'     => $globals->geocoder->gmaps_gl,
-            '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.
@@ -152,131 +143,43 @@ 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.
+    // 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;
         }
 
-        // Check that at least one placemark was found.
-        if (count($data['Placemark']) == 0) {
-            return null;
+        // If there are results return the first one.
+        if ($status == 'OK') {
+            return $data['results'][0];
         }
 
-        // 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;
-            }
-        }
-
-        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, $isLocal) {
-        // 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
-        if ($isLocal) {
-            $ext = 'Local';
-        } else {
-            $ext = ucfirst(Platal::globals()->geocoder->gmaps_hl);
-            $address->geocodedText = str_replace(', ', "\n", $geocodedData['address']);
-        }
-
-        if (isset($geocodedData['AddressDetails']['Accuracy'])) {
-            $address->accuracy = $geocodedData['AddressDetails']['Accuracy'];
-        }
-
-        $currentPosition = $geocodedData['AddressDetails'];
-        if (isset($currentPosition['Country'])) {
-            $country = 'country' . $ext;
-            $currentPosition    = $currentPosition['Country'];
-            $address->countryId = $currentPosition['CountryNameCode'];
-            $address->$country  = $currentPosition['CountryName'];
-        }
-        if (isset($currentPosition['AdministrativeArea'])) {
-            $administrativeAreaName = 'administrativeAreaName' . $ext;
-            $currentPosition                  = $currentPosition['AdministrativeArea'];
-            $address->$administrativeAreaName = $currentPosition['AdministrativeAreaName'];
-        }
-        if (isset($currentPosition['SubAdministrativeArea'])) {
-            $subAdministrativeAreaName = 'subAdministrativeAreaName' . $ext;
-            $currentPosition                     = $currentPosition['SubAdministrativeArea'];
-            $address->$subAdministrativeAreaName = $currentPosition['SubAdministrativeAreaName'];
-        }
-        if (isset($currentPosition['Locality'])) {
-            $localityName = 'localityName' . $ext;
-            $currentPosition        = $currentPosition['Locality'];
-            $address->$localityName = $currentPosition['LocalityName'];
-        }
-        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'];
-        }
-    }
-
-    // Compares the geocoded address with the given address and returns true
-    // iff their are close enough to be considered as equals or not.
-    private function compareAddress($address)
-    {
-        $same = true;
-        $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;
-                    break;
-                }
-            }
-        }
-
-        return $same;
+        $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
@@ -284,13 +187,8 @@ class GMapsGeocoder extends Geocoder {
     // will be asked to choose between them.
     private function formatAddress(Address $address, $extraLines, $forceLanguage)
     {
-        if ($extraLines) {
-            $address->geocodedText = $extraLines . "\n" . $address->geocodedText;
-        }
-
-        if ($this->compareAddress($address)) {
-            $address->geocodedText = null;
-        } elseif (!$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)
@@ -300,21 +198,13 @@ class GMapsGeocoder extends Geocoder {
                                            $address->countryId);
             $toGeocode = substr($address->text, strlen($extraLines));
             foreach (explode(',', $languages) as $language) {
-                if ($language != Platal::globals()->geocoder->gmaps_hl) {
+                if ($language != Platal::globals()->geocoder->gmaps_language) {
                     $geocodedData = $this->getPlacemarkForAddress($toGeocode, $language);
-                    $address->geocodedText = str_replace(', ', "\n", $geocodedData['address']);
-                    if ($extraLines) {
-                        $address->geocodedText = $extraLines . "\n" . $address->geocodedText;
-                    }
-                    if ($this->compareAddress($address)) {
-                        $this->fillAddressWithGeocoding($address, $geocodedData, true);
-                        $address->geocodedText = null;
-                        break;
-                    }
+                    $this->fillAddressWithGeocoding($address, $geocodedData, true);
+                    break;
                 }
             }
-            $address->geocodedText = str_replace("\n", "\r\n", $address->geocodedText);
-        }
+        }*/
         $address->text = str_replace("\n", "\r\n", $address->text);
     }
 
index d17d066..06fc744 100644 (file)
@@ -34,7 +34,7 @@ to=hotliners@staff.polytechnique.org
 
 [geoloc_error]
 from=webmaster@polytechnique.org
-to=non-geoloc@staff.polytechnique.org
+to=br@staff.polytechnique.org
 
 [mails_ax]
 from="Amicale des Anciens de l'X" <reponses@amicale.polytechnique.org>
index a057415..f4e7e78 100644 (file)
@@ -216,21 +216,17 @@ event_reply = ""
 ; Unused parameter.
 email = ""
 
-; $globals->geocoder->gmaps_key
-; API key to use when querying google maps web service
-gmaps_key = ""
-
 ; $globals->geocoder->gmaps_url
 ; URL of geocoding webservice
-gmaps_url = "http://maps.google.com/maps/geo"
+gmaps_url = "https://maps.googleapis.com/maps/api/geocode/"
 
-; $globals->geocoder->gmaps_hl
+; $globals->geocoder->gmaps_language
 ; Default output language.
-gmaps_hl = "fr"
+gmaps_language = "fr"
 
-; $globals->geocoder->gmaps_gl
+; $globals->geocoder->gmaps_region
 ; Default location preference.
-gmaps_gl = "fr"
+gmaps_region = "fr"
 
 
 ; The lists section contains parameters used to interact with mailman.
index a41cd4f..033c3c0 100644 (file)
 <tr{if t($class)} class="{$class}"{/if}>
   <td>
     <textarea name="{$prefname}[text]" cols="30" rows="4" onkeyup="addressChanged('{$prefid}')">{$address.text}</textarea>
-    <input type="hidden" name="{$prefname}[accuracy]" value="{$address.accuracy}" />
     <input type="hidden" name="{$prefname}[postalText]" value="{$address.postalText}" />
-    <input type="hidden" name="{$prefname}[postalCode]" value="{$address.postalCode}" />
-    <input type="hidden" name="{$prefname}[administrativeAreaId]" value="{$address.administrativeAreaId}" />
-    <input type="hidden" name="{$prefname}[subAdministrativeAreaId]" value="{$address.subAdministrativeAreaId}" />
-    <input type="hidden" name="{$prefname}[localityId]" value="{$address.localityId}" />
-    <input type="hidden" name="{$prefname}[countryId]" value="{$address.countryId}" />
+    <input type="hidden" name="{$prefname}[types]" value="{$address.types}" />
+    <input type="hidden" name="{$prefname}[formatted_address]" value="{$address.formatted_address}" />
     <input type="hidden" name="{$prefname}[latitude]" value="{$address.latitude}" />
     <input type="hidden" name="{$prefname}[longitude]" value="{$address.longitude}" />
-    <input type="hidden" name="{$prefname}[north]" value="{$address.north}" />
-    <input type="hidden" name="{$prefname}[south]" value="{$address.south}" />
-    <input type="hidden" name="{$prefname}[east]" value="{$address.east}" />
-    <input type="hidden" name="{$prefname}[west]" value="{$address.west}" />
+    <input type="hidden" name="{$prefname}[southwest_latitude]" value="{$address.southwest_latitude}" />
+    <input type="hidden" name="{$prefname}[southwest_longitude]" value="{$address.southwest_longitude}" />
+    <input type="hidden" name="{$prefname}[northeast_latitude]" value="{$address.northeast_latitude}" />
+    <input type="hidden" name="{$prefname}[northeast_longitude]" value="{$address.northeast_longitude}" />
+    <input type="hidden" name="{$prefname}[location_type]" value="{$address.location_type}" />
+    <input type="hidden" name="{$prefname}[partial_match]" value="{$address.partial_match}" />
+    <input type="hidden" name="{$prefname}[componentsIds]" value="{$address.componentsIds}" />
     <input type="hidden" name="{$prefname}[changed]" value="0" />
     <input type="hidden" name="{$prefname}[removed]" value="0" />
   </td>
   <td>
   {if t($address.latitude)}
-    <img src="https://maps.googleapis.com/maps/api/staticmap?size=300x100&amp;markers=color:{$profile->promoColor()}%7C{$address.longitude},{$address.latitude}&amp;zoom=12&amp;sensor=false"
+    <img src="https://maps.googleapis.com/maps/api/staticmap?size=300x100&amp;markers=color:{$profile->promoColor()}%7C{$address.latitude},{$address.longitude}&amp;zoom=12&amp;sensor=false"
          alt="Position de l'adresse" />
     <br />
     <small><a href="javascript:deleteGeocoding()">{icon name=cross title="Adresse mal localisée"} Signaler que le repère est mal placé</a></small>
index 1637b19..5df456d 100644 (file)
 {if $mail_part eq 'head'}
 {from full=#from#}
 {to addr=#to#}
-{subject text="Adresse impossible à geolocaliser"}
+{subject text="Erreur de géolocalisation"}
 {elseif $mail_part eq 'wiki'}
-Un camarade {$smarty.session.hruid} a forcé l'utilisation de l'adresse entrée
-à la main et a refusé la version geolocalisée :
+La requête de géocodage générée par {$smarty.session.hruid} et dont l'url
+est la suivante :\\
+{$url}\\
+a reçu le status d'erreur suivant :\\
+{$status}
 
-'''Version utilisateur (validée) :'''\\
-{$text}
-
-'''Version geoloc (refusée) :'''\\
-{$geoloc}
 {include file="include/signature.mail.tpl"}
 {/if}
 
diff --git a/upgrade/1.1.2/01_geocoding.sql b/upgrade/1.1.2/01_geocoding.sql
new file mode 100644 (file)
index 0000000..b89083d
--- /dev/null
@@ -0,0 +1,59 @@
+DROP TABLE IF EXISTS profile_addresses_components;
+DROP TABLE IF EXISTS profile_addresses_components_enum;
+
+CREATE TABLE profile_addresses_components_enum (
+  id BIGINT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+  short_name VARCHAR(255) NOT NULL DEFAULT '',
+  long_name VARCHAR(255) NOT NULL DEFAULT '',
+  types SET('street_address', 'route', 'intersection', 'political', 'country', 'administrative_area_level_1', 'administrative_area_level_2', 'administrative_area_level_3', 'colloquial_area', 'locality', 'sublocality', 'neighborhood', 'premise', 'subpremise', 'postal_code', 'natural_feature', 'airport', 'park', 'point_of_interest', 'post_box', 'street_number', 'floor', 'room') NOT NULL DEFAULT '',
+  PRIMARY KEY (id),
+  UNIQUE KEY (types, long_name),
+  KEY (types, short_name)
+) ENGINE=InnoDB, CHARSET=utf8;
+
+CREATE TABLE profile_addresses_components (
+  pid INT(11) UNSIGNED NOT NULL DEFAULT 0,
+  jobid INT(6) UNSIGNED NOT NULL DEFAULT 0,
+  groupid SMALLINT(5) UNSIGNED NOT NULL DEFAULT 0,
+  type ENUM('home', 'job', 'hq', 'group') NOT NULL DEFAULT 'home',
+  id TINYINT(3) UNSIGNED NOT NULL DEFAULT 0,
+  component_id BIGINT(10) UNSIGNED NOT NULL DEFAULT 0,
+  PRIMARY KEY (pid, jobid, groupid, type, id, component_id),
+  KEY(component_id),
+  FOREIGN KEY (pid, jobid, groupid, type, id) REFERENCES profile_addresses (pid, jobid, groupid, type, id) ON UPDATE CASCADE ON DELETE CASCADE,
+  FOREIGN KEY (component_id) REFERENCES profile_addresses_components_enum (id) ON UPDATE CASCADE ON DELETE CASCADE
+) ENGINE=InnoDB, CHARSET=utf8;
+
+ALTER TABLE profile_addresses DROP FOREIGN KEY profile_addresses_ibfk_1;
+ALTER TABLE profile_addresses DROP FOREIGN KEY profile_addresses_ibfk_2;
+ALTER TABLE profile_addresses DROP FOREIGN KEY profile_addresses_ibfk_3;
+ALTER TABLE profile_addresses DROP FOREIGN KEY profile_addresses_ibfk_4;
+
+ALTER TABLE profile_addresses DROP COLUMN accuracy;
+ALTER TABLE profile_addresses DROP COLUMN postalCode;
+ALTER TABLE profile_addresses DROP COLUMN localityId;
+ALTER TABLE profile_addresses DROP COLUMN subAdministrativeAreaId;
+ALTER TABLE profile_addresses DROP COLUMN administrativeAreaId;
+ALTER TABLE profile_addresses DROP COLUMN countryId;
+ALTER TABLE profile_addresses DROP COLUMN north;
+ALTER TABLE profile_addresses DROP COLUMN south;
+ALTER TABLE profile_addresses DROP COLUMN east;
+ALTER TABLE profile_addresses DROP COLUMN west;
+
+ALTER TABLE profile_addresses ADD COLUMN formatted_address TEXT NOT NULL DEFAULT '' AFTER postalText;
+ALTER TABLE profile_addresses ADD COLUMN types SET('street_address', 'route', 'intersection', 'political', 'country', 'administrative_area_level_1', 'administrative_area_level_2', 'administrative_area_level_3', 'colloquial_area', 'locality', 'sublocality', 'neighborhood', 'premise', 'subpremise', 'postal_code', 'natural_feature', 'airport', 'park', 'point_of_interest', 'post_box', 'street_number', 'floor', 'room') NOT NULL DEFAULT '' AFTER formatted_address;
+ALTER TABLE profile_addresses ADD COLUMN southwest_latitude FLOAT(10, 7) DEFAULT NULL AFTER longitude;
+ALTER TABLE profile_addresses ADD COLUMN southwest_longitude FLOAT(10, 7) DEFAULT NULL AFTER southwest_latitude;
+ALTER TABLE profile_addresses ADD COLUMN northeast_latitude FLOAT(10, 7) DEFAULT NULL AFTER southwest_longitude;
+ALTER TABLE profile_addresses ADD COLUMN northeast_longitude FLOAT(10, 7) DEFAULT NULL AFTER northeast_latitude;
+ALTER TABLE profile_addresses ADD COLUMN location_type ENUM('ROOFTOP', 'RANGE_INTERPOLATED', 'GEOMETRIC_CENTER', 'APPROXIMATE') DEFAULT NULL AFTER northeast_longitude;
+ALTER TABLE profile_addresses ADD COLUMN partial_match BOOLEAN NOT NULL DEFAULT false AFTER location_type;
+
+UPDATE  profile_addresses
+   SET  latitude = NULL, longitude = NULL;
+
+DROP TABLE IF EXISTS geoloc_administrativeareas;
+DROP TABLE IF EXISTS geoloc_localities;
+DROP TABLE IF EXISTS geoloc_subadministrativeareas;
+
+-- vim:set syntax=mysql: