Come posso cercare in modo efficiente tutti i punti di riferimento all'interno di un intervallo di un determinato punto di riferimento?


14

Sto cercando di iniziare con un progetto di ricerca geografica che troverà tutti i punti di riferimento nei 10 km / miglia (non importante per questa storia) di un punto di riferimento particolare.

Ad esempio, supponiamo che io abbia un database di 1.000.000 di punti di riferimento. Per trovare tutti i punti di riferimento nel raggio di 10 miglia di un punto di riferimento con determinate coordinate, dovrei calcolare una distanza tra un punto di riferimento dalla mia ricerca e 1.000.000 di punti di riferimento.

C'è un modo migliore per farlo?

L'alternativa che stavo pensando è quella di classificare punti di riferimento come paese, regione, città, quartiere, affari, storico, ecc. In modo tale che gli affari possano far parte di un quartiere o di una città. La città fa parte di una regione, di un paese, ecc. Questo può restringere un elenco di calcoli, ma sembra che ci sia ancora molto lavoro da fare affinché la ricerca sia veloce e accurata.

L'API di Google Maps potrebbe essere d'aiuto?


5
Probabilmente potresti eliminarne molti semplicemente eseguendo un rapido calcolo della distanza a Manhattan e successivamente eseguendo un secondo filtro per escludere punti di riferimento che si trovano all'interno di un quadrato di 10 km ma al di fuori del raggio di 10 km.
Neil,

3
Quale tecnologia di database stai usando? La risposta non è indipendente dal database.
jpmc26,

1
@Neil Come secondo passaggio è possibile includere qualsiasi punto di riferimento con cui x e y cadono entrambi in 7 km dall'origine senza calcolare la distanza effettiva.
JimmyJames,

Risposte:


10

Da SQL Server 2008 esiste un tipo di dati geografici che memorizza posizioni (coppie lat / lon) e semplifica la scrittura di query relative alla posizione.

Esiste una risposta StackOverflow esistente che ne discute in modo approfondito.

Una query di base per trovare i 7 articoli più vicini :

USE AdventureWorks2012  
GO  
DECLARE @g geography = 'POINT(-121.626 47.8315)';  
SELECT TOP(7) SpatialLocation.ToString(), City FROM Person.Address  
WHERE SpatialLocation.STDistance(@g) IS NOT NULL  
ORDER BY SpatialLocation.STDistance(@g);  

Una query di base per trovare tutto entro 100 m (seconda risposta alla domanda)

-- Get the center point
DECLARE @g geography
SELECT @g = geo FROM yourTable WHERE PointId = something

-- Get the results, radius 100m
SELECT * FROM yourTable WHERE @g.STDistance(geo) <= 100

11
@KonradRudolph: come nel caso di qualsiasi colonna SQL utilizzata per l'interrogazione su una tabella con un numero di righe enorme. Hai ragione, ma quel commento si applica praticamente a qualsiasi query SQL pubblicata come risposta.
Flater

2
Dove hai letto "MS SQL Server" nella domanda?
Doc Brown,

3
@Flater Sono d'accordo sul fatto che sarebbe normalmente ovvio e ridondante, ma la formulazione di OP sembra suggerire che non sono a conoscenza di tali meccanismi.
Konrad Rudolph,

2
@ jpmc26: Sei sgomento per aver elencato un'opzione valida e non ho incluso qualche altra opzione? Che cosa? Se ritieni sia importante aggiungere PostGIS, aggiungi tu stesso la risposta (cosa che hai fatto) e non ricorrere a criticare gli altri per non avere la tua stessa idea.
Flater,

3
La tua risposta mi sembra sostanzialmente un passo di vendita di MS SQL. I tuoi commenti suggeriscono che passano dai database a qualcosa che costerebbe 10s di migliaia di dollari senza in realtà indagare su quale sia la loro situazione che la fa sembrare più semplice. Non descrive nemmeno come l'OP possa effettivamente implementare la propria query o discutere del fatto che farlo e sfruttare l'indice spaziale non sono così semplici in MS SQL come in altri DB. Né discute nessuno dei concetti sottostanti. È una cattiva risposta, indipendentemente dal fatto che sia "valida". Ecco perché mi dà fastidio.
jpmc26,

29

Utilizzare un database con supporto per query GIS (sistemi di informazione geografica) . La maggior parte dei database supporta questo in modo definitivo o ha estensioni, ma i dettagli saranno specifici del database (nella loro risposta , Flater mostra la sintassi per il server SQL).

Se è necessario implementare tali query all'interno dell'applicazione, è possibile implementare una struttura di dati che consenta query spaziali, ad esempio un albero kd . Questo è come un albero di ricerca binario, tranne per il fatto che ciascun livello delle partizioni dell'albero su una diversa dimensione delle coordinate. Ciò consente di limitare la ricerca a un numero inferiore di candidati fattibili. In effetti, traduci la tua ricerca "raggio di 10 km" in limiti per ogni dimensione delle coordinate e restringi i limiti mentre ti rechi nell'albero.



8
PostGIS è la prima opzione gratuita. Supporta molto, molto più dei tipi e delle funzioni GIS di base di SQL Server. Ma questa è la funzionalità di base.
jpmc26,

@amon Trovo il commento di jpmc26 come una buona aggiunta e non tanto quanto criticare il tuo esempio. "Se vuoi ricominciare da zero, non devi pagare per un DB con licenza - anche questo open-source gratuito farà davvero il trucco".
Margarciaisaia,

11

Sì, c'è un modo migliore. Devi usare un indice spaziale . Questi indici organizzano metadati sulle geometrie per filtrare le geometrie lontane molto rapidamente, risparmiando molti cicli della CPU evitando i calcoli che descrivi. Non dovresti preoccuparti di implementarne uno tu stesso poiché tutti i principali database relazionali forniscono un tipo di geometria spaziale e indici per accompagnarli.

Ciò che si desidera esaminare sono query "a distanza" (query per geometrie a una certa distanza da un'altra geometria). Questi sono un problema molto standard e molto risolto e sono possibili in tutti i database di cui sopra (e integrati in diversi):

  • PostGIS: ST_DWithin
  • Server SQL: STDistance (Non è chiaro che l'utilizzo dell'indice sulla versione geografica 3D di questa funzione è supportato)
  • Oracle: SDO_WITHIN_DISTANCE(Questo non dice esplicitamente che attiverà l'utilizzo dell'indice. Doppio controllo del piano di query. Potrebbe essere necessario applicare un SDO_FILTERper farlo utilizzare per utilizzare l'indice.)
  • MySQL: lo sto ancora capendo.

Soluzione alternativa per l'attivazione dell'utilizzo dell'indice

Nel caso peggiore in cui si riscontrano problemi nel far sì che il sistema utilizzi l'indice spaziale con queste query, è possibile aggiungere un filtro aggiuntivo. Dovresti creare un riquadro di delimitazione quadrato con lati di lunghezza 2 * (distanza di ricerca) centrati nel punto di ricerca e confrontare i riquadri di delimitazione delle geometrie della tabella con quelli prima di verificare la distanza effettiva. Questo è ciò che PostGIS ' ST_DWithinsopra fa comunque internamente.


Distanza in GIS

Mentre gli indici spaziali sono fantastici e assolutamente la soluzione giusta al tuo problema, il calcolo della distanza può complicarsi logicamente. In particolare, devi preoccuparti di quale proiezione (fondamentalmente tutti i parametri per il sistema di coordinate) sono memorizzati i tuoi dati. La maggior parte delle proiezioni 2D (cose diverse dai sistemi di coordinate angolari come le varie lat / long proiezioni) distorcono significativamente la lunghezza. Ad esempio, la proiezione di Web Mercator (quella utilizzata da Google, Bing e tutti gli altri principali provider di mappe di base) espande le aree e le distanze in modo crescente man mano che la posizione si allontana dall'unico dell'equatore . Potrei sbagliarmi perché non sono formalmente istruito in GIS, ma il migliore che ho visto per le proiezioni 2D è alcuni specifici che promettono distanze corrette da unpunto unico e costante in tutto il mondo. (No, non è pratico utilizzare una proiezione diversa per ogni query; ciò renderebbe inutili i tuoi indici.)

La linea di fondo è che è necessario assicurarsi che la matematica sia accurata. Il modo più semplice di farlo dal punto di vista dello sviluppo è usare le proiezioni angolari (spesso definite "geografiche") e le funzioni che supportano la matematica usando un modello sferoide, ma questi calcoli sono leggermente più costosi rispetto alle controparti 2D e alcuni DB potrebbero non supportare l'indicizzazione. Se riesci a ottenere prestazioni accettabili utilizzandole, tuttavia, questa è probabilmente la strada da percorrere. Un'altra opzione comune sono le proiezioni regionali (come le zone UTM) che avvicinano le distanze e le aree piuttosto vicine per correggere se i tuoi dati sono confinati in una particolare parte del mondo. La cosa migliore per la tua app dipenderà dai tuoi requisiti specifici,

Questo vale anche se non si utilizzano indici spaziali incorporati. I tuoi dati hanno una certa proiezione a prescindere dalla tecnologia o tecnica che stai attualmente utilizzando o che utilizzerai in futuro, e stanno già influenzando tutte le query e i calcoli che stai facendo.


3

Concordo sul fatto che, se possibile, utilizzare il supporto specifico in un database sarebbe il modo più sensato per farlo.

Tuttavia, se dovessi farlo su un database senza supporto specifico, inizierei chiedendo un quadrato che racchiude il circolo, ad es. (Y> (y1 - rad)) AND (y <(y1 + rad)) AND (x> ( x1 - rad)) AND (x <(x1 + rad)). Supponendo che i tuoi punti abbiano una distribuzione della distribuzione approssimativamente uniforme per un quadrato, otterrai le tue partite vere più circa il 30% di partite false in più. È quindi possibile eliminare le false corrispondenze.


Ma senza un adeguato indice spaziale, una query di questo tipo scansionerà nella peggiore delle ipotesi l'intero database, nella migliore delle ipotesi tutti gli elementi entro l'intervallo di latitudine o longitudine dato a seconda dell'indice, cioè una "banda" anziché un quadrato. Se non vuoi uccidere le prestazioni, usa un database che supporti gli indici spaziali!
jcaron,

@jcaron Credo che questa query possa essere ottimizzata con un normale indice B-tree su xe y. (Forse combinato, forse separato. Profili un po 'per capire quale funziona meglio in pratica.)
jpmc26

@ jpmc26 No, non può. Pensaci bene, vedrai.
Jcaron,

@jcaron Forse sarebbe meglio se non fossi criptico su qualcosa che chiaramente non è semplice. Gli alberi B possono essere utilizzati per le BETWEENquery. Non vedo perché nel caso peggiore non si possano avere 2 indici e quindi i risultati filtrati di ciascun indice vengono uniti. (Questo è qualcosa che gli RDBMS fanno internamente quando ritengono che valga la pena usare più indici.) Se un indice combinato funziona, dovrebbe filtrare una dimensione interamente al primo livello e poi restringere relativamente rapidamente al secondo livello.
jpmc26,

2
@jcaron in realtà puoi usare l'indice per qualcosa di simile, y between -68 and -69 and x between 10 and 11ma ovviamente l'indice spaziale fa un lavoro migliore per quell'attività
Juan Carlos Oropeza,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.