I wrote a function in my Rails app that has isn't passing a smell test and I'm unsure how to refactor.
Currently, a user is able to enter in an an address into a form and my goal is to spit out any delivery companies that are within a given delivery radius. Right now that looks like this:
class Dealers < ActiveRecord::Base
validates :name, presence: true
validates :delivery_radius, numericality: { only_integer: true }
end
so a search comes into my controller and I call a method I wrote (available_deliveries
) like this:
@dealers = Dealer.available_deliveries(Geocoder.coordinates(search_params))
search_params
is just street, city & state.
my Dealer#available_deliveries
method looks like this:
def self.available_deliveries(geo)
dealers = []
Dealer.all.each do |dealer|
if dealer.distance_from(geo) <= dealer.delivery_radius
dealers << dealer
end
end
dealers
end
YIKES that's ugly. I'm unsure how to do a where
SQL statement to get the same results...
Depending on your backing database, I suggest you use its abilities for indexing according to geo-spatial distance.
Finding Records in a Radius
Another great function provided by these extensions is the
earthbox(lltoearth($latlngcube), $radiusinmetres)
this function allows us to perform a simple compare to find all records in a certain radius. This is done by the function by returning the great circle distance between the points, a more thorough explanation is located at http://en.wikipedia.org/wiki/Greatcircle.This could be used to show all events in our current city.
An example of such a query:
SELECT events.id, events.name FROM events WHERE earth_box( {current_user_lat}, {current_user_lng}, {radius_in_metres}) @>
ll_to_earth(events.lat, events.lng);
We are using
double
to storelatitude
andlongitude
. In addition we precompute (by triggers) all values which are precomputable when looking at one point only. I currently don't have access to the formula we are using, will add this later. This is optimized for an optimal speed / precision balance.