From d4fff2b5a1c638db490c5de0401318575a5a0c8a Mon Sep 17 00:00:00 2001
From: Ron Shimshock <ron@shimshockgroup.com>
Date: Wed, 8 Jun 2016 15:08:28 -0500
Subject: [PATCH] Geocoder structured format

---
 geocoder.admin.inc                              | 10 ++++++
 plugins/geocoder_handler/google.inc             | 47 +++++++++++++++++++++++--
 plugins/geocoder_handler/mapquest_nominatim.inc | 37 ++++++++++++++++---
 3 files changed, 86 insertions(+), 8 deletions(-)

diff --git a/geocoder.admin.inc b/geocoder.admin.inc
index f94f208..d397683 100644
--- a/geocoder.admin.inc
+++ b/geocoder.admin.inc
@@ -103,6 +103,15 @@ function geocoder_admin_settings($form, &$form_state) {
     ),
   );
 
+  $google_region_url = 'https://developers.google.com/maps/documentation/geocoding/intro#RegionCodes';
+  $form['geocoder_region_google'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Google Region Code'),
+    '#description' => t('This regional context helps Google choose the right location. See !link', array('!link' => l($google_region_url, $google_region_url, array('external' => TRUE)))),
+    '#default_value' => empty($geocoder_settings['geocoder_region_google']) ? '' : $geocoder_settings['geocoder_region_google'],
+    '#required' => FALSE,
+  );
+
   $form['geocoder_google_delay'] = array(
     '#type' => 'textfield',
     '#title' => t('Delay between Google geocoding requests (in milliseconds)'),
@@ -142,5 +151,6 @@ function geocoder_admin_settings_submit($form, &$form_state) {
   $geocoder_settings['geocoder_apikey_bing'] = trim($form_state['values']['geocoder_apikey_bing']);
   $geocoder_settings['geocoder_apikey_mapzen'] = trim($form_state['values']['geocoder_apikey_mapzen']);
   $geocoder_settings['geocoder_apikey_google'] = trim($form_state['values']['geocoder_apikey_google']);
+  $geocoder_settings['geocoder_region_google'] = trim($form_state['values']['geocoder_region_google']);
   variable_set("geocoder_settings", $geocoder_settings);
 }
diff --git a/plugins/geocoder_handler/google.inc b/plugins/geocoder_handler/google.inc
index e676783..cb311fd 100644
--- a/plugins/geocoder_handler/google.inc
+++ b/plugins/geocoder_handler/google.inc
@@ -22,7 +22,7 @@ $plugin = array(
 /**
  * Process Markup
  */
-function geocoder_google($address, $options = array()) {
+function geocoder_google($address, $options = array(), $components = array()) {
   $delay_trigger = &drupal_static(__FUNCTION__);
   $delay = variable_get('geocoder_google_delay', 0);
   if ($delay > 0 && $delay_trigger) {
@@ -44,6 +44,12 @@ function geocoder_google($address, $options = array()) {
   if (!empty($options['biasing']['region'])) {
     $query['region'] = $options['biasing']['region'];
   }
+  else {
+    $geocoder_settings = variable_get("geocoder_settings", array());
+    if (isset($geocoder_settings['geocoder_region_google'])) {
+      $query['region'] = $geocoder_settings['geocoder_region_google'];
+    }
+  }
   if (!empty($options['biasing']['components'])) {
     $query['components'] = $options['biasing']['components'];
   }
@@ -196,8 +202,8 @@ function geocoder_google_field($field, $field_item, $options = array()) {
     return geocoder_google($field_item['value'], $options);
   }
   if ($field['type'] == 'addressfield' && module_exists('addressfield') && !addressfield_field_is_empty($field_item, $field)) {
-    $address = geocoder_widget_parse_addressfield($field_item);
-    return geocoder_google($address, $options);
+    list($address, $components) = geocoder_google_parse_addressfield($field_item);
+    return geocoder_google($address, $options, $components);
   }
   if ($field['type'] == 'location') {
     $address = geocoder_widget_parse_locationfield($field_item);
@@ -280,3 +286,38 @@ function geocoder_google_form($default_values = array()) {
 
   return $form;
 }
+
+function geocoder_google_parse_addressfield($field_item) {
+  $address = array();
+  $address[] = !empty($field_item['premise']) ? $field_item['premise'] : NULL;
+  $address[] = !empty($field_item['thoroughfare']) ? $field_item['thoroughfare'] : NULL;
+  $address[] = !empty($field_item['sub_administrative_area']) && !empty($field_item['administrative_area']) ? $field_item['administrative_area'] : NULL;
+  $address[] = !empty($field_item['postal_code']) ? $field_item['postal_code'] : NULL;
+
+  $components = array();
+  $components['locality'] = !empty($field_item['locality']) ? $field_item['locality'] : NULL;
+  if (!empty($field_item['sub_administrative_area'])) {
+    $components['administrative_area'] = $field_item['sub_administrative_area'];
+  }
+  else if (!empty($field_item['administrative_area'])) {
+    $components['administrative_area'] = $field_item['administrative_area'];
+  }
+
+  if (!empty($field_item['country'])) {
+    if (module_exists('countries')) {
+      $country = country_load($field_item['country']);
+      $field_item['country'] = $country->name;
+    }
+    else {
+      // Convert country code to country name.
+      include_once DRUPAL_ROOT . '/includes/locale.inc';
+      $countries = country_get_list();
+      if (array_key_exists($field_item['country'], $countries)) {
+        $field_item['country'] = $countries[$field_item['country']];
+      }
+    }
+    $components['country'] = $field_item['country'];
+  }
+
+  return array(implode(',', array_filter($address)), $components);
+}
diff --git a/plugins/geocoder_handler/mapquest_nominatim.inc b/plugins/geocoder_handler/mapquest_nominatim.inc
index 41e33b3..c5b0206 100644
--- a/plugins/geocoder_handler/mapquest_nominatim.inc
+++ b/plugins/geocoder_handler/mapquest_nominatim.inc
@@ -21,17 +21,30 @@ $plugin = array(
 /**
  * Process Markup
  */
-function geocoder_mapquest_nominatim($address, $options = array()) {
+function geocoder_mapquest_nominatim($address, $options = array(), $components = array()) {
   $geocoder_settings = variable_get("geocoder_settings", array());
   $api_url = "http://open.mapquestapi.com/nominatim/v1/search";
-  $params = array(
+  $static_params = array(
     'format' => 'json',
     'addressdetails' => 0,
     'limit' => 1,
     'osm_type' => 'N',
   );
-  $request = drupal_http_request($api_url . '?q=' . urlencode($address) . '&' . drupal_http_build_query($params));
-  $data = json_decode($request->data);
+  do {
+    if (!empty($components)) {
+      // Use structured request format for better results.
+      // See http://wiki.openstreetmap.org/wiki/Nominatim
+      $params = array_merge($static_params, $components);
+      // Mapquest can fail for an unknown street, even if it knows the city.
+      // So we'll ask again with less details if it fails.
+      array_shift($components);
+    }
+    else {
+      $params = $static_params;
+    }
+    $request = drupal_http_request($api_url . '?q=' . urlencode($address) . '&' . drupal_http_build_query($params));
+    $data = json_decode($request->data);
+  } while (empty($data) && !empty($components));
   return _geocoder_mapquest_nominatim_geometry($data);
 }
 
@@ -41,7 +54,8 @@ function geocoder_mapquest_nominatim_field($field, $field_item) {
   }
   if ($field['type'] == 'addressfield' && module_exists('addressfield') && !addressfield_field_is_empty($field_item, $field)) {
     $address = geocoder_widget_parse_addressfield($field_item);
-    return geocoder_mapquest_nominatim($address);
+    $components = geocoder_mapquest_nominatim_parse_addressfield($field_item);
+    return geocoder_mapquest_nominatim($address, array(), $components);
   }
   if ($field['type'] == 'location') {
     $address = geocoder_widget_parse_locationfield($field_item);
@@ -60,3 +74,16 @@ function _geocoder_mapquest_nominatim_geometry(&$data) {
   geophp_load();
   return new Point($data[0]->lon, $data[0]->lat);
 }
+
+function geocoder_mapquest_nominatim_parse_addressfield($field_item) {
+  $components = array();
+  // Params order matters. The more precise params first.
+  // See geocoder_mapquest_nominatim().
+  $components['street'] = !empty($field_item['thoroughfare']) ? $field_item['thoroughfare'] : NULL;
+  $components['postalcode'] = !empty($field_item['postal_code']) ? $field_item['postal_code'] : NULL;
+  $components['city'] = !empty($field_item['locality']) ? $field_item['locality'] : NULL;
+  $components['county'] = !empty($field_item['sub_administrative_area']) ? $field_item['sub_administrative_area'] : NULL;
+  $components['state'] = !empty($field_item['administrative_area']) ? $field_item['administrative_area'] : NULL;
+  $components['country'] = !empty($field_item['country']) ? $field_item['country'] : NULL;
+  return $components;
+}
-- 
2.8.4

