SQL-запрос для вычисления координатной близости - PullRequest
7 голосов
/ 18 сентября 2008

Я использую эту формулу для вычисления расстояния между записями в моей (My) базе данных SQL, которые имеют поля широты и долготы в десятичном формате:

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

Подставляя% lat1% и% lat2% соответственно, его можно использовать в предложении WHERE для поиска записей в пределах определенного радиуса другой записи, используя его в предложении ORDER BY вместе с LIMIT, чтобы найти ближайшие x записей и т. Д.

Я пишу это в основном для заметки, но улучшения всегда приветствуются. :)

Примечание. Как уже упоминалось ниже, Валерион рассчитывает километры. Замените 6371 на соответствующий альтернативный номер , чтобы использовать метры, мили и т. Д.

Ответы [ 4 ]

7 голосов
/ 23 сентября 2008

Для баз данных, которые не поддерживают trig (например, SQLite), вы можете использовать пифагор. Даже если ваша база данных поддерживает триг, пифагор гораздо быстрее. Предостережения:

  • вам нужно хранить координаты в сетке x, y вместо (или также) lat, lng;
  • в расчете принимается «плоская земля», но это хорошо для сравнительно локальных поисков.

Вот пример из проекта Rails, над которым я работаю (важный бит - SQL в середине):

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
2 голосов
/ 18 сентября 2008

Прав ли я, думая, что это формула Хаверсина?

1 голос
/ 24 июля 2012

Я использовал это, забудь, где я получил это все же.

SELECT n, SQRT(POW((69.1 * (n.field_geofield_lat - :lat)) , 2 ) + POW((53 * (n.field_geofield_lon - :lon)), 2)) AS distance FROM field_revision_field_geofield n ORDER BY distance ASC
1 голос
/ 18 сентября 2008

Я использую точно такой же метод в приложении для отслеживания транспортных средств и делал это годами. Работает на отлично. Быстрая проверка какого-то старого кода показывает, что я умножаю результат на 6378137, который, если память служит, преобразуется в метры, но я не трогал его в течение очень долгого времени.

Я полагаю, что в SQL 2008 появился новый пространственный тип данных, который, как мне кажется, позволяет проводить такого рода сравнения без знания этой формулы, а также позволяет использовать пространственные индексы, которые могут быть интересными, но я не рассматривал их.

...