a work on process

Viewing posts tagged: GeoKit

Graticule and acts_as_geocodable, which I wrote about here, has a new release with a couple of updates worthy of note. First up is the addition of new geocoders which provide better coverage of locations outside North America, and second is IP address to co-ordinates conversion similar to that in GeoKit.

Brandon has the lowdown, and I’ve updated the Comparison chart on the wiki.

Recommend this post:

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

 

:select and :include in ActiveRecord queries

20 March 2007 (3:40 am)

By James Stewart
Filed under: Notes
Tagged: , ,

Along with Bill Eisenhauer, I’ve been digging into what it would take to fix the problem I found with GeoKit, that it wouldn’t support :include queries properly. The explanation has seen us going deep into the internals of ActiveRecord to discover, as others have before, that the :select parameter (which GeoKit uses to build its distance column in the query) and the :include parameter don’t play nicely together.

The reason for that is that Rails employs special methods to build the select parameters when there’s a :include in the query (ie. when you’re joining with other tables and eager-loading them) and currently ignores :select along the way. GeoKit would ordinarily produce SQL along the lines of:

SELECT *, 
	SQRT(
		POW(69.1*(42.962927-latitude), 2) +
		POW(42.0590342306803*(-85.637179-longitude), 2)
	) AS distance 
FROM bus_stops 
ORDER BY distance ASC LIMIT 1

but with a :include on a has_many relationship, we don’t get that distance column and so most of the queries will fail.

There are a couple of patches on the rails trac that seek to clean that up, but so far neither has been adopted. I’m sure the GeoKit developers would appreciate any input on workarounds.

Recommend this post:

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

 

Rails Geo Plugins: GeoKit

12 March 2007 (12:52 pm)

By James Stewart
Filed under: Notes
Tagged: , , , , ,

There’s quite a bit of overlap between GeoKit and acts_as_geocodable/graticule, as the latter pair were based on GeoKit. But it provides at least one feature (IP-based location lookup) that they don’t, so I decided to give it a whirl.

Since my main geographically related projects are both now based on plugins that I’m pretty happy with and which suit them well, I decided to resuscitate an old sample piece. A few months back I wrote about scraping the Grand Rapids bus routes site and put up a toy application utilising the resource features in then-edge Rails. I’ve been meaning to return to that project to test out some features in ActiveResource, but in the meantime it seemed like it might be useful to be able to search for the nearest bus stop.

The plugin comes with an extensive README file and getting up and running is very straightforward:

class BusStop < ActiveRecord::Base
	acts_as_mappable :default_units => :kms, 
		:default_formula => :flat, 
		:distance_field_name => :distance,
		:lng_column_name => 'longitude', :lat_column_name => 'latitude'

I had to add the column_name parameters to signify which database columns I was using, as the plugin defaults to using ‘lat’ and ‘lng’ for brevity. The inclusion of the default_units parameter is a nice one, but it would also be nice if the plugin provided an accessor method to convert distance on the fly to help developers localise their apps.

With that done I get access to a suite of methods for doing location searches. So if I wanted to find the nearest bus stop to Common Ground Coffee Shop I could call:

BusStop.find(:nearest, 
	:origin => '1319 Fulton St. East, Grand Rapids MI 49503')

That address actually failed in the google geocoder (probably because I had yet to enter my api key), but GeoKit automatically fell back to geocoder.us and got the co-ordinates.

Where it seemed to fall over was when I tried to limit by bus route:

BusStop.find(:nearest, :origin => '1319 Fulton St. East, Grand Rapids MI 49503',
	:include => :route_stops, 
	:conditions => 'route_stops.bus_route_id = 6')
 
ActiveRecord::StatementInvalid: Mysql::Error: Unknown column 'distance' in 'field list': SELECT DISTINCT bus_stops.id, distance FROM bus_stops  LEFT OUTER JOIN route_stops ON route_stops.bus_stop_id = bus_stops.id WHERE (route_stops.bus_route_id = 6) ORDER BY distance ASC  LIMIT 1

Andre Lewis, one of the plugin’s developers (along with Bill Eisenhauer), tells me that GeoKit doesn’t currently support the :include option or conditions on join tables. He’s hoping to add that soon, but for now you’re limited to a single table.

Implementing the IP-based geolocation was very easy. A before_filter is included which can be included in a controller with the declaration:

geocode_ip_address

will return a GeoLoc object containing details of the lookup source, the address and the co-ordinates. I chose to add my own before_filter so I could find the nearest bus stop:

class ApplicationController < ActionController::Base
  before_filter :find_nearest_stop
 
  def find_nearest_stop
    @nearest = BusStop.find_nearest(:origin => get_ip_address)
  end
end

(where get_ip_address is a method provided by the plugin). I had to deploy to a remote host to actually test this as 127.0.0.1 obviously doesn’t work as a source IP address for these purposes, and it’s likely that a number of users working through proxies of one sort or another will find the information less than accurate. But so long as that’s clear, it’s a nice feature to add to local information services, potentially getting users to relevant information more quickly.

I do like the separation between gem and plugin that acts_as_locateable offers, the fact that geocodes are stored in a separate table, and full join support for queries. But the IP translation from GeoKit is also a nice feature. Both are highly capable solutions, so it’s likely that once GeoKit adds full join support, choosing between them may well come down to personal taste.

(Nb. Those following along with these posts may be interested in GeoKit co-author Andre Lewis’ book Beginning Google Maps Applications with Rails and Ajax. I’m hoping to check it out soon)

Recommend this post:

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

 

There seems to be quite a plethora of Ruby/Rails libraries appearing aiming to simplify handling geography and distances. In some cases these libraries do quite distinct things (zip codes vs. longitude/latitude, map output vs. distance calculations) but they’re frequently lumped together and it’s difficult to tell which will be best to use in your projects.

I’ve used several of these projects and have previously blogged about YM4R and acts_as_locateable, but I’m still not sure which I’d pick for new projects. So I thought it would be helpful to try to put together a comparison of which libraries offer what functionality. Here I’ll just offer a quick chart, but I’m hoping to write them up in a bit more detail over the coming days/weeks. If there’s sufficient interest, I’d consider moving this out to a wiki for more general use.

Rails Geo Plugins
  Auto-geocoding ActiveRecord models Multi-provider geo-coding Distance based finds Distance based :through finds Google map output Yahoo map output
GeoKit

(plugin index)
Y Y Y N N N

acts_as_geocodable


(
plugin index
)
Y Y* Y N N N

acts_as_geocode


(plugin index)
Website currently down

actsaslocateable


(plugin index)
N N Y Y N N

YM4R


(plugin index)
N N N N Y Y

(* through companion gem)

Also worth mentioning are SpatialAdapter and GeoRuby (from the same developer as YM4R) which respectively provide ActiveRecord support for MySql Spatial and PostGIS geometric columns, and ruby data types for that information.

A recent addition is this Ruby library for the Geonames API which provides a nice way to interface with the GeoNames database of 2.2 million populated places. Properly harnessed, that service begins to make it possible to allow your users to describe their location in natural language and convert that into machine-parseable co-ordinates.

Update (8th March): Updated versions of this comparison will now appear on the foss4r wiki

Update (October 2008): The chart now lives on a separate page within this site.

Recommend this post:

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]