web-dev-qa-db-ja.com

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

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

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

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

72
Valera

緯度間の距離を比較することから始めます。各緯度は約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の最新バージョンは、地理データタイプとクエリをネイティブにサポートしていることに注意してください。

37
Lior Kogan

現在のユーザーの緯度、経度、および検索する距離に基づいて、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は、ポイントの緯度と経度です。緯度と経度は距離テーブルの列です。 piの値は22/7です

12
yogihosting

PostgreSQL GIS拡張 が役立つ可能性があります-のように、実装を考えている機能の多くを既に実装している可能性があります。

6
Gian

良い解決策のためにこれを試してください: Geolocation Search

5

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

私のデータベースには、Open Streep 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;
4
Helmut Kemper
2
Alix Axel

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

2
templatetypedef

必要なのは空間検索です。 Solr Spatial search を使用できます。また、lat/longデータ型が組み込まれています こちらをチェック

1
Zimbabao

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

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

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

places列によってgeomにインデックスを付けたと仮定すると、これはかなり効率的です。

0
Toby Speight

緯度経度をUTM形式に変換できます。UTM形式は、距離の計算に役立つメトリック形式です。その後、ポイントが特定の場所に該当するかどうかを簡単に判断できます。

0
Hamdi