google.inc 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <?php
  2. /**
  3. * @file
  4. * Google geocoder.
  5. */
  6. /**
  7. * Returns an XML document containing the list of countries supported by the
  8. * Google geocoder.
  9. * A cached version is stored in the Drupal cache in case Google is unreachable.
  10. */
  11. function google_geocode_country_list_xml() {
  12. // Get the google data from the feed.
  13. $source = drupal_http_request('http://spreadsheets.google.com/feeds/list/p9pdwsai2hDMsLkXsoM05KQ/default/public/values');
  14. if (!$source->data) {
  15. // Use the cache.
  16. $data = cache_get('location_google');
  17. if (!defined('LIBXML_VERSION') || (version_compare(phpversion(), '5.1.0', '<'))) {
  18. $xml = simplexml_load_string($data->data, NULL);
  19. }
  20. else {
  21. $xml = simplexml_load_string($data->data, NULL, LIBXML_NOERROR | LIBXML_NOWARNING);
  22. }
  23. }
  24. else {
  25. if (!defined('LIBXML_VERSION') || (version_compare(phpversion(), '5.1.0', '<'))) {
  26. $xml = simplexml_load_string($source->data, NULL);
  27. // Stores the XML in the cache to eventually use it later.
  28. cache_set('location_google', $xml->asXML());
  29. }
  30. else {
  31. $xml = simplexml_load_string($source->data, NULL, LIBXML_NOERROR | LIBXML_NOWARNING);
  32. // Store the XML in the cache to eventually use it later.
  33. cache_set('location_google', $xml->asXML());
  34. }
  35. }
  36. return $xml;
  37. }
  38. /**
  39. * Return the list of ISO3166 codes supported by this geocoder.
  40. * Coverage list: http://gmaps-samples.googlecode.com/svn/trunk/mapcoverage_filtered.html
  41. * Coverage list feed: http://spreadsheets.google.com/feeds/list/p9pdwsai2hDMsLkXsoM05KQ/default/public/values
  42. */
  43. function google_geocode_country_list() {
  44. // Get the google data from the feed.
  45. $xml = google_geocode_country_list_xml();
  46. // Loop through google data and find all valid entries.
  47. $regionclean = array();
  48. foreach($xml->entry as $region) {
  49. $pos = strpos($region->content, 'geocoding:') + 11;
  50. $geocoding = substr($region->content, $pos, strpos($region->content, ',', $pos) - $pos);
  51. if (strpos($geocoding, "Yes") !== FALSE) {
  52. $regionclean[] = t(htmlentities($region->title));
  53. }
  54. }
  55. // Get the countries list and clean it up so that names will match to google.
  56. // The regex removes parenthetical items so that both of the "Congo" entries
  57. // and the "Coco Islands" work.
  58. // The $countriesfixes overwrites values in the Drupal API countries list
  59. // with values that will match to google's entries.
  60. // "Sao Tome and Principe" are non-accented in the Drupal API so the entry
  61. // here is to match the htmlentities() fix in the foreach loop below.
  62. // Note: it may be neccessary to adjust/add to the fixes list in the future
  63. // if google adds countries that don't match the Drupal API list for whatever
  64. // reason.
  65. $countries = location_get_iso3166_list();
  66. $regex = "#[ (].*[)]#e";
  67. $cntryclean = preg_replace($regex, "", $countries);
  68. $countriesfixes = array_merge($cntryclean, array(
  69. "hk" => t("China"),
  70. "mo" => t("China"),
  71. "pn" => t("Pitcairn Islands"),
  72. "wf" => t("Wallis Futuna"),
  73. "st" => t("S&Atilde;&pound;o Tom&Atilde;&copy; and Pr&Atilde;&shy;ncipe"),
  74. ));
  75. // Compare new google data found to fixed country name values and return
  76. // matches with abbreviations as keys.
  77. $googlematched = array_intersect($countriesfixes, $regionclean);
  78. // Compare new keys to original Drupal API and return the array with the
  79. // original name values.
  80. $fixedkeys = array_intersect_key($countries, $googlematched);
  81. return array_keys($fixedkeys);
  82. }
  83. /**
  84. * Return general information about this geocoder.
  85. */
  86. function google_geocode_info() {
  87. return array(
  88. 'name' => 'Google Maps',
  89. 'url' => 'http://maps.google.com',
  90. 'tos' => 'http://www.google.com/help/terms_local.html',
  91. 'general' => TRUE,
  92. );
  93. }
  94. /**
  95. * Perform a geocode on a location array.
  96. * @param $location
  97. * The location array to process.
  98. * @return
  99. * an associative array with keys 'lat' and 'lon' containing the coordinates.
  100. */
  101. function google_geocode_location($location = array()) {
  102. if (function_exists('gmap_get_key')) {
  103. $key = gmap_get_key();
  104. }
  105. else {
  106. $key = variable_get('location_geocode_google_apikey', '');
  107. }
  108. $query = array(
  109. 'key' => $key,
  110. 'sensor' => 'false', // Required by TOS.
  111. 'output' => 'xml',
  112. //'ll' => 0,
  113. //'spn' => 0,
  114. 'gl' => $location['country'],
  115. 'q' => _google_geocode_flatten($location),
  116. );
  117. $url = url('http://maps.google.com/maps/geo', array(
  118. 'query' => $query,
  119. 'external' => TRUE,
  120. ));
  121. $http_reply = drupal_http_request($url);
  122. $status_code_match = array();
  123. preg_match('/<code>(.*)<\/code>/', $http_reply->data, $status_code_match);
  124. $status_code = $status_code_match[1];
  125. if ($status_code != 200) {
  126. watchdog('location', 'Google geocoding returned status code: %status_code', array('%status_code' => $status_code));
  127. return NULL;
  128. }
  129. $accuracy_code_match = array();
  130. preg_match('/Accuracy="([0-9])"/', $http_reply->data, $accuracy_code_match);
  131. $accuracy_code = $accuracy_code_match[1];
  132. $min_accuracy = variable_get('location_geocode_' . $location['country'] . 'google_accuracy_code', variable_get('location_geocode_google_minimum_accuracy', '3'));
  133. if ($accuracy_code < $min_accuracy) {
  134. watchdog('location', 'Google geocoding result for %country did not meet the minimum accuracy level of %min_accuracy. Result accuracy: %accuracy_code', array('%country' => $location['country'], '%min_accuracy' => $min_accuracy, '%accuracy_code' => $accuracy_code));
  135. return NULL;
  136. }
  137. $latlon_match = array();
  138. preg_match('/<coordinates>(.*)<\/coordinates>/', $http_reply->data, $latlon_match);
  139. $latlon_exploded = explode(',', $latlon_match[1]);
  140. return array('lat' => $latlon_exploded[1], 'lon' => $latlon_exploded[0]);
  141. }
  142. /**
  143. * General settings for this geocoder.
  144. */
  145. function google_geocode_settings() {
  146. $form = array();
  147. $key = '';
  148. if (function_exists('gmap_get_key')) {
  149. $key = gmap_get_key();
  150. }
  151. if (!empty($key)) {
  152. $form['location_geocode_google_apikey'] = array(
  153. '#type' => 'item',
  154. '#title' => t('Google Maps API Key'),
  155. '#value' => $key,
  156. '#description' => t('The key in use was automatically provided by GMap.'),
  157. );
  158. }
  159. else {
  160. $form['location_geocode_google_apikey'] = array(
  161. '#type' => 'textfield',
  162. '#title' => t('Google Maps API Key'),
  163. '#size' => 64,
  164. '#maxlength' => 128,
  165. '#default_value' => variable_get('location_geocode_google_apikey', ''),
  166. '#description' => t('In order to use the Google Maps API geocoding web-service, you will need a Google Maps API Key. You can obtain one at the !sign_up_link for the !google_maps_api. PLEASE NOTE: You will <em>not</em> have to re-enter your API key for each country for which you have selected Google Maps for geocoding. This setting is global.', array('!sign_up_link' => '<a href="http://www.google.com/apis/maps/signup.html">sign-up page</a>', '!google_maps_api' => '<a href="http://www.google.com/apis/maps/">Google Maps API</a>'))
  167. );
  168. }
  169. $country = arg(5);
  170. if ($country) {
  171. $form['location_geocode_' . $country . '_google_accuracy_code'] = array(
  172. '#type' => 'select',
  173. '#title' => t('Google Maps Geocoding Accuracy for %country', array('%country' => $country ) ),
  174. '#default_value' => variable_get('location_geocode_' . $country . '_google_accuracy_code', variable_get('location_geocode_google_minimum_accuracy', '3')),
  175. '#options' => location_google_geocode_accuracy_codes(),
  176. '#description' => t('The minimum required accuracy for the geolocation data to be saved.'),
  177. );
  178. }
  179. return $form;
  180. }
  181. function _google_geocode_flatten($location = array()) {
  182. // Check if its a valid address
  183. if (empty($location)) {
  184. return '';
  185. }
  186. $address = '';
  187. if (!empty($location['street'])) {
  188. $address .= $location['street'];
  189. }
  190. if (!empty($location['city'])) {
  191. if (!empty($address)) {
  192. $address .= ', ';
  193. }
  194. $address .= $location['city'];
  195. }
  196. if (!empty($location['province'])) {
  197. if (!empty($address)) {
  198. $address .= ', ';
  199. }
  200. // @@@ Fix this!
  201. if (substr($location['province'], 0, 3) == $location['country'] . '-') {
  202. $address .= substr($location['province'], 3);
  203. watchdog('Location', 'BUG: Country found in province attribute.');
  204. }
  205. else {
  206. $address .= $location['province'];
  207. }
  208. }
  209. if (!empty($location['postal_code'])) {
  210. if (!empty($address)) {
  211. $address .= ' ';
  212. }
  213. $address .= $location['postal_code'];
  214. }
  215. if (!empty($location['country'])) {
  216. if (!empty($address)) {
  217. $address .= ', ';
  218. }
  219. $address .= $location['country'];
  220. }
  221. return $address;
  222. }