Nie ma funkcji rozszerzeń geoprzestrzennych w MySQL obsługujących obliczenia odległości szerokości i długości geograficznej. Jest od MySQL 5.7
.
Pytasz o kręgi zbliżeniowe na powierzchni ziemi. W swoim pytaniu wspominasz, że masz wartości długości/szerokości dla każdego wiersza w Twoich flags
tabeli, a także uniwersalny poprzeczny Mercator
(UTM) przewidywane wartości w jednej z kilku różnych strefach UTM
. Jeśli dobrze pamiętam moje mapy UK Ordnance Survey, UTM jest przydatny do lokalizowania przedmiotów na tych mapach.
Łatwo jest obliczyć odległość między dwoma punktami w tej samej strefie w UTM:odległość kartezjańska załatwia sprawę. Ale gdy punkty znajdują się w różnych strefach, to obliczenie nie działa.
W związku z tym w przypadku aplikacji opisanej w Twoim pytaniu konieczne jest użycie Great Circle Distance , który jest obliczany za pomocą hasrsine lub innej odpowiedniej formuły.
MySQL, wzbogacony o rozszerzenia geoprzestrzenne, obsługuje sposób przedstawiania różnych płaskich kształtów (punktów, polilinii, wielokątów itd.) jako prymitywów geometrycznych. MySQL 5.6 implementuje nieudokumentowaną funkcję odległości st_distance(p1, p2)
. Jednak ta funkcja zwraca odległości kartezjańskie. Więc jest całkowicie nieodpowiednie do obliczeń opartych na szerokości i długości geograficznej. W umiarkowanych szerokościach geograficznych stopień szerokości geograficznej odpowiada prawie dwukrotnie większej odległości powierzchniowej (północ-południe) niż stopniowi długości geograficznej (wschód-zachód), ponieważ linie szerokości geograficznej zbliżają się do siebie bliżej biegunów.
Tak więc formuła bliskości kołowej musi używać prawdziwej szerokości i długości geograficznej.
W swojej aplikacji możesz znaleźć wszystkie flags
punkty w promieniu dziesięciu mil statutowych od danego latpoint,longpoint
z zapytaniem takim jak to:
SELECT id, coordinates, name, r,
units * DEGREES(ACOS(LEAST(1.0, COS(RADIANS(latpoint))
* COS(RADIANS(latitude))
* COS(RADIANS(longpoint) - RADIANS(longitude))
+ SIN(RADIANS(latpoint))
* SIN(RADIANS(latitude))))) AS distance
FROM flags
JOIN (
SELECT 42.81 AS latpoint, -70.81 AS longpoint,
10.0 AS r, 69.0 AS units
) AS p ON (1=1)
WHERE MbrContains(GeomFromText (
CONCAT('LINESTRING(',
latpoint-(r/units),' ',
longpoint-(r /(units* COS(RADIANS(latpoint)))),
',',
latpoint+(r/units) ,' ',
longpoint+(r /(units * COS(RADIANS(latpoint)))),
')')), coordinates)
Jeśli chcesz wyszukać punkty w promieniu 20 km, zmień ten wiersz zapytania
20.0 AS r, 69.0 AS units
do tego, na przykład
20.0 AS r, 111.045 AS units
r
to promień, w którym chcesz szukać. units
to jednostki odległości (mile, km, stadia, cokolwiek chcesz) na stopień szerokości geograficznej na powierzchni ziemi.
To zapytanie używa ograniczenia szerokości/długości wraz z MbrContains
aby wykluczyć punkty, które są zdecydowanie za daleko od punktu początkowego, użyj wzoru na odległość po wielkim okręgu, aby wygenerować odległości dla pozostałych punktów. wyjaśnienie tego wszystkiego można znaleźć tutaj
. Jeśli twoja tabela używa metody dostępu MyISAM i ma indeks przestrzenny, MbrContains
wykorzysta ten indeks, aby przyspieszyć wyszukiwanie.
Na koniec powyższe zapytanie wybiera wszystkie punkty w prostokącie. Aby zawęzić to tylko do punktów w okręgu i uporządkować je według odległości, zawiń zapytanie w ten sposób:
SELECT id, coordinates, name
FROM (
/* the query above, paste it in here */
) AS d
WHERE d.distance <= d.r
ORDER BY d.distance ASC