Mysql
 sql >> Database >  >> RDS >> Mysql

Usa le estensioni spaziali MySQL per selezionare i punti all'interno del cerchio

Non ci sono funzioni di estensione geospaziale in MySQL che supportino i calcoli della distanza di latitudine/longitudine. C'è come di MySQL 5.7 .

Stai chiedendo dei cerchi di prossimità sulla superficie della terra. Nella tua domanda dici che hai valori lat/long per ogni riga nei tuoi flags tabella e anche universal trasversale Mercator (UTM) valori proiettati in una delle diverse zone UTM . Se ricordo correttamente le mie mappe del Regno Unito Ordnance Survey, UTM è utile per localizzare gli oggetti su quelle mappe.

È semplice calcolare la distanza tra due punti nella stessa zona in UTM:la distanza cartesiana fa il trucco. Ma quando i punti si trovano in zone diverse, il calcolo non funziona.

Di conseguenza, per l'applicazione descritta nella tua domanda, è necessario utilizzare la Great Circle Distance , che viene calcolato utilizzando l'haversine o un'altra formula adatta.

MySQL, potenziato con estensioni geospaziali, supporta un modo per rappresentare varie forme planari (punti, polilinee, poligoni e così via) come primitive geometriche. MySQL 5.6 implementa una funzione di distanza non documentata st_distance(p1, p2) . Tuttavia, questa funzione restituisce distanze cartesiane. Quindi è del tutto inadatto per calcoli basati su latitudine e longitudine. Alle latitudini temperate un grado di latitudine sottende quasi il doppio della distanza superficiale (nord-sud) di un grado di longitudine (est-ovest), perché le linee di latitudine si avvicinano l'una all'altra più vicino ai poli.

Quindi, una formula di prossimità circolare deve utilizzare la latitudine e la longitudine autentiche.

Nella tua applicazione puoi trovare tutti i flags punti entro dieci miglia statutarie da un dato latpoint,longpoint con una query come questa:

 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)

Se vuoi cercare punti entro 20 km, cambia questa riga della query

               20.0 AS r, 69.0 AS units

a questo, per esempio

               20.0 AS r, 111.045 AS units

r è il raggio in cui vuoi cercare. units sono le unità di distanza (miglia, km, stadi, qualunque cosa tu voglia) per grado di latitudine sulla superficie terrestre.

Questa query utilizza un limite lat/long insieme a MbrContains per escludere punti che sono decisamente troppo lontani dal punto di partenza, usa la formula della distanza del cerchio massimo per generare le distanze per i punti rimanenti. Una spiegazione di tutto questo può essere trovata qui . Se la tabella utilizza il metodo di accesso MyISAM e dispone di un indice spaziale, MbrContains sfrutterà quell'indice per farti cercare velocemente.

Infine, la query precedente seleziona tutti i punti all'interno del rettangolo. Per restringerlo solo ai punti del cerchio e ordinarli per prossimità, avvolgi la query in questo modo:

 SELECT id, coordinates, name
   FROM (
         /* the query above, paste it in here */
        ) AS d
  WHERE d.distance <= d.r
  ORDER BY d.distance ASC