Er zijn geen geospatiale uitbreidingsfuncties in MySQL die lengte- en breedtegraadberekeningen ondersteunen. Er is vanaf MySQL 5.7
.
Je vraagt om nabijheidscirkels op het aardoppervlak. U vermeldt in uw vraag dat u lat/long-waarden heeft voor elke rij in uw flags
tabel, en ook universele transversale Mercator
(UTM) geprojecteerde waarden in een van de verschillende UTM-zones
. Als ik me mijn UK Ordnance Survey-kaarten goed herinner, is UTM handig om items op die kaarten te lokaliseren.
Het is eenvoudig om de afstand te berekenen tussen twee punten in dezelfde zone in UTM:de cartesiaanse afstand doet de truc. Maar als punten zich in verschillende zones bevinden, werkt die berekening niet.
Dienovereenkomstig is het voor de toepassing die in uw vraag wordt beschreven, noodzakelijk om de Great Circle Distance , die wordt berekend met behulp van de haversine of een andere geschikte formule.
MySQL, aangevuld met geospatiale extensies, ondersteunt een manier om verschillende vlakke vormen (punten, polylijnen, polygonen, enzovoort) als geometrische primitieven weer te geven. MySQL 5.6 implementeert een ongedocumenteerde afstandsfunctie st_distance(p1, p2)
. Deze functie retourneert echter cartesiaanse afstanden. Het is dus helemaal ongeschikt voor op breedte- en lengtegraad gebaseerde berekeningen. Op gematigde breedtegraden beslaat een breedtegraad bijna twee keer zoveel oppervlakteafstand (noord-zuid) als een lengtegraad (oost-west), omdat de breedtegraadlijnen dichter naar elkaar toegroeien, dichter bij de polen.
Een formule voor circulaire nabijheid moet dus echte lengte- en breedtegraden gebruiken.
In uw applicatie vindt u alle flags
punten binnen tien statuutmijlen van een gegeven latpoint,longpoint
met een vraag als deze:
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)
Als u punten binnen 20 km wilt zoeken, wijzigt u deze regel van de zoekopdracht
20.0 AS r, 69.0 AS units
naar dit, bijvoorbeeld
20.0 AS r, 111.045 AS units
r
is de straal waarin u wilt zoeken. units
zijn de afstandseenheden (mijlen, km, stadiën, wat je maar wilt) per breedtegraad op het aardoppervlak.
Deze zoekopdracht gebruikt een begrenzing lat/long samen met MbrContains
om punten uit te sluiten die beslist te ver van uw startpunt verwijderd zijn, gebruikt u vervolgens de formule voor de grote cirkelafstand om de afstanden voor de overige punten te genereren. Een uitleg van dit alles is hier te vinden
. Als uw tabel de MyISAM-toegangsmethode gebruikt en een ruimtelijke index heeft, MbrContains
zal die index misbruiken om u snel te laten zoeken.
Ten slotte selecteert de bovenstaande query alle punten binnen de rechthoek. Om dat te beperken tot alleen de punten in de cirkel en ze te rangschikken op nabijheid, sluit u de zoekopdracht als volgt af:
SELECT id, coordinates, name
FROM (
/* the query above, paste it in here */
) AS d
WHERE d.distance <= d.r
ORDER BY d.distance ASC