Calculate Latitude/Longitude Distances in MySQL with the Haversine Function
tumblr.jonthornton.com
tumblr.jonthornton.com
Since then, MongoDB 1.7 has been released with spherical sort support (http://www.mongodb.org/display/DOCS/Geospatial+Indexing#Geos...)
I swear - next time I'm going to get a R-Tree novelty account.
Also, thing is filtering is not much of a problem as you can use the Haversine formula ... it gets to be a problem if you also want sorting from closest to furthest.
Example: http://www.god-object.com/2009/10/20/geospatial-search-using...
Yep, I just reinstalled an old app yesterday, and for some unfathomable reason it loses communication with mysql at some point (it worked perfectly for what, 6 years?). Gosh, I hate mysql.</rant>
Any other ideas?
One solution I saw used more simple arithmetic to calculate a range of coordinates within levels of distance. That could be pre-cached, but it's a lot less accurate.
(Edit: to be more specific, you can get a pretty good distance measurement using Geohash and comparing strings. Obviously, indexing strings is something databases do well. The exact distance a single character corresponds to depends on longitude & latitude, but there are lookup tables for that. There are also edge conditions to be aware of which may affect your application)
Or, use Postgres which has geospatial indexes.
Using a B-Tree on a Geohash (like MongoDB does) is a bit more efficient that just indexing min/max values, but not by much. MySQL, PostgreSQL and even SQLite have R-Tree indices that perform 10x better.
If you're already using MongoDB, it's really dead-easy to setup (see the docs).
You can basically reduce the filtering done to something like: WHERE lat BETWEEN val1 AND val2 AND lon BETWEEN val3 AND val4.
So indexing will work.
What I'm doing, given a max distance and a search point, is to calculate the bounding box in which I want to search in filter results with
WHERE lat BETWEEN lat_min AND lat_max AND lng BETWEEN lng_min AND lng_max
Calculating latitude min/max is trivial knowing that 1 latitude degree is 111.2KM. Longitude is a bit more convoluted because longitude degree size changes moving north/south.
$lat_min = $lat - $range_km * (1 / 111.2); $lat_max = $lat + $range_km * (1 / 111.2);
$k = $range_km/6371.04; $lng_min = $lng - rad2deg($k/cos(deg2rad($lat))); $lng_max = $lng + rad2deg($k/cos(deg2rad($lat)));