diff -urNp location_old/location.inc location/location.inc --- location_old/location.inc 2007-03-19 16:49:14.000000000 +0000 +++ location/location.inc 2007-03-19 17:02:09.000000000 +0000 @@ -13,6 +13,11 @@ define('LOCATION_PATH', drupal_get_path( include_once LOCATION_PATH.'/earth.inc'; _location_include_configured(); +//Load the generic location fallback functions if we need them... +if(variable_get('location_generic_fallback',0)) { + include_once LOCATION_PATH.'/supported/location.generic.inc'; +} + /** * Get a deep-link to a mapping service such as Yahoo! Maps or MapPoint given an location. The * call is delegated based on the 'country' value in the $location parameter. @@ -1128,13 +1133,29 @@ function _location_format_search_result_ * 'lon' => A floating point number for the longitude coordinate of the parameter location * 'lat' => A floating point number for the latitude coordinate of the parameter location */ -function location_latlon_exact($location = array()) { +function location_latlon_exact($location = array(),$force_generic = FALSE) { $country = trim($location['country']); $service = variable_get('location_geocode_'. $country, 'none'); + + if(($service == 'none' || $force_generic) && variable_get('location_generic_fallback',0)) { + $service = variable_get('location_geocode_generic','none'); + $force_generic = TRUE; + $country = 'generic'; + } + if (!empty($country) && $service != 'none') { $exact_latlon_function = 'location_geocode_'. $country .'_'. $service; if (function_exists($exact_latlon_function)) { - return $exact_latlon_function($location); + $latlong = $exact_latlon_function($location); + + if(is_array($latlong)) { //If we got a value back then we're happy... + return $latlong; + } elseif(variable_get('location_generic_fallback',0) && !$force_generic) { //If nothing was returned by the country specific geocoder... then lets give the generic geocoder a shot if its available.. + return location_latlon_exact($location,TRUE); //Call with 'force_generic == TRUE'... + } else { + return NULL; + } + } else { return NULL; @@ -1211,6 +1232,11 @@ function location_configured_countries() asort($configured_countries_associative); } + + if(variable_get('location_generic_fallback',0)) { + $configured_countries_associative = array_merge(array('generic' => 'generic'),$configured_countries_associative); + } + return $configured_countries_associative; } diff -urNp location_old/location.module location/location.module --- location_old/location.module 2007-03-19 16:49:14.000000000 +0000 +++ location/location.module 2007-03-19 17:07:41.000000000 +0000 @@ -1404,10 +1404,19 @@ function location_admin_settings() { '#options' => $supported_countries, '#description' => t('Currently, your CivicSpace site is capable of supporting extra features (e.g., postal code proximity searches) for locations from this list of countries. Please narrow the list down to countries for which you want to support these extra features. It may be useful for performance to narrow down this list if most the locations in your system are from only a handful of the listed countries.') ); + - return system_settings_form($form); -} + $form['location_generic_fallback'] = array( + '#type' => 'checkbox', + '#title' => t('Use generic location function fallbacks.'), + '#return_value' => 1, + '#default_value' => variable_get('location_generic_fallback',0), + '#description' => t('If any of the countries enabled below do not have their own geocoding functions, should we use a gazeteer web server (e.g. Geonames) to discover an approximate latitude and longitude for each location. As this will sometimes resort to looking up locations by town or county names, it is possible that the latitudes and longitudes returned may be incorrect or wildly inaccurate.') + ); + + return system_settings_form($form); +} function _location_effective_user_setting() { if (module_exists('civicrm')) { diff -urNp location_old/supported/location.generic.inc location/supported/location.generic.inc --- location_old/supported/location.generic.inc 1970-01-01 01:00:00.000000000 +0100 +++ location/supported/location.generic.inc 2007-03-19 16:49:55.000000000 +0000 @@ -0,0 +1,90 @@ + array('name' => 'Geonames Webservice', 'url' => 'http://www.geonames.org/export/', 'tos' => 'http://creativecommons.org/licenses/by/2.5/') + ); +} + +/** + * Calls up the Geonames web-service to retrieve a lat/lon pair for many different countries... + * http://www.geonames.org/export/free-geocoding.html + * + * @param $location + * An associative array that represents an location where + * 'street' => is the street location + * 'supplemental' => any supplemental portion to the street location + * 'city' => city name + * 'province' => state, province, or territorial abbreviation + * 'postal_code' => postal code + * 'country' => lower-cased two-letter ISO code (REQUIRED) + * + * + * @return + * An associative array where + * 'lat' => Is a float value in latitude + * 'lon' => Is a float value in longitude + * If the location supplied does not provide enough information, NULL is returned. + * "Enough information" means that at least a country has been specified, along with a postcode, or a city name + */ +function location_geocode_generic_geonames($location = array()) { + $location_string = ''; + if(!isset($location['country'])) { + return NULL; //We can't do anything without a country code, so return null + } + + $location_string = 'country='.$location['country'] . '&postalcode=' . $location['postal_code'] . '&placename=' + . $location['city'] . '&maxRows=1&style=short'; + + $result = drupal_http_request('http://ws.geonames.org/postalCodeSearch?' . $location_string); + + //TODO: Need to include ERROR HANDLING for when the http_request doens't work... + + $data = $result->data; + + $xml_parser = drupal_xml_parser_create($data); + xml_parse_into_struct($xml_parser, $data, $vals, $index); + xml_parser_free($xml_parser); + + $params = array(); + $level = array(); + foreach ($vals as $xml_elem) { + if ($xml_elem['type'] == 'open') { + if (array_key_exists('attributes',$xml_elem)) { + list($level[$xml_elem['level']],$extra) = array_values($xml_elem['attributes']); + } else { + $level[$xml_elem['level']] = $xml_elem['tag'].$id++; //Note modification to handle Geonames structure (number each code block...) + } + } + if ($xml_elem['type'] == 'complete') { + $start_level = 1; + $php_stmt = '$params'; + while($start_level < $xml_elem['level']) { + $php_stmt .= '[$level['.$start_level.']]'; + $start_level++; + } + $php_stmt .= '[$xml_elem[\'tag\']] = $xml_elem[\'value\'];'; + eval($php_stmt); + } + } + + /*Note: Geonames doesn't have province data for the UK... but for other countries we might now like to perform an extra look up from the Lat, Long to find out the province... + See: http://www.geonames.org/export/index.html#countrysubdiv + */ + + IF($params['GEONAMES']['CODE1']['LAT']) { + return array('lat' => floatval($params['GEONAMES']['CODE1']['LAT']), 'lon' => floatval($params['GEONAMES']['CODE1']['LNG']), 'postal_code' => $params['GEONAMES']['CODE1']['POSTALCODE'], 'city' => $params['GEONAMES']['CODE1']['NAME']); + } else { + return NULL; + } + +} + + + + + + +