Enable Distance Filtering in Drupal Services

When developing mobile apps, location is one of core components that nearly all mobile apps are developed with.  However, when it comes to building mobile apps with Drupal, working with location can be challenging.  There are number of geo-related modules and discussions on enabling location by working with Views.  But in our case, we decided not to use Views, but just with the Services modules, to provide the maximum performance to mobile apps.

So in this post, I will show you how we enabled distance filtering/sorting in Drupal so that the XML/JSON data you get through Services module is filtered or sorted based on locations or latitude/longitude data.

For this, we used following tools/modules:

  • Postgresql database
  • PostGIS extension
  • Services module
  • AddressField module
  • Geocoder & Geofield modules

Postgresql – we use Postgresql instead of MySQL because working with locations is much easier and faster by using PostGIS extension.  Yes, it “is” possible to enable locations with MySQL, but for this exercise, we will use Postgresql and PostGIS combination.

1. Install PostGIS extension

There are numerous websites and pages that show you how to install the PostGIS extension so I won’t cover that here.

2. Install Services, AddressField, Geocoder, Geofield modules

These are all standard Drupal contributed modules that are fairly stable.  If you’ve been using Drupal, then these modules should be familiar to you.  If not, you can easily install them using drush:

3. Create a new field for storing address

Now that we have everything installed, we will go ahead and create a new content type and field to store location information.

We will create a new content type called “Business” and two fields: Address (postal address) and Geodata (geofield) for storing location information such as Well Known Byte (WKB), latitude and longigude. You have to set up Geodata field as “automatically encode from another field” and choose the address field.

Location modules in Drupal

Now, if we look at the database table that holds the field_geodata information, there are three columns that we are interested in and will be using for our exercise: field_geodata_geom, field_geodata_lat, and field_geodata_lon.

Underlying table for Geofield

Your table at this point would be empty. I took above screenshot after adding some sample businesses.

4. Create a custom REST endpoint

For our purpose, we will be exporting businesses from a Drupal site to XML/JSON using the Services module which will be consumed by client apps such as mobile applications. We will create one index function that will retrieve all businesses.

First, create your own module. Then, create a custom REST endpoint using using MODULE_services_resources() to create your own endpoint.  I called my module “isaac”:

There are some great tutorials on how to create custom REST endpoint:

We will review three PostGIS functions used in above code:

  • ST_Distance – Given two geometry points, this function returns distance between two points in spatial ref units
  • ST_MakePoint – Given latitude and longitude, this function returns a point geometry that can be used in ST_Distance and other PostGIS functions
  • ST_GeomFromWKB – Creates a geometry instance from a Well-Known Binary geometry representation (WKB)

So what’s happening here is that ST_Distance() will return distance values that we can use for filtering or sorting in our code. ST_Distance() takes two geometry points – user’s location and business location.

For user’s location, we pass in lat/long coordinates through URL and convert it to geometry point using ST_MakePoint() function.

For business location, we can do same with values from field_geodata_lat and field_geodata_lon columns or we can use field_geodata_geom value. Geofield module stores Well-Known Binary (WKB) data in that column and we use ST_GeomFromWKB() function to convert that into a geometry point.

Results

We will test with following four businesses – also indicated in red marker on below map:

  1. Atlantic Coast Mortgage LLC – 4100 Monument Corner Dr, Fairfax, VA 22030
  2. Silver Spoon Caterers – 12450 Fair Lakes Cir, Fairfax, VA 22033
  3. Allegra Fairfax – 2812 Merrilee Dr, Fairfax, VA 22031
  4. Capitol Financial Partners – 1593 Spring Hill Road, Vienna, VA 22182

The user will be in Fairfax – indicated in blue marker on the map.

Build mobile apps with Drupal

We then call the endpoint we created above with latitude/longitude of user’s location (blue marker) as parameters:

We then get following results:

As you can see from the distance values, the four businesses were retrieved based on lat/long location data sent in as parameters. They are sorted by distance from shortest to farthest.

As mentioned above, ST_Distance() function will return distance in spatial ref units. If you want it in meters, you can use ST_Distance_Sphere() in place of ST_Distance(). Make sure to check out the PostGIS documentation if you want to see what other functions are avaialble.

Conclusion

So in this post, I wanted to share my proof of concept that location feature can be made available in Drupal when it’s being used as a mobile backend – that is, without using Views.

We only passed in latitude and longitude to our custom REST endpoint but we can also add “limit” and “offset” param/value pairs to enable paging so that you get only 20-30 businesses at a time if there are a lot of businesses.