This feature request was first mentioned here: http://drupal.org/node/321114#comment-1694654

Regarding ZIP/postcodes for proximity searching in a View. I think it would be a valuable feature to also add functionality for a larger proximity scope, such as for Country + City?

An alternative could be to enter ZIP or Country+City - then a method to convert to the central ZIP for that city.

Here in Europe we don't have that many huge cities so using a ZIP code is often too specific. For example, I live in the 7th largest city in Italy - which has an area of only 55 square miles. So for us to implement a ZIP codes method seems a little unusual.

What does everyone else think about this?

Comments

fallsemo’s picture

Big +1 for this one. I'm surprised this has not been implemented. Any reason for that? Are there other Drupal methods to achieve this?

ioskevich’s picture

Very interested as well.

juicytoo’s picture

something like this would be great.

the search box on the left of this page.

http://www.trulia.com/NY/Bronx/

dpatte’s picture

subscribe

dufferin’s picture

On top the ZIP is kind of useless... who knows them? I have a hard time to remind mine!

summit’s picture

Subscribing, interested in this also! Martijn

jastylr’s picture

+1 for this. I'm looking for proximity search by City/State myself. I have implemented Zip Code search but as others have mentioned, who knows the Zip Codes of all the cities they could potentially be searching for. I know my own zip code but if I want to find nodes in other cities, I would first need to look up the zip somewhere and then enter it.

dargente’s picture

just got proximity search working from views. same functionality as Spatial Solr except in views we are able to configure the search results in a customized format. Problem with both solutions is that they search with zip code input, rather than City, State. i have limited knowledge about module codes, but this doesn't seem like a difficult endeavor to patch up, whereas we take City, State (default country) input and do a node proximity search. The zipcodes database already includes the following fields:

(zip, city, state, latitude, longitude, timezone, dst, country)

so city and state already matched to lat/long,
the problem might be that there are multiple lat/longs for some zips or cities:

('48103', 'Ann Arbor', 'MI', '42.280887', '-83.791470', -5, 1, 'us'),
('48104', 'Ann Arbor', 'MI', '42.266805', '-83.722970', -5, 1, 'us'),
('48105', 'Ann Arbor', 'MI', '42.307288', '-83.704430', -5, 1, 'us'),
('48106', 'Ann Arbor', 'MI', '42.253502', '-83.836571', -5, 1, 'us'),
('48107', 'Ann Arbor', 'MI', '42.253502', '-83.836571', -5, 1, 'us'),
('48108', 'Ann Arbor', 'MI', '42.232807', '-83.726710', -5, 1, 'us'),
('48109', 'Ann Arbor', 'MI', '42.291637', '-83.718310', -5, 1, 'us'),

in this case can we take the median of each lat/long, and specify one point for all?

for me, it would even suffice just to take the first instance of the city/state.

anybody know if there is a patch for this yet?

jpowell’s picture

Similar to the request to do City/State proximity searching, it would be great to use the HTML 5 getCurrentPosition tag to get the GPS coordinates of the user through a mobile browser like Safari on the iPhone and do a proximity search based on the latitude/longitude of the user.

jcarnett’s picture

If you're comfortable coding and you're using both Location and Gmap modules, it's pretty easy to make proximity search much better than the simple zip-only lookup provided by Location. The idea is that we can pass off the source to Google for geocoding, which allows us to search on street addresses, cities, states, zip codes, even landmark names.

Change the view's proximity filter to latitude/longitude input and then create a module with something like this:


/**
 * Implementation of hook_form_alter().
 */
function mymodule_form_alter(&$form, $form_state, $form_id) {
  if ($form_id == 'views_exposed_form' && $form_state['view']->name == 'my_view_name') {
    // Add a text field where the user can enter arbitrary locations
    $form['search'] = array(
      '#type' => 'textfield',
      '#title' => t('Locations near'),
      '#default_value' => '',
      '#size' => 25,
      '#weight' => -10,
    );
    
    // Hide the original proximity inputs
    $form['distance']['#type'] = 'value';
    
    // Add a validation function to restore the form to its original structure
    // so modules that follow us don't break
    array_unshift($form['#validate'], 'mymodule_form_validate');
  }
}

/**
 * FAPI validation callback for the local search form.
 */
function mymodule_form_validate($form, &$form_state) {
  if ($form_state['values']['search']) {
    // Keep a static cache for the life of the request
    static $cache = array();
    
    // If the lat/long are in our static cache, use it
    if (array_key_exists($form_state['values']['search'], $cache)) {
      $geodata = $cache[$form_state['values']['search']];
    }
    // Otherwise do a lookup to get the lat/long
    else {
      $cid = 'mymodule:' . sha1($form_state['values']['search']);
      // Check the persistent cache
      if ($data = cache_get($cid)) {
        $geodata = $data->data;
      }
      else {
        // If a five-digit zip code was submitted, we can do a fast zip lookup
        // in the database
        if (preg_match('/^\d{5}$/', $form_state['values']['search'])) {
          $location = array(
            'postal_code' => $form_state['values']['search'],
            'country' => 'us',
          );
          $geodata = location_latlon_rough($location);
        }
        // Otherwise, query Google to geocode the search
        else {
          $geocode = gmap_geocode($form_state['values']['search']);
          // If the geocode is successful, grab the lat/long
          // we could potentially add an accuracy test using $geocode['accuracy']
          if ($geocode && $geocode['status'] == 200) {
            $geodata = array(
              'lat' => $geocode['latitude'],
              'lon' => $geocode['longitude'],
            );
          }
        }

        // Update caches
        if ($geodata) {
          $cache[$form_state['values']['search']] = $geodata;
          // Store in the persistent cache for 30 minutes
          cache_set($cid, $geodata, 'cache', strtotime('+30 minutes'));
        }
      }
    }

    if ($geodata) {
      if ($geodata['lat'] && $geodata['lon']) {
        $form_state['values']['distance']['latitude'] = $geodata['lat'];
        $form_state['values']['distance']['longitude'] = $geodata['lon'];
      }
    }
  }
}

Notes:

  • The 30-minute cache is important so you don't repeatedly geocode as the user clicks through each result page. You could cache for a much longer period (last I heard, cities don't move around much).
  • This example comes from a US-specific implementation so it has checks for 5-digit zip codes. You should adjust it to fit your needs.
  • This example doesn't check the accuracy value that comes back from Google's geocoding service. In my case it wasn't important, but you should consider checking it to gauge how "rough" the latitude/longitude are.
  • YMMV
dargente’s picture

thanks for this jcarnett,

seems like i almost got it working after doing the following:
- created new module
- changed my_view_name to my view name

results
it hides the previous proximity input fields as intended,
and adds the new input field,
but i'm not getting any search results when using any proximity filter now,
unless i enter in a static lat/lon or zipcode in the filter setup.

i have it configured as Lat / Long input (map)
proximity (rectangular)
distance 100 miles

i am testing with US zipcodes, cities, states to no avail.
am i missing something?

dargente’s picture

disregard above ,

i was getting no results when testing in Views,
but it is working in the actual page, nice!

although, how to set the location input as optional,
right now, if field is blank then there are no results,
it would be nice to show ALL keyword-based results if location is not set.

checking-off the exposed filter as Optional has no effect.

jcarnett’s picture

That's an open issue with the location module (http://drupal.org/node/505596). The "fix" without patching anything is to clear the default search radius in your view. If you still want a search radius, you can modify the above code to set it along with the latitude and longitude when it updates $form_state.

yesct’s picture

#505596: Optional Proximity Value does not seem to be optional (just linking the issue from #13 using the bracket #505596 bracket notation)

dargente’s picture

how do i modify the above code to set it along with the latitude and longitude when it updates $form_state?

thanks

agreenwood13’s picture

I have got this working - as inthe long and lat co-ordinates are coming back.

But for some reason the search is not working.

Did a bit of fiddling and if the long/lat information is manually inputted it is not work. A postcode search works fine but long/lat does not.

Any ideas?

dargente’s picture

Same here, Search is not working.

Anyone have a fix for this?

xpersonas’s picture

Failed post. I'm an idiot.

Anonymous’s picture

any update on this, or input on how to implement this for 7.x-3.x-dev? this would definitely be a great feature to be able to proximity search by city name. thanks for any advice/help!

sumitk’s picture

Duplicate #1109240 but I can confirm location 3.x is not working with latest version of views. Proximity search with zip is broken there.

clashar’s picture

+1, would like to see at least in D7

HippoOnDiet’s picture

Hi Guys,

Just an idea of tackling this issue...

I am thinking of using Location: Distance / Proximity exposed filter with Postal Code and Distance (Country set to default).

Basically I will label Postal code with Location Near or City (so I will do a bit form Alter on the Postal Code field itself)

Now the idea is...

User will enter a city in Postal_code field, for example "Melbourne" and search then I will run Database query:
db_query("SELECT zip FROM {zipcodes} WHERE city = '%s'", Melbourne);
In order to get the Postal_Code number.

Once we have the postal code number, then we can set the value to postal_code field and we will have result around that area.

the only issue is I am not really with coding :(

Thank you.

Update... this is what I come up with for function yourmodule_form_alter.
I hide two fields and change label of Postal Code to Location Near:

$form['distance']['search_distance']['#type'] = 'hidden';
$form['distance']['search_unit']['#type'] = 'hidden';
$form['distance']['postal_code']['#maxlength'] = 25;
$form['distance']['postal_code']['#size'] = 10;
$form['distance']['postal_code']['#title'] = t('Location near');

$form['#validate'][] = 'yourmodule_form_alter_validate';

Then this is during validation:

//Cities will be replaced by post code, but if it's number/postcode then it use the submitted value  
function exptheming_form_alter_validate(&$form, &$form_state) {
	if ($form_state['values']['distance']['postal_code']){
		//To check whether it's a postcode or cities
		if (is_numeric($form_state['values']['distance']['postal_code'])) {
		return;
		}
		else {
		$form_state['values']['distance']['postal_code'] = db_result(db_query("SELECT zip FROM {zipcodes} WHERE city = '%s'", $form_state['values']['distance']['postal_code']));
		}
	//Set radius 10KM by default
	$form_state['values']['distance']['search_distance'] = 10;
	}
 }

NOTE: I use this for my particular state only, therefore no duplication of city name in database table.

damienmckenna’s picture

I'm working on this for an existing D6 site. While digging through the code to see why Canadian searches weren't working I ran across the following in location.views.inc:

    case 'postal':
    case 'postal_default':
      // Force default for country.
      if ($options['origin'] == 'postal_default') {
        $options['country'] = variable_get('location_default_country', 'us');
      }

This indicates that the postal code proximity search is limited to only working with US values. This is kinda silly :-)

Instead of this the postal code value should be checked against the 'zipcodes' table to find the appropriate country name.

damienmckenna’s picture

Never mind, someone had used the wrong 'origin' on the filter on my site. Gah.

damienmckenna’s picture

Oh. Wait. Someone set the proximity filter to the "Postal Code (assume default country)" origin rather than "Postal Code / Country". Dangit.

legolasbo’s picture

Issue summary: View changes
Status: Active » Closed (outdated)

Closing old D6 issues as D6 is end of life