The attached patch is Views plugin that adds to Geofield a contextual (as opposed to normal Views) filter for proximity, along the lines of what the Location module supports also.

So you can filter a View (formatted as table, map etc. ) containing a Geofield field by appending a /lat,lon distance argument to the base URL.
Example:

www.yoursite.com/yourview/-37.8136,144.9648 10

This will filter the View (e.g. map) at path /yourview to include only locations within a radius of 10 miles (or km) of the centre of Melbourne.

This has its uses if you want to construct URLs programmatically or want to send hyperlinks out in emails or newsletters.
As a UI this is of course not very friendly and an approach like #2011340: Geofield plugin to extend proximity filtering based on postal address with option to use HTML5 location is usually much preferred.

Things get more interesting though because this Views plugin also supports Fixed value and PHP Code fallbacks for the use-cases where either the lat+lon or the distance aren't specified.

So if you type:

www.yoursite.com/yourview/10

The plugin will take the reference location at the heart of the 10 mi/km radius from either a fixed lat+lon specified through the View UI panel or from a piece of PHP Code. This means that you can enter a code snippet and employ another module, like Smart IP or IPGV&M, to provide the reference location. For instance:

  $location = ip_geoloc_get_visitor_location();  // returns the visitor's location via satellite/WifI with IP address as a backup
  return $location['latitude'] . ',' . $location['longitude'];

But the best reason to for a Geofield contextual proximity filter is perhaps that this opens the door for Views Global Filter (soon to be enhanced for Geofield) to do its thing. Which is: on any landing page, offer the user a block with a field to enter a distance once. Views Global Filter then invisibly passes this to any number of Views on the site, until the visitor changes the distance.
This means that the visitor doesn't have to keep entering the distance for every View that requires it.

Naturally all this may be used in combination with Geocoder, so that instead of the visitor's current location an approximate address, eg "Bourke St, Melbourne", of some point of interest may be used.

Note: patch is simple and self-explanatory. The attached file geofield_handler_argument_proximity.txt is new. It should be renamed to geofield_handler_argument_proximity.inc and placed in sites/all/module/geofield/views/handlers

EDIT1: a bug-fixed version of this file is below, in comment number #19 of this thread.
EDIT2: a much improved patch that also allows contextual filter arguments like "/Indiana/Portland" is in comment number #23 of this thread. It also enhances the Views Geofield field (as opposed to Views contextual argument) by allowing the user to select as the reference point for distance calculations, the location appended to the URL, thus making everything consistent

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

RdeBoer’s picture

FileSize
335.93 KB

Forgot to attach the image of the resulting Views Contextual Filter for Geofields.

RdeBoer’s picture

Issue summary: View changes

typos

RdeBoer’s picture

The above patch has been tested both with and without the 7.x-1.x-dev version of Views Global Filter. View Global Filter 7.x-1.x-dev features a new proximity widget that allows the user to enter a distance (radius) and an approximate address (e.g city and/or street), very much like the Geofield normal Views proximity filter. If the address field is omitted the user's actual location (HTML5) is used.
The difference is that the Global Filter variant of the proximity filter can be simultaneously applied to any number of Views you like, across multiple pages on your site. Change the filter once and all these Views, when re-visited, will pick up the change and display accordingly.
Great for maps and tabular Views alike.

RdeBoer’s picture

FileSize
523.88 KB

Forgot screenshot. The new "Global Filter Proximity" block is on the right. Note that in order to filter the map, the global filter block need not be on the same page as the map. It can be on any page and it can filter any number of Views.
Global Filter Proximity block

johnhorning’s picture

Help! Am getting this error:
Fatal error: Call to undefined function geofield_property_info_callback() in /home2/rivulcom/drupal-7.20/sites/all/modules/entity/modules/field.info.inc on line 30

I applied the patch manually (so I guess I could have done it wrong). Also created the geofield_handler_argument_proximity.inc file. In my view, I found the new contextual filter for proximity but was then getting the message that the handler was missing. I thought it might help to disable and re-enable the module, but got a fatal error - not sure if it was exactly the same one I listed above. I then restored the original geofield files and set the status of all "geo" modules to 0 using myPHPadmin. Still getting fatal errors no matter what I try to do.

Fortunately, this is not a production site, but could use some advice.

RdeBoer’s picture

Hi John,
What do you mean by "status" and why are you messing with it in the database -- there should be no need for that.
Did you apply the patch to the 7.x-2.x-dev version of Geofield?
Did you run update.php? (before, and afterwards to restore).
Rik

johnhorning’s picture

I was unable to disable geofield the normal way - that's when the fatal error first showed up. I then changed the status of the geofield module in the system table to 0. Yes - applied the patch to the 7.x-2.x-dev version of Geofield and yes ran update.php after the patch, although it said no updates available. Ran it again after putting original files back, but get the fatal error. I've also emptied a few of the cache tables, including cache_bootstrap and cache_session_cache.

johnhorning’s picture

Okay - having much better luck with a fresh Drupal install. I am using your contextual proximity filter on a page view, along with a regular proximity filter and it seems to be working well. I hope your patch gets committed!
John

johnhorning’s picture

Now trying to get a block display working with proximity set as the contextual filter. Since the argument is not passed to blocks, I tried these settings in the filter:
When the filter value is NOT available,
Provide default value
PHP code
inserted this value: return arg(1);

The block is not showing up when I try this. It does show up if I select Display all results for the specified field.

My url looks like this: http://town1.biz/town/40.96367,-81.33122 30

Any suggestions would be appreciated.

RdeBoer’s picture

Hi John,
That won't work for two reasons:
1) if the block is on a normal page (not a Views page), then it won't support args and the URL will be invald. You would get "Page not found"
2) you can only use arg(1) if there ARE arguments

Here's an alternative, For your Contextual Filter, in the section "WHEN THE FILTER VALUE IS NOT AVAILABLE", click the radio button Provide default value and for the type select PHP Code.
Enter

  return $_GET['lat'] . ',' . $_GET['lon'] . '_' . $_GET['dist'];

Then append your coords and distance to the URL as follows:

http://town1.biz/town?lat=40.96367&lon=-81.33122&dist=30

That should do it.
Rik

RdeBoer’s picture

Re #9....

To avoid warnings when URL parameters are omitted, use this PHP Code snippet instead of the above:

  return empty($_GET['lat']) ? '' : $_GET['lat'] . ',' . $_GET['lon'] . '_' . $_GET['dist'];

or if you wish to an overall default:

  return empty($_GET['lat']) ? '0,0_20000' : $_GET['lat'] . ',' . $_GET['lon'] . '_' . $_GET['dist'];

Rik

johnhorning’s picture

The page is actually a views page even though at this point there's nothing in the view. I did that simply to avoid having to create regular pages - potentially thousands of them. So does that change any of your recommendations? The view for the page does not have any kind of filtering at this point.
John

RdeBoer’s picture

Hi John,
Re #11 if that is the case, then from memory your solution should have worked....
If I have time I can do a quick test to see why your return arg(1); solution doesn't seem to work.
But the solution from #9/#10 should also work in the case of the page being a VIew with Page display.
Rik

RdeBoer’s picture

Actually, no... your solution won't work because of #9, point b): if you don't append contextual filter arguments to the URL, then there aren't any for arg(1) to return!

If you DO append arguments, then the default PHP code does NOT apply, as it is for the situation WHEN THE FILTER VALUE IS NOT AVAILABLE".

EDIT: I was wrong here -- for a block, which does not act on URL contextual arguments, putting return arg(1); in the PHP code section of "Provide default value" DOES work.

johnhorning’s picture

Okay am trying your suggestions, but now my views page display is not found for url http://town1.biz/town?lat=40.96367&lon=-81.33122&dist=30. I've got the page path set as "/town%". It doesn't like that apparently.
John

RdeBoer’s picture

Set the Views path to /town. The percent sign means that it will insist on a Views argument, which is different from the ? mark bit aka query string.

johnhorning’s picture

Okay - got the page display to show up with url http://town1.biz/town?lat=40.96367&lon=-81.33122&dist=30, but the block isn't there. It will show up when I check "Display all results for the specified field", but not when I check "Provide default value" and use this value:
return $_GET['lat'] . ',' . $_GET['lon'] . '_' . $_GET['dist'];

But I have another question. My understanding is that block displays cannot "see" the arguments in the url. Why else would we see the following note in the contextual filter settings?
This display does not have a source for contextual filters, so no contextual filter value will be available unless you select 'Provide default'.

If that's the case, why wouldn't a my original "return arg(1);" work as the default value?

RdeBoer’s picture

Well I"m a little confused now, as it works for both Block and Page displays in my View.
And indeed your method, return arg(1); works too on my system http://localhost:8888/drupal7/best-deals-in-town/-37.7,144.98_12.7

Are the visibility settings of your block correct? Have you appended an asterisk to cater for any arguments that may be appended?

johnhorning’s picture

I do have the asterisk in the block path settings. The block shows up fine if I set the view to "Display all results".
I did discover a small bug - the settings for "Select locations" and "Unit of distance" can't be changed to anything other than "Inside radius" and "kilometers", respectively.

I tried various radii (distances) Also, instead of PHP Code for the default value, I tried "Raw value from URL" (don't know why I hadn't tried that one before). Also tried various separators for the values for latitude, longitude, and distance - spaces, commas, and underscores. No luck getting the block to show up on my views page display with any setting other than "display all results".

However, some good news! - I had success with a contextual filter in my block for address->locality, which should work fine for my purpose. I have a link in the block display header that goes to a page display with exposed filters, including one for geofield proximity. I passed the coordinates and distance with the URL and use your contextual proximity filter to give the view a default state. You can see where I'm going here: http://town1.biz/town/Hartville/OH/40.96367,-81.33122_30

I will still likely need your services to help me auto-generate the URLs.

johnhorning’s picture

Issue summary: View changes

Mention bug-fixed version of handler.

RdeBoer’s picture

Hi John,
I fixed the bug you mentioned. Thanks for pointing it out. Functionally everything was alright, it was just always showing the default option on the form. The bug-fixed version is attached. Change extension to .inc and drop it over the top of the existing version in geofield/views/handlers. No need to clear caches or run update.php.

Why your block doesn't show is a mystery. As you can see from the attached image, YOUR solution, return arg(1);, works on MY system: a View as a block, filtered by proximity to a reference point. In my case the View is rendered as a map. I added a Views Attachment display also for handy debug.
In this example the reference point, appended to the URL together with a proximity of 1.5km, is the blue marker, a shop called David Jones.
See the URL in the browser address bar in the image.
I've added a diagnostic message to the contextual filter default argument PHP code, so that I can see when the contextual filter kicks in. It will print a status message like this:

  args = Array ( [0] => best-deals-in-town [1] => -37.8136792,144.964818_1.5 ) 

You may want to try that on your system.

Contextual Filter settings

Map driven by contextual filter in URL

I'm glad you have success with the address locality, which is along the lines of what I suggested here https://drupal.org/node/2018565#comment-7542041

However, now you're passing two types of position (locality and lat,lon), which is more than is required. Wouldn't you want the locality converted internally to lat,lon ? I can help you with that.

johnhorning’s picture

Thanks for the code update. And yes I know you had suggested using addressfield before as the contextual filter. Also, yes the URL is a bit unwieldy - I could use some help with that - will private message you.
John

johnhorning’s picture

Issue summary: View changes

mention Geofield extension for Global Filter

RdeBoer’s picture

The extended version of geofield/views/handlers/geofield_handler_argument_proximity.inc (see #22 below for attachment, to go with the original patch at the top of this issue) allows you to use geo-filtering URLs like:

yoursite.com/yourview/Sydney/Opera House
yoursite.com/yourview/sydney opera house/10 (the last argument specifies the proximity radius)
yoursite.com/yourview/Australia 3182 (3182 is the postcode of a suburb in Melbourne called St. Kilda)
yoursite.com/yourview/1000 Australia (Adelaide)
yoursite.com/yourview/Portland (defaults to Portland, Oregon)
yoursite.com/yourview/Indiana/Portland/1.5
yoursite.com/yourview/IN/Portland
yoursite.com/yourview/Portland IN

It does this by geocoding the appended arguments (slashes optional), using Geocoder and then passing the resulting lat, lon and dist values to the query builder as before.

As with any View, when NO arguments are provided, a PHP code snippet can be provided to be executed (under "Provide default value").
This can be used to default to the visitor's current location as the center of the proximity circle, with only an optional distance as an argument:

www.yoursite.com/yourview
www.yoursite.com/yourview/10

The PHP snippet below works for Views with Page displays as well as Block displays (requires IPGV&M)

if (arg(1)) {
  if (is_numeric(arg(1))) {
    $dist = arg(1);
  }
  else return arg(1);
}
$location = ip_geoloc_get_visitor_location();
if (empty($location)) return 'all';
$lat_lon = $location['latitude'] . ',' . $location['longitude'] ;
return empty($dist) ? $lat_lon : $lat_lon . '_' . $dist;

As a final bonus the latest IPGV&M dev snapshot, 7.x-1.x-dev, uses the new hook provided by geofield/views/handlers/geofield_handler_argument_proximity.inc to display the location of the geocoded arguments as the "visitor" marker on the map, thus giving a visual indication of what the argument, e.g. "/Oregon" was geocoded to.

RdeBoer’s picture

Attachement belonging to comment #21 above...

RdeBoer’s picture

Issue summary: View changes

Mention improved version of geofield_handler_argument_proximity.inc that supports addesses as contextual fitler args.

RdeBoer’s picture

Extended the patch of #22 so that the Geofield proximity Field has an option to calculate distances with respect to the Contextual Filter argument (ie. the arguments in the URL or a lat,lon provided by PHP Code or Fixed Value).
This makes the filtering distance consistance with the distances shown for each Views result item.

One new patch file to replace the previous.
Two new .txt files, just drop these in the correct directories, changing extensions to .inc:
geofield/views/handlers/geofield_handler_argument_proximity.inc
geofield/views/proximity_plugins/geofieldProximityContextualFilter.inc

RdeBoer’s picture

Issue summary: View changes

typos

RdeBoer’s picture

Sorry, better version of geofield_handler_argument_proximity.txt in comment #23 above... Fixes a bug.
Patch file and second .txt file remain the same.

Brandonian’s picture

Status: Active » Fixed

Nice work, @RdeBoer! I committed the patch/attachments in #23/24 with minor modifications. Specifically, I updated the help text in the contextual plugin to help better identify that the Geocoder module is needed to use a string instead of lat/lon components, and I updated the argument handler to use geofield_radius_options() instead of manually adding the various units of measure.

http://drupalcode.org/project/geofield.git/commit/afdf4fe7785c82e5dfa164...

RdeBoer’s picture

Great Brandon!
Geofield is going from strength to strength!
Rik

johnhorning’s picture

FileSize
59.25 KB

Thanks for the great work here. I have a question that's more related to the exposed geofield proximity filter, but it's also related to the contextual geofield proximity filter. I'm using both and would like to have the source address on the exposed filter filled in automatically, based on the URL argument. Please see attached image. Any suggestions?
John

RdeBoer’s picture

@JohnHorning, #27:

Hi John,
A you know from a previous conversation, functionally this already exists in that the contextual filter value is indeed used upon initial page load (when the exposed filter isn't set yet),. After this the contextual filter retreats to let any further filtering take place via the exposed filters.

This functionality is implemented by the Views Filter Harmonizer module : http://drupal.org/project/filter_harmonizer.

So the initial page is properly filtered, it just doesn't make that fact obvious... So I understand your request.

The only bit missing from the FIlter Harmonizer is it taking the contextual filter value, which it already knows internally, and then pushing that onto the exposed filter, which it is also already aware of, during initial page load.

This should/could be straightforward for single value exposed filters, but more laborious for cases where the contextual filter is a single string (as it always is), while the exposed filter expects more than one value. An example is the "BETWEEN x and y" exposed filters.

Similarly, in the Geofield case, the contextual filter is a "compound" string containing location and proximity parts, which need to be separated and then pushed into the appropriate boxes on the page.

All in all doable... but a little ugly as Filter Harmonizer will have to be taught all these special exposed filter cases, starting with... Geofield, I guess...

Rik

johnhorning’s picture

hmm... I was afraid you were going to say something like that.

You gave me an idea, though. Since the value is already there, how about if I simply hide the field so the user doesn't feel he needs to enter a value? It seems to work in my test case.

RdeBoer’s picture

#29:
You mean using jQuery?
Sure, what will be the trigger to reveal the exposed filter field again?
Rik

johnhorning’s picture

Actually I was thinking of just using css, but never mind - it's not working after all. It defaults to all results if I don't enter a source value. If there was a way to enter a token or php code into the source field in the filter settings, that could work, although I would prefer to hide the source so the user just changes the distance.

RdeBoer’s picture

Status: Fixed » Active
FileSize
2.26 KB

Extension to the contextual filter plugin patch already committed in #25 of this thread.

This patch makes the function get_default_argument() more complete and fixes a couple of notices that occasionally popped up during my testing of this plugin/handler.

EDIT: the patch below is better

RdeBoer’s picture

Improvement... use the attached patch below in #34, instead of the patch from #32 or #33

This does what the patch from #32 does, but also allows the Geofield Exposed Proximity filter to work with the Geofield Contextual Proximity filter used as a fallback, so that visitors don't have to fill out the reference/origin, just the distance.

So you can have a URL like www.mysite.com/Hartville/Ohio/10 to filter a View page on locations within a radius of 10 miles of Hartville OH. The page may now have a Geofield exposed proximity filter on it so that visitors can subsequently change the distance to zoom-in further on the default location. For this to work don't forget to specify a default proximity on the exposed filter panel and to select "Contextual Geofield Proximity Filter" as the Source of Origin Point.

For all of this to work, in the patch I had to remove the validation rule that insistis that both distance and origin fields must not be blank.

Note that due to the inherent nature of Views in Drupal7 exposed and contextual filters will both remain active while the visitor is on the page. So to avoid confusion you may want to hide the origin field using CSS. Or install Views Filter Harmonizer.

Rik

RdeBoer’s picture

I'll get it right one day...

johnhorning’s picture

Thanks, Rik. Should this be applied to 7.x-2.x-dev?

RdeBoer’s picture

@johnhorning, #35:
Yes, the 7.x-2.x branch.
Rik

PS: then when done, try this: https://drupal.org/node/2037703#comment-7638733

RaulMuroc’s picture

I think would be better to put efforts on the following project:

Geofield Proximity Filter

That becomes a stable project :) and solves our problems with proximity filtering!

pixelsweatshop’s picture

@RaulMuroc, can you explain how that sandbox project helps with contextual proximity filters in views?

RaulMuroc’s picture

@nicoz, It is explained in the sandbox's project:

This module integrates with the Search API Solr server to provide a Views proximity filter for Geofield fields.

RdeBoer’s picture

Re #39:
So is it a regular (eg exposed) proximity filter or a contextual proximity filter?

pixelsweatshop’s picture

Rik is correct. My question is was about contextual filters, not regular exposed filters.

pixelsweatshop’s picture

Issue summary: View changes

Mention patch in #23

okeedoak’s picture

Issue summary: View changes
mamanerd’s picture

Okay, I've been trying to get patch #34 to work for hours now, but it doesn't want to seem to cooperate. Maybe I'm doing something wrong. I'm trying to set up a block via Views to be used on user profiles that will show all nodes within 10 miles of the user's address. My nodes have a geocoded field based on a separate addressfield, and my user profile also has a geocoded field based on a separate addressfield.

Within the block display from the Views UI, I tried adding a contextual filter for the node's geofield - proximity field. I understand that I can dynamically provide the lat/lon coordinate default values with PHP code, but I've been trying to just test it using a fixed value. So under "provide default value", I selected Fixed value and put in 40.5884,-73.6579. Then for "default radius" I put 10, and "unit of distance" I selected miles.

After saving changes, the view returns 0 results, even though I know there are nodes within 10 miles of that lat/lon. What am I doing wrong?

chrislabeard’s picture

I'm trying to use contextual filters and its returning 0 results when I use lat, long distance. Did someone get this working?

Anonymous’s picture

Is this plugin code included in the current stable or dev version? There is a contextual filter included but i'm having trouble with it. When adding the filter everything works as expected while using the "Views UI Preview". It translates entered cities perfectly into proximity fields.

Adding the view programmatically within a module and entering cities there, it always renders 0 distance even if it has the city name. I've traced it with "!1" replacement of the field. The query itself is missing any distances it has in the preview.

Anyone got this working?

joelpittet’s picture

re #34

+++ b/views/handlers/geofield_handler_filter.inc
@@ -218,7 +218,7 @@ class geofield_handler_filter extends views_handler_filter_numeric {
-    if (empty($input[$input_id]) || $input[$input_id]['distance'] === '' || $input[$input_id]['origin'] === '') {
+    if (empty($input[$input_id]) /*|| $input[$input_id]['distance'] === '' || $input[$input_id]['origin'] === ''*/) {

This looks a bit suspect, is this meant to be commented out like this?

jaylotta’s picture

I cannot for the life of me get this feature to work at all.

I have setup the contextual filter using both fixed value and PHP code.

This is the address that the google geocoder plugin is receiving...

'structure, views, nojs, preview, showrooms, page_2'

Definitely not the fixed value or 'New York' which I specified.

Perhaps I'm not setting up the filter criteria in the field properly.

Could someone please upload a working view that supports this feature so I can see the configuration.

SocialNicheGuru’s picture

Status: Active » Needs review

Is this patch still needed in geofield? The issue https://drupal.org/node/2037703#comment-7638733 is closed/fixed.

patch -p1 < con*h (from 34 yields against geofield 7.3 http://ftp.drupal.org/files/projects/geofield-7.x-2.3.tar.gz)
patching file views/handlers/geofield_handler_argument_proximity.inc
Hunk #3 FAILED at 213.
1 out of 3 hunks FAILED -- saving rejects to file views/handlers/geofield_handler_argument_proximity.inc.rej
patching file views/handlers/geofield_handler_filter.inc
Hunk #1 succeeded at 226 (offset 8 lines).
patching file views/proximity_plugins/geofieldProximityContextualFilter.inc

Summit’s picture

Hi, what is the status of this issue please?
thanks for your reply in advance!
greetings, Martijn

FireHawkX’s picture

I have also been trying for a couple of weeks to get any kind of proximity search to work in my particular use case with zero success so far...

I have a geocoded field (which has lat/long/etc) in a node type, (i want this to be the center for promity search in the view)

Then i have another node type which also has its own geocoded, and about 3000 entries (businesses),

when i open the view page (map leaflet or ipgvm), i simply want to display the closest business from the center point...

No matter what I tried, nothing worked, I'm going to try these patches in the next week and see if i can make it work...
(Sorry for writing here since there was zero activity in the past 5 years, but this seems like the most promising i have seen as of yet)

Edit : previous posts were right as this does not apply to latest dev... seems like a couple (at least 2) of the changes in the patch #34 were added since in the latest dev... (applying manually for now to test it out and see if everything breaks)

Summit’s picture

Hi FireHawkX,
Looking forward to your results. I gave up on getting it to work with Drupal 7.
greetings, Martijn

FireHawkX’s picture

I did manage to get the patch to apply... and following that I was able to get the exposed Lat/Long in the URL and get those to work in the contextual filter...
HOWEVER, after achieving that, I removed the patch (replaced the module with the version I had prior), and it still did the exact same thing...
So, maybe the important part of the patch were already in the latest dev version?
I honestly dont know... But since I am unsure, I will not be using this patch as it doesnt seem to be needed to work.
Still, using exposed Lat/Long as contextual filter was an approach I did not think about previously,
So THANK you very much for the idea!

Now If only I could display both the source node AND the many-proximity ones at the same time
(right now I can only display one or the other, but not both node types at once)