Jump to content

How to compute distances based on zip codes

+ 3
  mike-loukides's Photo
Posted Oct 26 2009 12:20 PM

If you're writing a web application, and you want to find places (restaurants, theaters) within a certain distance from your user, you'll probably need to work with zip codes. Here's a good approximation from Enterprise Rails, by Dan Chak.

Zip code data is available from a variety of sources online, and with some massaging, can be easily imported into your database. As we discussed earlier, one of the primary benefits of having a zip code domain table is the ability to do distance calculations to answer questions such as, "What theatres are playing Casablanca within 10 miles of my home?" Almost all zip code databases available online provide latitude and longitude coordinates for zip codes. To facilitate calculating the answer to our question quickly, we create a stored procedure, miles_between_lat_long, which provides a reasonably good approximation of distance with a minimal set of complex calculations (note that this equation is an approximation only; if your application requires high-precision distances, you will want to use a better—but likely slower—formula):

create or replace function miles_between_lat_long(
  lat1 numeric, long1 numeric, lat2 numeric, long2 numeric
) returns numeric
language 'plpgsql' as $$
declare
  x numeric = 69.1 * (lat2 - lat1);
  y numeric = 69.1 * (long2 - long1) * cos(lat1/57.3);
begin
    return sqrt(x * x + y * y);
end
$$;



We can then add a method, zips_within_miles to our ZipCode model, which returns all of the zip code objects within a given distance from the zip in question:
class ZipCode < ActiveRecord::Base
  set_primary_key 'zip'

  def zips_within_miles(miles)
    ZipCode.find(:all,
                 :conditions => ["miles_between_lat_long(?, ?,
                       zip_codes.latitude, zip_codes.longitude) < ?",
                       self.latitude, self.longitude, miles])
  end
end


The following example, using script/console, finds all of the cities within two miles of Cambridge, Massachusetts:

>> z = ZipCode.find('02139')=> #<ZipCode:0x3463cc0
@attributes={"city"=>"CAMBRIDGE",
"latitude"=>#<BigDecimal:3463d10,'0.42365079E2',12(12)>, "zip"=>"02139",
"county"=>"MIDDLESEX", "state_abbreviation"=>"MA",
"longitude"=>#<BigDecimal:3463ce8,'-0.71104519E2',12(12)>}>
>> z.zips_within_miles(2).collect{|z| z.city}.uniq
=> ["BOSTON", "CHARLESTOWN", "ALLSTON", "BRIGHTON", "CAMBRIDGE", 
"BROOKLINE", "BROOKLINE VILLAGE"]


Enterprise Rails

Learn more about this topic from Enterprise Rails.

Enterprise Rails introduces time-tested software engineering principles to help you build a high-performance, scalable website with global reach. You'll learn how to design a solid architecture that ties the many parts of an enterprise website together, including the database, your servers and clients, and other services as well. Throughout this book, you'll work on an example enterprise project to learn first-hand what's involved in architecting serious web applications.

See what you'll learn


0 Replies