Der er ingen geospatiale udvidelsesfunktioner i MySQL, der understøtter afstandsberegninger for breddegrad/længde. Der er fra MySQL 5.7
.
Du beder om nærhedscirkler på jordens overflade. Du nævner i dit spørgsmål, at du har lat/lang værdier for hver række i dine flags
tabel, og også universal tværgående Mercator
(UTM) projekterede værdier i en af flere forskellige UTM-zoner
. Hvis jeg husker mine UK Ordnance Survey-kort korrekt, er UTM nyttig til at lokalisere genstande på disse kort.
Det er en simpel sag at beregne afstanden mellem to punkter i samme zone i UTM:den kartesiske afstand gør det trick. Men når punkter er i forskellige zoner, virker den beregning ikke.
For den applikation, der er beskrevet i dit spørgsmål, er det derfor nødvendigt at bruge Great Circle Distance , som beregnes ved hjælp af haversin eller en anden passende formel.
MySQL, forstærket med geospatiale udvidelser, understøtter en måde at repræsentere forskellige plane former (punkter, polylinjer, polygoner og så videre) som geometriske primitiver. MySQL 5.6 implementerer en udokumenteret afstandsfunktion st_distance(p1, p2)
. Denne funktion returnerer dog kartesiske afstande. Så det er helt uegnet til bredde- og længdegradsbaserede beregninger. På tempererede breddegrader strækker en breddegrad sig næsten dobbelt så meget overfladeafstand (nord-syd) som en længdegrad (øst-vest), fordi breddegradslinjerne vokser tættere sammen tættere på polerne.
Så en cirkulær nærhedsformel skal bruge ægte bredde- og længdegrad.
I din applikation kan du finde alle flags
punkter inden for ti statut miles fra et givet latpoint,longpoint
med en forespørgsel som denne:
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)
Hvis du vil søge efter punkter inden for 20 km, skal du ændre denne linje i forespørgslen
20.0 AS r, 69.0 AS units
hertil f.eks.
20.0 AS r, 111.045 AS units
r
er den radius, du vil søge i. units
er afstandsenhederne (miles, km, furlongs, hvad end du vil) pr. breddegrad på jordens overflade.
Denne forespørgsel bruger en afgrænsende lat/lang sammen med MbrContains
for at udelukke punkter, der bestemt er for langt fra dit udgangspunkt, bruger du derefter storcirkelafstandsformlen til at generere afstandene for de resterende punkter. En forklaring på alt dette kan findes her
. Hvis din tabel bruger MyISAM-adgangsmetoden og har et rumligt indeks, MbrContains
vil udnytte det indeks til at få dig til at søge hurtigt.
Til sidst vælger forespørgslen ovenfor alle punkter i rektanglet. For at indsnævre det til kun punkterne i cirklen og sortere dem efter nærhed, skal du afslutte forespørgslen sådan her:
SELECT id, coordinates, name
FROM (
/* the query above, paste it in here */
) AS d
WHERE d.distance <= d.r
ORDER BY d.distance ASC