# How to compute distances based on zip codes

+ 3

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"]```

Tags:
0