diff -rup location-3.1/location.inc location/location.inc --- location-3.1/location.inc 2010-08-14 01:54:27.000000000 +0200 +++ location/location.inc 2010-08-10 14:05:16.000000000 +0200 @@ -78,6 +78,8 @@ function location_map_link($location = a * 'province' => the province code defined in the country-specific include file * 'country' => the lower-case of the two-letter ISO code (REQUIRED) * 'postal_code' => the postal-code (REQUIRED) + * @param $geocode_in_db + * Boolean. If TRUE, the coordinates for the zipcode may be geocoded, and cached in the database. * * @return * Array or NULL. NULL if the delegated-to function that does the @@ -88,18 +90,55 @@ function location_map_link($location = a * * @ingroup Location */ -function location_get_postalcode_data($location = array()) { +function location_get_postalcode_data($location = array(), $geocode_in_db = FALSE) { $location['country'] = isset($location['country']) ? trim($location['country']) : NULL; $location['postal_code'] = isset($location['postal_code']) ? trim($location['postal_code']) : NULL; if (is_null($location['postal_code']) || is_null($location['country']) || empty($location['country']) || empty($location['postal_code']) || $location['postal_code'] == 'xx') { return NULL; } + + // Normalize postalcode data + if (!location_standardize_postalcode($location['postal_code'], $location['country'])) { + return NULL; + } location_load_country($location['country']); $country_specific_function = 'location_get_postalcode_data_'. $location['country']; if (function_exists($country_specific_function)) { return $country_specific_function($location); } else { + + // Check whether postalcode data is present in zipcode table + $result = db_query("SELECT * FROM {zipcodes} WHERE country = '%s' AND zip = '%s'", $location['country'], $location['postal_code']); + if ($row = db_fetch_object($result)) { + // We can't be absolutely sure that city/province are filled in the database, but some callers use it + return array('lat' => $row->latitude, 'lon' => $row->longitude, 'city' => $row->city, 'province' => $row->state, 'country' => $row->country); + } + elseif ($geocode_in_db) { + + // Try to get exact location. If it succeeds, cache it in zipcode table + if ($data = location_latlon_exact($location)) { + $location['latitude'] = $data['lat']; + $location['longitude'] = $data['lon']; + + // Try to see whether we can get timezone/dst data based on location. If not, we'll insert zeroes. + $country_specific_function = 'location_timezone_data_'. $location['country']; + if (function_exists($country_specific_function)) { + $tzdata = $country_specific_function($location); + } + else { + $tzdata = array('timezone' => 0, 'dst' => 0); + } + // Make sure values have the right keys and write record to database. + if (isset($location['province'])) { + $tzdata['state'] = $location['province']; + } + $tzdata['zip'] = $location['postal_code']; + drupal_write_record('zipcodes', array_merge($location, $tzdata)); + + return array('lat' => $location['latitude'], 'lon' => $location['longitude']); + } + } return NULL; } } @@ -491,6 +530,26 @@ function location_province_code($country } // @@@ New in 3.x, document. +/** + * Canonicalize a postal code. + */ +function location_standardize_postalcode(&$postalcode, $country = 'us') { + + // Standard things go here + $postalcode = trim($postalcode); + + // Country-custom stuff + location_load_country($country); + $country_specific_function = 'location_standardize_postalcode_'. $country; + if (function_exists($country_specific_function)) { + return $country_specific_function($postalcode); + } + + // If no custom functions yielded 'invalid', the postal code is OK + return TRUE; +} + +// @@@ New in 3.x, document. /** * Canonicalize a country code. */ diff -rup location-3.1/location.views.inc location/location.views.inc --- location-3.1/location.views.inc 2010-08-14 01:54:27.000000000 +0200 +++ location/location.views.inc 2010-08-10 20:42:29.000000000 +0200 @@ -497,7 +497,7 @@ function location_views_proximity_get_re } // Zip code lookup. if (!empty($options['postal_code']) && !empty($options['country'])) { - $coords = location_latlon_rough($options); + $coords = location_get_postalcode_data($options, TRUE); if ($coords) { $coordinates['latitude'] = (float) $coords['lat']; $coordinates['longitude'] = (float) $coords['lon']; diff -rup location-3.1/supported/location.ca.inc location/supported/location.ca.inc --- location-3.1/supported/location.ca.inc 2010-06-03 04:27:53.000000000 +0200 +++ location/supported/location.ca.inc 2009-12-26 20:50:41.000000000 +0100 @@ -387,3 +387,24 @@ function location_geocode_ca_geocoder($l } } } + +function location_standardize_postalcode_ca(&$postalcode) { + if (!is_string($postalcode)) { + return FALSE; + } + + $code = strtoupper(str_replace(' ','',$postalcode)); + if (preg_match("/^[A-Z]{1,2}[0-9]{2,3}[A-Z]{2}$/", $code) + || preg_match("/^[A-Z]{1,2}[0-9]{1}[A-Z]{1}[0-9]{1}[A-Z]{2}$/", $code) + || preg_match("/^GIR0[A-Z]{2}$/", $code)) { + + preg_match('/^[a-zA-Z]*[0-9 ]+/', $postalcode, $matches); + $postalcode = substr_replace(str_replace(' ', '', $matches[0]), '', -1); + return TRUE; + } + // Return TRUE anyway. The code in http://drupal.org/files/issues/location.proximity.handler.patch + // does not protest if the postal code does not adhere to above regexps. + // @@@ Someone with knowledge about CA zipcodes please check whether we should return FALSE. + // The database ({zipcode} cache) is better off when 'wrongly formatted' postal codes get denied. + return TRUE; +} diff -rup location-3.1/supported/location.de.inc location/supported/location.de.inc --- location-3.1/supported/location.de.inc 2010-06-03 04:27:53.000000000 +0200 +++ location/supported/location.de.inc 2009-12-26 20:55:44.000000000 +0100 @@ -256,3 +256,11 @@ function location_get_postalcode_data_de return NULL; } } + +function location_standardize_postalcode_de(&$postalcode) { + // @@@ omg, like, srly. The "$dash_index === FALSE" SO does not make sense. + // Someone please check the code in location_get_postalcode_data_de() + // and insert something _proper_ here, please? + // (Was someone trying to strip a trailing 'D-', or something?) + return TRUE; +} diff -rup location-3.1/supported/location.nl.inc location/supported/location.nl.inc --- location-3.1/supported/location.nl.inc 2010-06-03 04:27:53.000000000 +0200 +++ location/supported/location.nl.inc 2010-03-20 16:47:59.000000000 +0100 @@ -20,6 +20,13 @@ function location_province_list_nl() { ); } +function location_timezone_data_nl($location) { + return array( + 'timezone' => 1, + 'dst' => 1 + ); +} + function location_map_link_nl_providers() { return array( 'google' => array( @@ -50,3 +57,19 @@ function location_map_link_nl_google($lo return NULL; } } + +function location_standardize_postalcode_nl(&$postalcode) { + if (!is_string($postalcode)) { + return FALSE; + } + $postalcode = strtoupper(str_replace(' ', '', $postalcode)); + // 4-digit as well as 4+2 letters are accepted. (The latter yields better geocoding) + switch (strlen($postalcode)) { + case 4: + return is_numeric($postalcode); + case 6: + return is_numeric(substr($postalcode,0,4)) && preg_match('/^[A-Z]{2}/', substr($postalcode,-2)); + default: + return FALSE; + } +} diff -rup location-3.1/supported/location.uk.inc location/supported/location.uk.inc --- location-3.1/supported/location.uk.inc 2010-06-03 04:27:53.000000000 +0200 +++ location/supported/location.uk.inc 2009-12-26 21:19:36.000000000 +0100 @@ -238,6 +238,13 @@ function location_province_list_uk() { 'WRX' => "Wrexham"); } +function location_timezone_data_uk($location) { + return array( + 'timezone' => 0, + 'dst' => 1 + ); +} + function location_map_link_uk_providers() { return array( 'google' => array( @@ -268,3 +275,24 @@ function location_map_link_uk_google($lo return NULL; } } + +function location_standardize_postalcode_uk(&$postalcode) { + if (!is_string($postalcode)) { + return FALSE; + } + + $code = strtoupper(str_replace(' ','',$postalcode)); + if (preg_match("/^[A-Z]{1,2}[0-9]{2,3}[A-Z]{2}$/",$code) + || preg_match("/^[A-Z]{1,2}[0-9]{1}[A-Z]{1}[0-9]{1}[A-Z]{2}$/",$code) + || preg_match("/^GIR0[A-Z]{2}$/",$postalcode)) { + + preg_match('/^[a-zA-Z]*[0-9 ]+/', $postalcode, $matches); + $postalcode = substr_replace(str_replace(' ', '', $matches[0]), '', -1); + return TRUE; + } + // Return TRUE anyway. The code in http://drupal.org/files/issues/location.proximity.handler.patch + // does not protest if the postal code does not adhere to above regexps. + // @@@ Someone with knowledge about UK zipcodes please check whether we should return FALSE. + // The database ({zipcode} cache) is better off when 'wrongly formatted' postal codes get denied. + return TRUE; +} diff -rup location-3.1/supported/location.us.inc location/supported/location.us.inc --- location-3.1/supported/location.us.inc 2010-06-03 04:27:53.000000000 +0200 +++ location/supported/location.us.inc 2009-12-26 21:17:23.000000000 +0100 @@ -605,3 +605,16 @@ function location_province_list_numeric_ '059' => 'Virgin Islands' ); } + +function location_standardize_postalcode_us(&$postalcode) { + if (!is_string($postalcode)) { + return FALSE; + } + + // If we're dealing with a 9-digit US zipcode, strip hyphen and the last 4 digits + $dash_index = strpos($postalcode, '-'); + if ($dash_index !== FALSE) { + $postalcode = substr($postalcode, 0, $dash_index); + } + return is_numeric($postalcode); +}