diff -u -p -r ./location-orig/geocoding/google.inc ./location/geocoding/google.inc --- ./location-orig/geocoding/google.inc 2010-05-27 05:16:14.000000000 -0700 +++ ./location/geocoding/google.inc 2010-08-23 16:03:08.732901134 -0700 @@ -142,10 +142,23 @@ function google_geocode_location($locati $latlon_match = array(); preg_match('/(.*)<\/coordinates>/', $http_reply->data, $latlon_match); - $latlon_exploded = explode(',', $latlon_match[1]); - return array('lat' => $latlon_exploded[1], 'lon' => $latlon_exploded[0]); + //TK edit: also get and return city postcode and province + preg_match('/(.*)<\/LocalityName>/', $http_reply->data, $city_match); + $city_exploded = explode(',', $city_match[1]); + + preg_match('/(.*)<\/PostalCodeNumber>/', $http_reply->data, $postcode_match); + $postcode = ''; + if (!empty($postcode_match)) { + $postcode_exploded = explode(',', $postcode_match[1]); + $postcode = $postcode_exploded[0]; + } + + preg_match('/(.*)<\/AdministrativeAreaName>/', $http_reply->data, $province_match); + $province_exploded = explode(',', $province_match[1]); + + return array('lat' => $latlon_exploded[1], 'lon' => $latlon_exploded[0], 'city'=> $city_exploded[0], 'postal_code' => $postcode, 'province'=> $province_exploded[0]); } /** diff -u -p -r ./location-orig/handlers/location_handler_argument_location_proximity.inc ./location/handlers/location_handler_argument_location_proximity.inc --- ./location-orig/handlers/location_handler_argument_location_proximity.inc 2010-07-04 03:34:04.000000000 -0700 +++ ./location/handlers/location_handler_argument_location_proximity.inc 2010-08-23 17:26:44.904400950 -0700 @@ -69,33 +69,72 @@ class location_handler_argument_location } // @@@ Switch to mock location object and rely on location more? - if ($this->options['type'] == 'postal') { - if (!isset($this->value['country'])) { - $this->value['country'] = variable_get('location_default_country', 'us'); + if (in_array($this->options['type'], array('postal', 'postal_default', 'city', 'city_default', 'postal_or_city', 'postal_or_city_default'))) { + if (in_array($this->options['type'], array('postal_default', 'city_default', 'postal_or_city_default'))) { + if (!isset($this->value['country'])) { + $this->value['country'] = variable_get('location_default_country', 'us'); + } } + + // we need a country, so if it is not set, return false + if (empty($this->value['country'])) { + return false; + } + // Zip code lookup. - if (!empty($this->value['postal_code']) && !empty($this->value['country'])) { - location_load_country($this->value['country']); - $coord = location_get_postalcode_data($this->value); - if ($coord) { - $this->value['latitude'] = $coord['lat']; - $this->value['longitude'] = $coord['lon']; - } - else { + $coord = NULL; + if (!empty($this->value['postal_code']) && in_array($this->options['type'], array('postal', 'postal_default', 'postal_or_city', 'postal_or_city_default'))) { + $coord = location_get_postalcode_data($this->value, TRUE); + //will fail validation if not a proper postal code and $coord will be NULL + } + + // City lookup using postcode value + if (!$coord && empty($this->value['city']) && in_array($this->options['type'], array('postal_or_city', 'postal_or_city_default'))) { + //copy the value of postal_code into city + $this->value['city'] = $this->value['postal_code']; + unset($this->value['postal_code']); + $coord = location_get_city_data($this->value, TRUE); + // clean up by setting the values back and returning false; + if (!$coord){ + $this->value['postal_code'] = $this->value['city']; + unset($this->value['city']); $coord = location_latlon_rough($this->value); if ($coord) { $this->value['latitude'] = $coord['lat']; $this->value['longitude'] = $coord['lon']; - } - else { + } else { return FALSE; } } } - else { - return FALSE; + // City lookup. + if (!empty($this->value['city']) && in_array($this->options['type'], array('city', 'city_default'))) { + $coord = location_get_city_data($this->value, TRUE); } + + if ($coord) { + $this->value['latitude'] = $coord['lat']; + $this->value['longitude'] = $coord['lon']; + // TK edit: Add city, post_code and province from our results to $this->value, + // which keeps things accurate and tidy + if (array_key_exists('city', $coord) && !empty($coord['city'])) { + $this->value['city'] = $coord['city']; + } + if (array_key_exists('postal_code', $coord) && !empty($coord['postal_code'])) { + $this->value['postal_code'] = $coord['postal_code']; + } + if (array_key_exists('province', $coord) && !empty($coord['province'])) { + $this->value['province'] = $coord['province']; + } + } else { + return false; + } + } + + if(!$coord) { + return false; } + return TRUE; } diff -u -p -r ./location-orig/handlers/location_handler_field_location_distance.inc ./location/handlers/location_handler_field_location_distance.inc --- ./location-orig/handlers/location_handler_field_location_distance.inc 2010-07-04 07:48:15.000000000 -0700 +++ ./location/handlers/location_handler_field_location_distance.inc 2010-08-23 16:29:26.644401141 -0700 @@ -45,6 +45,7 @@ class location_handler_field_location_di 'hybrid' => t("User's Latitude / Longitude (fall back to static if unset)"), 'static' => t('Static Latitude / Longitude'), 'tied' => t("Use Distance / Proximity filter"), + 'arg' => t("Use Distance / Proximity argument"), 'postal' => t('Postal Code / Country'), 'postal_default' => t('Postal Code (assume default country)'), 'php' => t('Use PHP code to determine latitude/longitude'), diff -u -p -r ./location-orig/handlers/location_views_handler_filter_proximity.inc ./location/handlers/location_views_handler_filter_proximity.inc --- ./location-orig/handlers/location_views_handler_filter_proximity.inc 2010-07-04 07:10:07.000000000 -0700 +++ ./location/handlers/location_views_handler_filter_proximity.inc 2010-08-23 17:05:49.692400691 -0700 @@ -22,7 +22,8 @@ class location_views_handler_filter_prox 'latitude' => '', 'longitude' => '', 'postal_code' => '', - 'country' => '', + 'city' => '', + 'country' => variable_get('location_default_country', 'us'), 'php_code' => '', 'nid_arg' => '', 'nid_loc_field' => 'node', @@ -78,6 +79,10 @@ class location_views_handler_filter_prox 'static' => t('Static Latitude / Longitude'), 'postal' => t('Postal Code / Country'), 'postal_default' => t('Postal Code (assume default country)'), + 'city' => t('City / Country'), + 'city_default' => t('City (assume default country)'), + 'postal_or_city' => t('Postal Code or City / Country'), + 'postal_or_city_default' => t('Postal Code or City (assume default country)'), 'php' => t('Use PHP code to determine latitude/longitude'), 'nid_arg' => t("Node's Latitude / Longitude from views nid argument"), 'uid_arg' => t("User's Latitude / Longitude from views uid argument"), @@ -137,6 +142,15 @@ class location_views_handler_filter_prox '#dependency' => array('edit-options-origin' => array('postal', 'postal_default')), '#weight' => 3, ); + + $form['value']['city'] = array( + '#type' => 'textfield', + '#title' => t('City'), + '#default_value' => $this->value['city'], + '#process' => array('views_process_dependency'), + '#dependency' => array('edit-options-type' => array('city')), + ); + $form['value']['country'] = array( '#type' => 'select', '#title' => t('Country'), @@ -262,7 +276,12 @@ class location_views_handler_filter_prox unset($form[$key]['longitude']); } if ($origin != 'postal' && $origin != 'postal_default') { - unset($form[$key]['postal_code']); + if ($type == 'postal_or_city' || $type == 'postal_or_city_default') { + $form[$key]['postal_code']['#title'] = t('Postal Code or City'); + } + else { + unset($form[$key]['postal_code']); + } } if ($origin != 'postal') { unset($form[$key]['country']); diff -u -p -r ./location-orig/location.inc ./location/location.inc --- ./location-orig/location.inc 2010-07-04 07:10:07.000000000 -0700 +++ ./location/location.inc 2010-08-25 11:43:23.932455577 -0700 @@ -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,10 +90,26 @@ 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') { + + // TK edit: add city and province (they are in the zipcode table and are useful) + $location['city'] = isset($location['city']) ? trim($location['city']) : NULL; + $location['province'] = isset($location['province']) ? trim($location['province']) : NULL; + + // TK edit: separated out country check from postal code check for flexibility + // TK edit: ensure we have a country + if (is_null($location['country']) || empty($location['country'])) { + return NULL; + } + // TK edit: ensure if we have a postcode. + if (is_null($location['postal_code']) || 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']); @@ -100,6 +118,157 @@ function location_get_postalcode_data($l 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 + // TK edit: also return the 'postal_code' for completeness sake and to make sure the return array is uniform + return array('lat' => $row->latitude, 'lon' => $row->longitude, 'city' => $row->city, 'postal_code' => $row->zip, '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']; + // TK edit: update our values to the values from our results if available + // this way we are using verified values ones when we store them and return them to the function + if (array_key_exists('city', $data) && !empty($data['city'])) { + $location['city'] = $data['city']; + } + if (array_key_exists('postal_code', $data) && !empty($data['postal_code'])) { + $location['postal_code'] = $data['postal_code']; + } + if (array_key_exists('province', $data) && !empty($data['province'])) { + $location['province'] = $data['province']; + } + // 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']; + // only write to the db if it isn't there already + $result = db_query("SELECT * FROM {zipcodes} WHERE country = '%s' AND zip = '%s'", $location['country'], $location['postal_code']); + if (!$row = db_fetch_object($result)) { + drupal_write_record('zipcodes', array_merge($location, $tzdata)); + } + //TK edit: added city postal_code and province to the return array to give complete results + return array('lat' => $location['latitude'], 'lon' => $location['longitude'], 'city' => $location['city'], 'postal_code' => $location['postal_code'], 'province' => $location['province']); + } + } + return NULL; + } +} + +/** + * Try to extract the the Latitude and Longitude data from the + * city. + * + * @param $location + * Array. the location data + * -> the values are: + * 'street' => the string representing the street location + * 'additional' => the string representing the additional street location portion in the location form + * 'city' => the city name (REQUIRED) + * '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 + * @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 + * actual look-up does not exist. If the appropriate function exists, + * then this function returns an associative array where + * '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 + * + * @ingroup Location + */ +function location_get_city_data($location = array()) { + $location['country'] = isset($location['country']) ? trim($location['country']) : NULL; + $location['postal_code'] = isset($location['postal_code']) ? trim($location['postal_code']) : NULL; + $location['city'] = isset($location['city']) ? trim($location['city']) : NULL; + $location['province'] = isset($location['province']) ? trim($location['province']) : NULL; + + // ensure we have a country + if (is_null($location['country']) || empty($location['country'])) { + return NULL; + } + // ensure if we have a city. + if (is_null($location['city']) || empty($location['city']) || $location['city'] == 'xx') { + return NULL; + } + + // Normalize city data -- note: function implemented starting on line 641 + if (!location_standardize_postalcode($location['city'], $location['city'])) { + return NULL; + } + location_load_country($location['country']); + $country_specific_function = 'location_get_city_data_'. $location['country']; + if (function_exists($country_specific_function)) { + return $country_specific_function($location); + } + else { + + /* + * @@@ Should we try to get city from the db? - problem is that we either need a cities table or we need to + * query the zipcodes table, in which case we will likely get multiple results and need to determine + * the best one to use - the code below takes the first one (we could sort on postalcode first) + * + * I personally dont care about this and will always hit the geocoder for a city search. + * + $result = db_query("SELECT * FROM {zipcodes} WHERE country = '%s' AND city = '%s'", $location['country'], $location['city']); + if ($row = db_fetch_object($result)) { + return array('lat' => $row[0]->latitude, 'lon' => $row[0]->longitude, 'city' => $location['city'], 'postal_code' => $row[0]->zip, 'province' => $row[0]->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']; + // TK edit: update our values to the values from our results if available + if (array_key_exists('city', $data) && !empty($data['city'])) { + $location['city'] = $data['city']; + } + if (array_key_exists('postal_code', $data) && !empty($data['postal_code'])) { + $location['postal_code'] = $data['postal_code']; + } + if (array_key_exists('province', $data) && !empty($data['province'])) { + $location['province'] = $data['province']; + } + /* + * @@@ I dont care about adding my results to the db, but others might, so I leave this in + * + // 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'], 'city' => $location['city'], 'postal_code' => $location['postal_code'], 'province' => $location['province']); + } + + // - uncomment if you decide to check the db first } return NULL; } } @@ -492,6 +661,45 @@ 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; +} + +/** + * Canonicalize a city. + */ +function location_standardize_city(&$city, $country = 'us') { + + // Standard things go here + $city = trim($city); + + // Country-custom stuff + location_load_country($country); + $country_specific_function = 'location_standardize_city_'. $country; + if (function_exists($country_specific_function)) { + return $country_specific_function($city); + } + + // If no custom functions yielded 'invalid', the postal code is OK + return TRUE; +} + +// @@@ New in 3.x, document. +/** * Canonicalize a country code. */ function location_standardize_country_code(&$country) { diff -u -p -r ./location-orig/location.views.inc ./location/location.views.inc --- ./location-orig/location.views.inc 2010-07-04 07:10:07.000000000 -0700 +++ ./location/location.views.inc 2010-08-25 11:25:27.036400771 -0700 @@ -54,6 +54,12 @@ function location_views_handlers() { 'location_handler_argument_location_proximity' => array( 'parent' => 'views_handler_argument', ), + 'location_handler_argument_location_distance' => array( + 'parent' => 'views_handler_argument', + ), +// 'location_handler_argument_location_proximity' => array( +// 'parent' => 'views_handler_argument', +// ), 'location_handler_field_location_address' => array( 'parent' => 'views_handler_field', ), @@ -312,6 +318,9 @@ function location_views_data() { 'filter' => array( 'handler' => 'location_views_handler_filter_proximity', ), + 'argument' => array( + 'handler' => 'location_handler_argument_location_distance', + ), // 'relationship' => array( // 'handler' => 'location_handler_relationship_location_distance', // ), @@ -456,6 +465,7 @@ function location_views_proximity_get_ar */ function location_views_proximity_get_reference_location($view, $options) { $coordinates = array(); + switch ($options['origin']) { case 'user': case 'hybrid': @@ -489,6 +499,18 @@ function location_views_proximity_get_re } } break; + case 'arg': + if (!empty($view->argument)) { + foreach ($view->argument as $k => $v) { + if ($v->table == 'location' && $v->field == 'distance' && $v->options['relationship'] == $options['relationship']) { + if ($v->calculate_coords()) { + $coordinates['latitude'] = (float)$v->value['latitude']; + $coordinates['longitude'] = (float)$v->value['longitude']; + } + } + } + } + break; case 'postal': case 'postal_default': // Force default for country. @@ -504,6 +526,21 @@ function location_views_proximity_get_re } } break; + case 'postal_or_city': + case 'postal_or_city_default': + if( $options['origin'] == 'postal_or_city_default') { + $options['country'] = variable_get('location_default_country', 'us'); + } + + // Lookup location by geocoding + if (!empty($options['city']) && !empty($options['country'])) { + $coords = location_latlon_exact($options); + if ($coords) { + $coordinates['latitude'] = (float) $coords['lat']; + $coordinates['longitude'] = (float) $coords['lon']; + } + } + break; case 'php': ob_start(); $result = eval($options['php_code']); diff -u -p -r ./location-orig/supported/location.ca.inc ./location/supported/location.ca.inc --- ./location-orig/supported/location.ca.inc 2008-03-08 14:25:39.000000000 -0800 +++ ./location/supported/location.ca.inc 2010-08-23 16:03:08.736400759 -0700 @@ -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 -u -p -r ./location-orig/supported/location.de.inc ./location/supported/location.de.inc --- ./location-orig/supported/location.de.inc 2009-02-04 15:16:17.000000000 -0800 +++ ./location/supported/location.de.inc 2010-08-23 16:03:08.736400759 -0700 @@ -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 -u -p -r ./location-orig/supported/location.nl.inc ./location/supported/location.nl.inc --- ./location-orig/supported/location.nl.inc 2008-07-23 11:13:07.000000000 -0700 +++ ./location/supported/location.nl.inc 2010-08-23 16:03:08.736400759 -0700 @@ -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 -u -p -r ./location-orig/supported/location.uk.inc ./location/supported/location.uk.inc --- ./location-orig/supported/location.uk.inc 2008-03-08 10:55:18.000000000 -0800 +++ ./location/supported/location.uk.inc 2010-08-23 16:03:08.736400759 -0700 @@ -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,26 @@ function location_map_link_uk_google($lo return NULL; } } + +function location_standardize_postalcode_uk(&$postalcode) { + if (!is_string($postalcode)) { + return FALSE; + } + //capitalize it + $postalcode = strtoupper($postalcode); + // return true if it is a full valid postcode + $pattern = '/(^gir\s0aa$)|(^[a-pr-uwyz]((\d{1,2})|([a-hk-y]\d{1,2})|(\d[a-hjks-uw])|([a-hk-y]\d[abehmnprv-y]))\s\d[abd-hjlnp-uw-z]{2}$)/i'; + if (preg_match($pattern, $postalcode)) { + // first change it so that only the outward (first) part and the first digit of the inward + // part are used (that's all google uses for licensing reasons - Royal Mail prohibits them from + // providing full postcode geocoding data to third parties) - this will keep our db accurate + $postalcode = substr($postalcode, 0, strpos($postalcode, ' ')+2); + return TRUE; + } + // return true if it is a valid outward + $pattern = '/(^gir\s0aa$)|(^[a-pr-uwyz]((\d{1,2})|([a-hk-y]\d{1,2})|(\d[a-hjks-uw])|([a-hk-y]\d[abehmnprv-y])))/i'; + if (preg_match($pattern, $postalcode)) { + return TRUE; + } + return FALSE; +} diff -u -p -r ./location-orig/supported/location.us.inc ./location/supported/location.us.inc --- ./location-orig/supported/location.us.inc 2009-02-04 15:16:17.000000000 -0800 +++ ./location/supported/location.us.inc 2010-08-23 16:03:08.736400759 -0700 @@ -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); +}