Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQL query to calculate coordinate proximity

Tags:

sql

geocoding

I'm using this formula to calculate the distance between entries in my (My)SQL database which have latitude and longitude fields in decimal format:

6371 * ACOS(SIN(RADIANS( %lat1% )) * SIN(RADIANS( %lat2% )) + 
COS(RADIANS( %lat1% )) * COS(RADIANS( %lat2% )) * COS(RADIANS( %lon2% ) - 
RADIANS( %lon1% )))

Substituting %lat1% and %lat2% appropriately it can be used in the WHERE clause to find entries within a certain radius of another entry, using it in the ORDER BY clause together with LIMIT will find the nearest x entries etc.

I'm writing this mostly as a note for myself, but improvements are always welcome. :)

Note: As mentioned by Valerion below, this calculates in kilometers. Substitute 6371 by an appropriate alternative number to use meters, miles etc.

like image 648
deceze Avatar asked Sep 18 '08 09:09

deceze


People also ask

How do I calculate distance using latitude and longitude in SQL?

Try the below code: SELECT latitude, longitude, SQRT( POW(69.1 * (latitude – [startlat]), 2) + POW(69.1 * ([startlng] – longitude) * COS(latitude / 57.3), 2)) AS distance.

How can I find the distance between two points in MySQL?

Calculating Distance in MySQL To get the distance between two points, you call the function with the two points as the arguments: -- Returns distance in meters.

How do you find the distance between two GPS coordinates?

For this divide the values of longitude and latitude of both the points by 180/pi. The value of pi is 22/7. The value of 180/pi is approximately 57.29577951. If we want to calculate the distance between two places in miles, use the value 3, 963, which is the radius of Earth.


1 Answers

For databases (such as SQLite) that don't support trigonometric functions you can use the Pythagorean theorem.

This is a faster method, even if your database does support trigonometric functions, with the following caveats:

  • you need to store coords in x,y grid instead of (or as well as) lat,lng;
  • the calculation assumes 'flat earth', but this is fine for relatively local searches.

Here's an example from a Rails project I'm working on (the important bit is the SQL in the middle):

class User < ActiveRecord::Base
  ...
  # has integer x & y coordinates
  ...

  # Returns array of {:user => <User>, :distance => <distance>}, sorted by distance (in metres).
  # Distance is rounded to nearest integer.
  # point is a Geo::LatLng.
  # radius is in metres.
  # limit specifies the maximum number of records to return (default 100).
  def self.find_within_radius(point, radius, limit = 100)

    sql = <<-SQL
      select id, lat, lng, (#{point.x} - x) * (#{point.x} - x) + (#{point.y} - y) * (#{point.y} - y) d 
      from users where #{(radius ** 2)} >= d 
      order by d limit #{limit}
    SQL
    
    users = User.find_by_sql(sql)
    users.each {|user| user.d = Math.sqrt(user.d.to_f).round}
    return users
  end
like image 165
David Avatar answered Sep 29 '22 11:09

David