sql >> Databasteknik >  >> RDS >> Mysql

Använd MySQL spatial extensions för att välja punkter inuti cirkeln

Det finns inga geospatiala förlängningsfunktioner i MySQL som stöder latitud/longitud avståndsberäkningar. Det finns från och med MySQL 5.7 .

Du ber om närhetscirklar på jordens yta. Du nämner i din fråga att du har lat/lång-värden för varje rad i dina flags tabell, och även universell tvärgående Mercator (UTM) projicerade värden i en av flera olika UTM-zoner . Om jag minns mina kartor från UK Ordnance Survey korrekt, är UTM användbart för att hitta föremål på dessa kartor.

Det är enkelt att beräkna avståndet mellan två punkter i samma zon i UTM:det kartesiska avståndet gör susen. Men när punkter är i olika zoner fungerar inte den beräkningen.

Följaktligen, för applikationen som beskrivs i din fråga, är det nödvändigt att använda Great Circle Distance , som beräknas med hjälp av haversin eller annan lämplig formel.

MySQL, utökat med geospatiala förlängningar, stöder ett sätt att representera olika plana former (punkter, polylinjer, polygoner och så vidare) som geometriska primitiver. MySQL 5.6 implementerar en odokumenterad avståndsfunktion st_distance(p1, p2) . Den här funktionen returnerar dock kartesiska avstånd. Så det är helt olämpligt för latitud- och longitudbaserade beräkningar. På tempererade breddgrader täcker en latitud nästan dubbelt så mycket ytavstånd (nord-syd) som en longitud (öst-västlig), eftersom latitudlinjerna växer närmare varandra närmare polerna.

Så en cirkulär närhetsformel måste använda äkta latitud och longitud.

I din applikation kan du hitta alla flags punkter inom tio lagstadgade miles från en given latpoint,longpoint med en fråga som denna:

 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)

Om du vill söka efter punkter inom 20 km, ändra denna rad i frågan

               20.0 AS r, 69.0 AS units

till detta, till exempel

               20.0 AS r, 111.045 AS units

r är radien i vilken du vill söka. units är avståndsenheterna (mil, km, furlongs, vad du vill) per latitudgrad på jordens yta.

Den här frågan använder en avgränsande lat/längd tillsammans med MbrContains för att utesluta punkter som definitivt är för långt från din startpunkt använder du sedan formeln för storcirkelavstånd för att generera avstånden för de återstående punkterna. En förklaring av allt detta finns här . Om din tabell använder MyISAM-åtkomstmetoden och har ett rumsligt index, MbrContains kommer att utnyttja det indexet för att få dig att söka snabbt.

Slutligen väljer frågan ovan alla punkter inom rektangeln. För att begränsa det till endast punkterna i cirkeln och sortera dem efter närhet, avsluta frågan så här:

 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. Hur SQLite Nullif() fungerar

  2. Dimensions of Dimensions:En titt på Data Warehousings vanligaste dimensionstabelltyper

  3. Finns det något sätt att använda ARRAYs i Entity Framework + PostgreSql

  4. Oracle SQL:Hur man använder mer än 1000 objekt i en IN-klausul