2008-09-18 10 views
7

Używam tego wzoru obliczyć odległość między wpisami w moim (My) bazy danych SQL, które mają szerokość i długość pola w formacie dziesiętnym współrzędnych:zapytań SQL do obliczenia zbliżeniowej

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

Podstawiając% lat1% i % lat2% odpowiednio może być użyte w klauzuli WHERE do znalezienia wpisów w określonym promieniu innego wpisu, użycie go w klauzuli ORDER BY wraz z LIMIT znajdzie najbliższe x wpisy itp.

Piszę to głównie jako notatkę dla mnie, ale ulepszenia są zawsze mile widziane. :)

Uwaga: jak wspomniano poniżej, Valerion oblicza to w kilometrach. Zastąpić 6371 przez appropriate alternative number, aby korzystać z liczników, mil itp.

Odpowiedz

2

Czy mam rację, myśląc, że to formuła Haversine?

+0

Znalazłem go jako "Formułę dystansu Wielkiego Koła", ale patrząc na to, tak, być może. – deceze

1

Używam tej samej metody w aplikacji śledzenia pojazdów i robię to od lat. Działa doskonale. Szybkie sprawdzenie starego kodu pokazuje, że pomnożę wynik przez 6378137, co jeśli pamięć służy do konwersji na metry, ale nie dotykałem go przez bardzo długi czas.

Wierzę, że SQL 2008 ma nowy typ danych przestrzennych, który, jak sobie wyobrażam, pozwala na takie porównania bez znajomości tej formuły, a także pozwala na przestrzenne indeksy, które mogą być interesujące, ale nie przyjrzałem się temu.

7

Dla baz danych, które nie obsługują trig (takich jak SQLite), można użyć pythagorus. Nawet jeśli twoja baza danych obsługuje trygonometryczne, pythagorus jest znacznie szybszy. Ograniczenia są następujące:

  • musisz przechowywać współrzędne w siatce x, y zamiast (lub tak samo) lat, lng;
  • Obliczenie zakłada "płaską ziemię", ale jest to dobre dla względnie lokalnych wyszukiwań.

Oto przykład z projektu Rails pracuję nad (ważny bit jest SQL w środku):

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 
+0

Czy istnieje sposób na konwersję długości i szerokości geograficznej na współrzędne x, y? –

1

Używam tego zapomnieć, gdzie mam go jednak.

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 
Powiązane problemy