2016-08-25 21 views
0

私は、ユーザーが指定した地点から近くの場所(300m)を探すサービスを持っています。緯度と経度の制限

私は場所が https://en.wikipedia.org/wiki/Haversine_formula

私の問題は、ポイントの近くにあるかどうかを確認するために半正矢式を使用していますが、それは私のDB内のすべての点に対してチェックだから、それは遅いということです。

私がやりたいことは、最初のクエリを制限し、狭い範囲の領域の点のリストにhaversineの式を適用することです。

results = (SELECT * FROM location WHERE location.latitude BETWEEN 14.223 AND 14.5) 
AND location.longitude BETWEEN 121.5 AND 122 

haversine(results, user_point) 

特定のポイントから境界を取得する緩い方法はありますか? または基本的にlat/longのメーターへの汚れた変換?球体で地球を近似

答えて

1

緯度と経度の代わりに(またはそれに加えて)位置座標を3D空間に変換し、x、y、およびzの列を使用して、データベース構造を変更することができます。 zはメートル単位です。

SELECT * FROM location 
    WHERE location.x BETWEEN center.x - 300 AND center.x + 300 
    AND location.y BETWEEN center.y - 300 AND center.y + 300 
    AND location.z BETWEEN center.z - 300 AND center.z + 300 

これはあなたのリストをきれいに整えてくれます。結果セットのhaversine計算を行うことができます。


緯度と経度のみのデータベースを使用している場合は、検索範囲を絞り込むことができます。緯度は簡単です。極に近づくと発生する合併症を無視する限り、南北の1度の緯度は常に111kmの距離に相当します。つまり、300mの距離が0.0027 ...緯度ですが、少し控えめで、0.003または0.004を使用することもできます。

経度は、あなたが南北にどれほど離れているかによって変わりますが、それほど複雑ではありません。緯度の余弦を乗算するだけです。

distance = cos(latitude) * 111.19... km/degree * delta_angle 

赤道では、緯度と同じです。赤道での経度の1度の変化は111kmです。北緯80度(または南緯)では、cos(80 degrees) = 0.17...の係数を掛けます。その結果、経度の1度の変化はわずか19.3kmです。あなたの目的のために、これを逆転させ、300 m/cos(latitude)/(111.19... km/degree) = (0.0027... degrees)/cos(latitude)として選択するために経度の範囲を見つけることができます。その係数は最初の段落と同じ量です。それは偶然ではありません。

極限に近づくと、座標系の不連続点の近くに問題が発生します。あなたは89.9996度のような緯度に差し込む開始なぜときには見ることができます:唯一の360度全円であるとき

0.0027... degrees/cos(89.9996 degrees) = 386... degrees 

さて、どのようにそれをすることができますか?これは、あなたの300mの半径がポールの周りに広がって、話す方法であなたの出発地点を含むように戻ってきたという指標です。その時点で、データベース内のすべてのポイントをポールの近くで検索するだけでよいでしょう。もちろん、89.999度程度でこれをやってみるべきです。なぜなら、あなたが探している領域の直径600mのところが極を完全に囲んでいるからです。

インターナショナル・デイト・ライン、またはより正確には「アンチメリディアン」にはもう1つの問題があり、経度の-180度から+180度までのジャンプに関連しています。赤道上の+ 179.9999度と-179.9999度の地点は、地理的にわずか数メートル離れていても非常に異なる座標を持ちます。より詳細な検索のための予備フィルタとしてこれをやっているだけなので、antimeridianの0.006度(それはおおよそ300mの半径の円の直径です)の範囲内のすべての点を通過するのが最も簡単でしょう。そしてhaversine計算によってポイントが実際に近いかどうかが判断されます。

要約すると、上記の緯度と経度の範囲を使用することができ、極とアンチメリディアンの特殊なケースを追加するだけです。擬似SQL /コードハイブリッドのいくつかの種類では:

:あなたが潜在的に2倍のポイントをテストすることを犠牲にして簡潔な声明をしたい場合は

IF abs(center.latitude) > 89.999 
    SELECT * FROM location WHERE abs(location.latitude - center.latitude) < 0.003 
ELSE 
    IF abs(center.longitude) > 179.997 
    SELECT * FROM location 
     WHERE abs(location.latitude - center.latitude) < 0.003 
     AND 180 - abs(location.longitude) < (0.006/cos(center.latitude)) 
    ELSE 
    SELECT * FROM location 
     WHERE abs(location.latitude - center.latitude) < 0.003 
     AND abs(location.longitude - center.longitude) < (0.003/cos(center.latitude)) 
    ENDIF 
ENDIF 

、あなただけの経度の絶対値を比較することができます

SELECT * FROM location 
    WHERE abs(location.latitude - center.latitude) < 0.003 
    AND abs(abs(location.longitude) - abs(center.longitude)) <= min(0.003/cos(center.latitude), 180) 
0

、二つの連続緯度の間の距離は

rは地球の半径で
dPerLat = pi * r/180°, 

によって計算することができます。これは約111kmになります。だから、

、あなたの基準点が(lat, long)で、検索半径がdであるならば、あなたはその後、与えられた緯度のために、連続した経度の距離がある範囲の緯度

lat* \in [lat - d/dPerLat, lat + d/dPerLat] 

を検索したい:

dPerLong = pi * r * cos(lat)/180° 

また、検索する経度の範囲は+- d/dPerLongです。控えめな(最大)範囲を与えるlat値、つまり最も高い絶対値を持つlat値を使用する必要があります。

極に注意してください。