V MySQL nejsou žádné funkce geoprostorového rozšíření, které by podporovaly výpočty zeměpisné šířky a délky. Existuje od MySQL 5.7
.
Žádáte o kruhy blízkosti na povrchu Země. Ve své otázce uvádíte, že pro každý řádek v flags
máte hodnoty lat/long stůl a také univerzální příčný Mercator
(UTM) projektované hodnoty v jedné z několika různých zón UTM
. Pokud si správně pamatuji své mapy UK Ordnance Survey, UTM je užitečné pro lokalizaci položek na těchto mapách.
Vypočítat vzdálenost mezi dvěma body ve stejné zóně je jednoduchá záležitost v UTM:Kartézská vzdálenost dělá trik. Ale když jsou body v různých zónách, tento výpočet nefunguje.
V souladu s tím je pro aplikaci popsanou ve vaší otázce nutné použít Vzdálenost velkého kruhu , který se vypočítá pomocí haversine nebo jiného vhodného vzorce.
MySQL, rozšířená o geoprostorová rozšíření, podporuje způsob, jak reprezentovat různé rovinné tvary (body, křivky, mnohoúhelníky a tak dále) jako geometrická primitiva. MySQL 5.6 implementuje nezdokumentovanou funkci vzdálenosti st_distance(p1, p2)
. Tato funkce však vrací kartézské vzdálenosti. Je tedy zcela nevhodné pro výpočty založené na zeměpisné šířce a délce. V mírných zeměpisných šířkách je stupeň zeměpisné šířky téměř dvakrát větší vzdáleností povrchu (sever-jih) než stupeň zeměpisné délky (východ-západ), protože čáry zeměpisné šířky se k sobě přibližují blíže k pólům.
Vzorec kruhové blízkosti tedy musí používat skutečnou zeměpisnou šířku a délku.
Ve své aplikaci můžete najít všechny flags
body do deseti mílí od daného latpoint,longpoint
s dotazem jako je tento:
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)
Pokud chcete hledat body do 20 km, změňte tento řádek dotazu
20.0 AS r, 69.0 AS units
k tomu například
20.0 AS r, 111.045 AS units
r
je poloměr, ve kterém chcete hledat. units
jsou jednotky vzdálenosti (míle, km, furlongy, cokoli chcete) na stupeň zeměpisné šířky na povrchu Země.
Tento dotaz používá ohraničující lat/long spolu s MbrContains
k vyloučení bodů, které jsou rozhodně příliš daleko od vašeho výchozího bodu, pak pomocí vzorce vzdálenosti velkého kruhu vygenerujte vzdálenosti pro zbývající body. vysvětlení toho všeho naleznete zde
. Pokud vaše tabulka používá přístupovou metodu MyISAM a má prostorový index, MbrContains
využije tento index k rychlému vyhledávání.
Nakonec výše uvedený dotaz vybere všechny body v obdélníku. Chcete-li to zúžit pouze na body v kruhu a seřadit je podle blízkosti, zabalte dotaz takto:
SELECT id, coordinates, name
FROM (
/* the query above, paste it in here */
) AS d
WHERE d.distance <= d.r
ORDER BY d.distance ASC