特定の緯度経度の場所から特定の距離内にあるすべての緯度経度の場所を見つけるアルゴリズム


86

40.8120390、-73.4889650など、緯度と経度の場所のデータベースがある場合、特定の場所から特定の距離内にあるすべての場所を見つけるにはどうすればよいですか?

DBからすべての場所を選択し、それらを1つずつ調べて、開始場所からの距離を取得し、指定された距離内にあるかどうかを確認するのは、あまり効率的ではないようです。DBから最初に選択した場所を絞り込む良い方法はありますか?絞り込んだ場所のセットを取得した後も(または取得しなかった場合)、距離を確認するためにそれらを1つずつ確認しますか、それとももっと良い方法がありますか?

私がこれを行う言語は実際には重要ではありません。ありがとう!


4
これはあなたが必要とするものかもしれません:en.wikipedia.org/wiki/K-d_tree
biziclop 2011

1
1つのSQLクエリで解決できませんでしたか?SELECT * FROM Places WHERE(Lat-:Lat)^ 2 +(Long-:Long)^ 2 <=:Distance ^ 2(多くの場合、他の数学は地球が球形であることに関係しており、これは単なる例です)
弁証法

1
@ Ashu、nOiAd、残念ながら私はそのプロジェクトを放棄しなければならなかったので、私は解決策を選ぶことになりませんでした。あなたたちがあなたのプロジェクトで解決策の1つを使用するなら、私と他の人はここでそれについてのあなたのコメントを本当に感謝します。
バレラ2013年

回答:


41

緯度間の距離を比較することから始めます。各緯度は約69マイル(111キロメートル)離れています。範囲は(地球のわずかな楕円形のために)赤道での68.703マイル(110.567 km)から極での69.407(111.699 km)まで変化します。2つの場所の間の距離は、それらの緯度の間の距離以上になります。

これは経度には当てはまらないことに注意してください。経度の各度の長さは緯度に依存します。ただし、データが特定の領域(たとえば、単一の国)に制限されている場合は、経度の最小および最大の境界を計算することもできます。


続行すると、球体地球を想定した低精度で高速の距離計算が行われます。

座標が{lat1、lon1}と{lat2、lon2}の2点間の大円距離dは、次の式で与えられます。

d = acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos(lon1-lon2))

数学的に同等の式で、短距離の丸め誤差の影響を受けにくいものは次のとおりです。

d = 2*asin(sqrt((sin((lat1-lat2)/2))^2 +
    cos(lat1)*cos(lat2)*(sin((lon1-lon2)/2))^2))

dはラジアン単位の距離です

distance_km ≈ radius_km * distance_radians ≈ 6371 * d

(6371 kmは地球の平均半径です

この方法の計算要件は最小限です。ただし、結果は短い距離では非常に正確です。


次に、それが特定の距離にある場合は、多かれ少なかれ、より正確な方法を使用します。

GeographicLibは私が知っている中で最も正確な実装ですが、Vincentyの逆式も使用できます。


RDBMSを使用している場合は、緯度を主キーとして設定し、経度を副キーとして設定します。上記のように、緯​​度範囲または緯度/経度範囲をクエリしてから、結果セットの正確な距離を計算します。

すべての主要なRDBMSの最新バージョンは、地理データ型とクエリをネイティブにサポートしていることに注意してください。


ただ注意してください、最初のリンクは壊れています。
kunruh 2017

@kunruh:ありがとう。リンクは、現在オフラインになっているように見えるエドウィリアムズの航空フォーミュラリーを指しています。リンクを数式に置き換えました。
Lior Kogan 2017

このリンクは、ほとんどすべて、このトピックに関連した説明movable-type.co.uk/scripts/...
madeinQuant

14

現在のユーザーの緯度、経度、および検索する距離に基づいて、SQLクエリを以下に示します。

SELECT * FROM(
    SELECT *,(((acos(sin((@latitude*pi()/180)) * sin((Latitude*pi()/180))+cos((@latitude*pi()/180)) * cos((Latitude*pi()/180)) * cos(((@longitude - Longitude)*pi()/180))))*180/pi())*60*1.1515*1.609344) as distance FROM Distances) t
WHERE distance <= @distance

@latitudeと@longitudeは、ポイントの緯度と経度です。緯度と経度は距離テーブルの列です。円周率の値は22/7です


2
@distanceパラメータはKMまたはマイルですか?
garfbradaz

距離がKM単位であるか、スクリプトが間違っていると思います。誰かが上記の質問に答えてください。
OmarAbbas20年


5

タンクのヨギホスティング

データベースにOpenStreep Mapsのテーブルが1つあり、テストに成功しました。

距離はメートル単位で問題なく機能します。

SET @orig_lat=-8.116137;
SET @orig_lon=-34.897488;
SET @dist=1000;

SELECT *,(((acos(sin((@orig_lat*pi()/180)) * sin((dest.latitude*pi()/180))+cos((@orig_lat*pi()/180))*cos((dest.latitude*pi()/180))*cos(((@orig_lon-dest.longitude)*pi()/180))))*180/pi())*60*1.1515*1609.344) as distance FROM nodes AS dest HAVING distance < @dist ORDER BY distance ASC LIMIT 100;

世界は球体ではありません!
Toby Speight 2016

あなたの提案は何ですか?
Helmut Kemper 2017年


2

biziclopが述べたように、ある種の距離空間ツリーがおそらくあなたの最良の選択肢でしょう。私はkdツリーとクアッドツリーを使用してこれらの種類の範囲クエリを実行した経験があり、驚くほど高速です。また、書くのもそれほど難しくありません。これらの構造の1つを調べることをお勧めします。これらの構造を使用すると、「データセット内でこの他のポイントに最も近いポイントは何ですか?」などの他の興味深い質問にも答えることができます。


これは問題を解決するための貴重なヒントかもしれませんが、答えは本当に解決策を示す必要があります。編集して、意味を示すサンプルコードを提供してください。または、代わりにコメントとしてこれを書くことを検討してください。
Toby Speight 2016

1
私は実際、ここのコードは気が散ると思います-ツリー構造と選択された特定の言語を含むライブラリに固有すぎるでしょう(この質問は言語でタグ付けされていないことに注意してください)
templatetypedef


0

緯度経度をUTM形式に変換できます。これは、距離の計算に役立つメートル法です。次に、ポイントが特定の場所にあるかどうかを簡単に判断できます。


1
これは問題を解決するための貴重なヒントかもしれませんが、答えは本当に解決策を示す必要があります。編集して、意味を示すサンプルコードを提供してください。または、代わりにコメントとしてこれを書くことを検討してください。
Toby Speight 2016

0

あなたはどんな言語でも受け入れられると言っているので、自然な選択はPostGISです:

SELECT * FROM places
WHERE ST_DistanceSpheroid(geom, $location, $spheroid) < $max_metres;

WGSデータムを使用する場合は、 $spheroidする'SPHEROID["WGS 84",6378137,298.257223563]'

列でインデックスを作成placesしたとするとgeom、これはかなり効率的です。


0

@yogihostingによって提供されたソリューションのおかげで、以下に示すコードを使用してmysqlのスキーマレス列から同様の結果を得ることができました。

// @params - will be bound to named query parameters
$criteria = [];
$criteria['latitude'] = '9.0285183';
$criteria['longitude'] = '7.4869546';
$criteria['distance'] = 500;
$criteria['skill'] = 'software developer';

// Get doctrine connection 
$conn = $this->getEntityManager()->getConnection();

        $sql = '
               SELECT DISTINCT m.uuid AS phone, (((acos(sin((:latitude*pi()/180)) * sin((JSON_EXTRACT(m.location, "$.latitude")*pi()/180))+cos((:latitude*pi()/180)) * 
              cos((JSON_EXTRACT(m.location, "$.latitude")*pi()/180)) * 
              cos(((:longitude - JSON_EXTRACT(m.location, "$.longitude"))*pi()/180))))*180/pi())*60*1.1515*1.609344) AS distance FROM member_profile AS m 
               INNER JOIN member_card_subscription mcs ON mcs.primary_identity = m.uuid
               WHERE mcs.end > now() AND JSON_SEARCH(m.skill_logic, "one", :skill) IS NOT NULL  AND (((acos(sin((:latitude*pi()/180)) * sin((JSON_EXTRACT(m.location, "$.latitude")*pi()/180))+cos((:latitude*pi()/180)) * 
              cos((JSON_EXTRACT(m.location, "$.latitude")*pi()/180)) * 
              cos(((:longitude - JSON_EXTRACT(m.location, "$.longitude"))*pi()/180))))*180/pi())*60*1.1515*1.609344) <= :distance ORDER BY distance
               ';
        $stmt = $conn->prepare($sql);
        $stmt->execute(['latitude'=>$criteria['latitude'], 'longitude'=>$criteria['longitude'], 'skill'=>$criteria['skill'], 'distance'=>$criteria['distance']]);
        var_dump($stmt->fetchAll());

上記のコードスニペットはDoctrineDB接続とPHPを使用していることに注意してください


-2

あなたはこの方程式をチェックするかもしれません私はそれが役立つと思います

SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance FROM markers HAVING distance < 25 ORDER BY distance LIMIT 0 , 20;

このコードは問題の解決に役立つ場合がありますが、その理由や質問への回答方法については説明していません。この追加のコンテキストを提供すると、その長期的な教育的価値が大幅に向上します。回答を編集して、適用される制限や前提条件などの説明を追加してください。特に、マジックバリュー3959と37はどこから来ているのでしょうか。
Toby Speight 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.