如果你可以修改你的數據庫結構,有一個超級簡單的方法來做到這一點:代替(或除了)存儲經度和緯度,將你的位置座標轉換爲三維空間,列爲x,y,和以米爲單位的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
這將減少你的清單非常好,你可以對結果集做正弦計算。
如果您堅持使用只有經度和緯度的數據庫,仍然可以縮小搜索範圍。緯度很容易:只要你忽略了當你接近極點時出現的複雜情況,北向或南向的一個緯度總是相當於111公里的距離。這意味着300米的距離爲0.0027 ...緯度,儘管您可能稍微保守一點,使用0.003或0.004。
經度有點棘手,因爲轉換因子根據你的北方或南方有多遠而變化,但它還不算太複雜:你只需乘以緯度的餘弦。
distance = cos(latitude) * 111.19... km/degree * delta_angle
在赤道上,它和緯度一樣:赤道經度變化一度爲111公里。在北緯80度或南緯80度處,乘以cos(80 degrees) = 0.17...
,結果經度1度變化僅爲19.3公里。出於您的目的,您可以將其反轉並找到經度範圍,以選擇300 m/cos(latitude)/(111.19... km/degree) = (0.0027... degrees)/cos(latitude)
。該係數與第一段的數量相同;這不是巧合。
棘手的問題出現在座標系的不連續處附近,例如當你靠近兩極時。你可以看到爲什麼當你開始在緯度堵漏像89.9996度:
0.0027... degrees/cos(89.9996 degrees) = 386... degrees
嘛,怎麼可能當只有360度的整圈?這是一個指標,你已經達到了你的300米半徑一直延伸到杆的位置,並以一種說話的方式回到你的起始位置。那時候,你可能只需要搜索數據庫中所有的點就足夠接近極點。當然,你應該在89.999度左右開始這樣做,因爲那是你正在搜索的地區的600米直徑完全包圍了杆。
還有另一個問題在(國際日期線附近),或者更確切地說是「反經絡」,與經度從-180跳到+180度有關。即使它們在地理上相距僅幾米,但在赤道上的點爲+179.9999度和在-179.9999度處的點將具有非常不同的座標。由於您只是將其作爲初步過濾器進行更詳細的搜索,因此最簡單的方法是穿過反子午線的0.006度(大致爲300米半徑圓的直徑)內的每個點,然後再通過半胱氨酸計算將確定這些點是否確實接近。總結一下,你可以使用上面提到的經度和緯度的界限,只是爲極點和反子午線添加特殊情況。在某種僞SQL /代碼混合的:
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)