Following the “why did I make longitude a string” debacle of 2014, adding a radius search to my Rails app was ridiculously simple.
The goal was a simple “what locations are within X metres of these coordinates” search. In the past, I’ve done this with MySQL by basically putting the great circle calculation into the query, which isn’t pretty.
Using PostgreSQL, it’s a lot easier. I’ll admit, I made the switch because I was deploying to Heroku, but I’m liking psql a lot and this is just one more reason.
First, you’ll need to add two extensions to your database:
CREATE EXTENSION cube; CREATE EXTENSION earthdistance;
(FYI, I seem to recall seeing that the order matters here, but I didn’t take the time to try them the other way because… this one worked.)
Then, just to be nice, add an index (this is all assuming, as in my previous post, that you’ve got a locations table with float fields called longitude and latitude, change as needed for your app)
execute "CREATE INDEX geo_location on locations USING gist(ll_to_earth(latitude, longitude));"
Then you can add a scope for your model:
scope :within_radius, lambda {|latitude, longitude, metres| where("earth_box(ll_to_earth(?, ?), ?) @> ll_to_earth(latitude, longitude)", latitude, longitude, metres) }
And that’s basically it. You can call Location.within_radius(43.6422125, -79.3744256, 5000) and you’ll have a list of locations within 5 km of downtown Toronto.
As an aside: I’m rather pleased with how I named that parameter metres. I’ve run into this problem numerous times where the units aren’t clear, especially in time/duration functions (milliseconds? seconds? minutes?) so this way even I can figure it out (at a cost of having to decide between metres and meters, but using the American English version when the USA isn’t on the metric system anyway felt a bit off.)