sql >> Databáze >  >> RDS >> Mysql

Použijte prostorová rozšíření MySQL k výběru bodů uvnitř kruhu

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 


  1. Jak FIELD() funguje v MariaDB

  2. Jak zkomprimovat a opravit databázi automaticky v Accessu 2016

  3. Návrh databáze 101

  4. ADDDATE() vs DATE_ADD() v MySQL:Jaký je rozdíl?