Usi geohash per le ricerche di prossimità?


30

Sto cercando di ottimizzare i tempi delle ricerche geografiche di prossimità dei punti.

Il mio input è lat, lng point e sto cercando un set precompilato di posizioni in n punti più vicini.

Non mi interessa quanto tempo / spazio occuperà la costruzione dell'indice precompilato delle posizioni, ma mi preoccupo che le query saranno super veloci.

Sto pensando di utilizzare geohash come chiave di ricerca, dove prima verificherei se ottengo risultati per i caratteri X della chiave e poi continuo a tagliare i caratteri dalla fine della chiave fino a quando non inizio a vedere i risultati.

Per la mia (molto sparsa per ora) comprensione delle tecniche dell'indice geografico questo approccio dovrebbe essere in grado di produrre i risultati più rapidi (in termini di tempo di query) rispetto a tutte le altre implementazioni conosciute (come R Tree e co.)


C'è una differenza significativa tra l'uso di un geohash e la memorizzazione del lat / long in est / nord (ad esempio)? Presumibilmente con entrambi è possibile modificare la precisione della ricerca tagliando caratteri / cifre. (Questa è puramente una domanda per curiosità - non ho familiarità con questo argomento).
djq

Questi punti sono memorizzati in un database o in memoria o?
Marc Pfister,

@MarcPfister questo problema ha 2 anni (per il mio caso d'uso) ma è sempre rilevante per la comunità, quindi continuerò la discussione attiva. I dati discussi sono stati effettivamente archiviati in un database nosql.
Maxim Veksler

Inoltre, credo che dal momento in cui è stata data risposta a questa domanda MongoDB ha implementato con successo l'indicizzazione e la ricerca geohash, a dimostrazione di ciò. Non ho ancora visto un white paper sull'implementazione, ma il codice è aperto e disponibile per tutte le parti interessate.
Maxim Veksler

Ah ok. Anche CouchDB aveva ora l'indicizzazione spaziale, probabilmente usando anche geohash.
Marc Pfister,

Risposte:


25

Assolutamente si. E può essere abbastanza veloce. (I bit di calcolo intensivo possono anche essere distribuiti)

Esistono diversi modi, ma un modo con cui ho lavorato è l'utilizzo di un elenco ordinato basato su numeri interi geohash e la ricerca di tutti gli intervalli di geohash vicini più vicini per una risoluzione di geohash specifica (la risoluzione si avvicina ai tuoi distancecriteri) e quindi interrogare quegli intervalli di geohash per ottenere un elenco di punti vicini. Uso redis e nodejs (es. Javascript) per questo. Redis è super veloce e può recuperare molto rapidamente gli intervalli ordinati, ma non può fare molte delle cose di manipolazione delle query di indicizzazione che i database SQL possono fare.

Il metodo è delineato qui: https://github.com/yinqiwen/ardb/wiki/Spatial-Index

Ma l'essenza è (per parafrasare il link):

  1. Memorizzi tutti i tuoi punti di geohash nella migliore risoluzione che desideri (max di solito intero a 64 bit se accessibile, o nel caso di javascript, 52 bit) in un set ordinato (es. Zset in redis). La maggior parte delle librerie di geohash al giorno d'oggi hanno funzioni intere di geohash integrate e dovrete usarle al posto delle più comuni geohash di base32.
  2. In base al raggio in cui si desidera effettuare la ricerca, è quindi necessario trovare una profondità / risoluzione in bit che corrisponda all'area di ricerca e che questa deve essere inferiore o uguale alla profondità in bit di geohash memorizzata. Il sito collegato ha una tabella che mette in relazione la profondità di bit di un geohash con l'area del riquadro di delimitazione in metri.
  3. Quindi ridisegna le coordinate originali a questa risoluzione inferiore.
  4. A quella risoluzione più bassa trovi anche le 8 aree geohash vicine (n, ne, e, se, s, sw, w, nw). Il motivo per cui devi fare il metodo vicino, è perché due coordinate quasi una accanto all'altra potrebbero avere geohash completamente diversi, quindi devi fare una media dell'area coperta dalla ricerca.
  5. Una volta ottenuti tutti i geohash vicini con questa risoluzione inferiore, aggiungi all'elenco il geohash delle coordinate dal passaggio 3.
  6. Quindi è necessario creare un intervallo di valori geohash per cercare all'interno dei quali coprire queste 9 aree. I valori del passaggio 5 rappresentano il limite di intervallo inferiore e, se aggiungi 1 a ciascuno di essi, otterrai il limite di intervallo superiore. Quindi dovresti avere una matrice di 9 intervalli, ognuno con un limite inferiore e un limite superiore di geohash (18 geohash in totale). Questi geohash sono ancora in quella risoluzione inferiore dal passaggio 2.
  7. Quindi converti tutti i 18 di questi geohash in qualunque profondità / risoluzione di bit hai archiviato tutti i tuoi geohash nel tuo database. Generalmente lo fai spostando i bit nella profondità di bit desiderata.
  8. Ora puoi eseguire una query di intervallo per punti all'interno di questi 9 intervalli e otterrai tutti i punti approssimativamente entro la distanza del punto originale. Non ci saranno sovrapposizioni, quindi non è necessario eseguire intersezioni, ma solo query di portata, molto velocemente. (ad es. in redis: ZRANGEBYSCORE zsetname lowerLimit upperLimit, oltre i 9 intervalli prodotti in questo passaggio)

Puoi ottimizzare ulteriormente (in termini di velocità):

  1. Prendendo quelle 9 gamme dal passaggio 6 e trovando dove conducono l'una nell'altra. Di solito puoi ridurre 9 intervalli separati in circa 4 o 5 a seconda di dove si trovano le tue coordinate. Ciò può ridurre della metà il tempo di interrogazione.
  2. Una volta che hai i tuoi range finali, dovresti tenerli per il riutilizzo. Il calcolo di questi intervalli può richiedere la maggior parte del tempo di elaborazione, quindi se le coordinate originali non cambiano molto ma è necessario ripetere nuovamente la stessa query di distanza, è necessario tenerla pronta anziché calcolarla ogni volta.
  3. Se stai usando redis, prova a combinare le query in un MULTI / EXEC in modo da condurle per prestazioni leggermente migliori.
  4. La parte MIGLIORE: è possibile distribuire i passaggi 2-7 sui client invece di eseguire tutti i calcoli in un'unica posizione. Ciò riduce notevolmente il carico della CPU in situazioni in cui arriverebbero milioni di richieste.

È possibile migliorare ulteriormente la precisione utilizzando una funzione di distanza del cerchio / tipo haversine sui risultati restituiti se si tiene molto alla precisione.

Ecco una tecnica simile che utilizza i normali geohashes base32 e una query SQL anziché redis: https://github.com/davetroy/geohash-js

Non intendo collegare le mie cose, ma ho scritto un modulo per nodejs e redis che lo rende davvero facile da implementare. Dai un'occhiata al codice se desideri: https://github.com/arjunmehta/node-georedis


Un paio di follow-up D - Come si calcolano i vicini? L'hash intero consente il taglio (base32 curva z no, ad es. (7 è molto lontano da 8 in base32 geohash). Come viene delineato il metodo in geohash-js github.com/davetroy/geohash-js/blob/ master / matrix.txt similar? Mentre questo algoritmo dovrebbe produrre geohash-js di geohash-js di prossimità fa solo il calcolo O (1) delle celle vicine
Maxim Veksler,

Caspita, è stato così utile. Tanta esperienza in questa risposta. Compito abbastanza impegnativo
simon

9

La domanda potrebbe essere letta in diversi modi. Lo interpreto nel senso che hai un gran numero di punti e intendi sondarli ripetutamente con punti arbitrari, dati come coppie di coordinate, e desidero ottenere i n punti più vicini alla sonda, con n fissati in anticipo. (In linea di principio, se n varia, è possibile impostare una struttura di dati per ogni possibile n e selezionarla in O (1) tempo con ogni sonda: ciò potrebbe richiedere tempi di installazione molto lunghi e richiedere molta RAM, ma noi viene detto di ignorare tali preoccupazioni.)

Costruisci il diagramma ordine-n Voronoi di tutti i punti. Ciò divide il piano in regioni collegate, ognuna delle quali ha gli stessi n vicini. Ciò riduce la situazione al problema del punto in poligono, che presenta molte soluzioni efficienti.

Utilizzando una struttura di dati vettoriali per il diagramma Voronoi, le ricerche punto-in-poligono impiegheranno il tempo O (log (n)). Ai fini pratici è possibile creare questa O (1) con un coefficiente implicito estremamente piccolo semplicemente creando una versione raster del diagramma. I valori delle celle nel raster sono (i) un puntatore a un elenco degli n punti più vicini o (ii) un'indicazione che questa cella si trova a cavallo di due o più regioni nel diagramma. Il test per un punto arbitrario in (x, y) diventa:

Fetch the cell value for (x,y).
If the value is a list of points, return it.
Else apply a vector point-in-polygon algorithm to (x,y).

Per ottenere prestazioni O (1), la mesh raster deve essere sufficientemente fine da far cadere relativamente pochi punti sonda in celle che si trovano a cavallo di più regioni Voronoi. Questo può sempre essere realizzato, con una spesa potenzialmente grande per lo stoccaggio delle griglie.


3

Uso i geohashes proprio per questo. Il motivo per cui mi trovo è perché avevo bisogno di implementare ricerche di prossimità usando un sistema informativo a piramide .. dove i geohash con una precisione di 8 ° livello erano la "base" e formavano nuovi totali per geohash della 7a precisione ... e così via e così via . Questi totali erano area, tipi di copertura del terreno, ecc. Era un modo molto elegante per fare alcune cose molto fantasiose.

Quindi i geohash di 8 ° livello conterrebbero informazioni come:

tipo: acri di erba: 1,23

e 7, 6 .. ecc. conterrebbero informazioni come:

tipi di erba: 123 acri: 6502

Questo è stato sempre costruito con la massima precisione. Questo mi ha permesso di fare ogni sorta di statistiche divertenti molto rapidamente. Sono stato anche in grado di assegnare un riferimento alla geometria a ciascun riferimento geohash usando GeoJSON.

Sono stato in grado di scrivere diverse funzioni per trovare i geohash più grandi che compongono il mio viewport attuale e quindi usarli per trovare geohash della seconda precisione più grande all'interno del viewport. Ciò potrebbe essere facilmente esteso alle query su intervalli indicizzati in cui richiederei un minimo di "86ssaaaa" e un massimo di "86sszzzz" per qualsiasi precisione desiderassi.

Lo sto facendo usando MongoDB.


3

Aggiornamento per il 2018 e alcuni fondi matematici o provenienza storica di Geohash:

  • l'ispirazione per Geohash era il semplice interlave di cifre binarie , forse un'ottimizzazione di algoritmi ingenui che intercalavano cifre decimali, come il quadrato di C-quadrati .

  • l'interlacciamento binario ha portato naturalmente a una strategia dell'indice della curva dell'ordine Z , l'inventore di Geohash non ha iniziato a "cercare la migliore curva frattale" ... Ma curiosamente, questa ottimizzazione del design, una migliore curva frattale, è possibile (!).

Utilizzare la libreria geometria S2

L'approccio con geometria S2 è migliore per Geohash perché utilizza la topologia sferica del globo (un cubo), usa la proiezione opzionale (quindi tutte le celle hanno la stessa forma e area vicina) e poiché l'indicizzazione con la curva di Hilbert è migliore per Z- curva d'ordine :

... possiamo fare di meglio ... La discontinuità mentre andiamo dal quad in alto a destra in basso a sinistra ci fa dover dividere alcune gamme che altrimenti potremmo rendere contigue. (...) possiamo eliminare completamente qualsiasi discontinuità (...)
blog.notdot.net/2009 sull'indicizzazione spaziale con Quadtrees e Hilbert Curves

Ora è una libreria gratuita ed efficiente, vedi https://s2geometry.io

PS: ci sono anche (buone) versioni semplificate non ufficiali come NodeJSs2-geometry e molti "campi da gioco", componenti aggiuntivi e demo, come s2.sidewalklabs.com .


2

Consiglierei di utilizzare la query GEORADIUS in redis.

Invia i dati suddivisi in base al livello di geohash più adatto utilizzando la chiamata GEOADD.

Inoltre, dai un'occhiata a questo -> ProximityHash .

ProximityHash genera un insieme di geohash che coprono un'area circolare, date le coordinate centrali e il raggio. Ha anche un'opzione aggiuntiva per utilizzare GeoRaptor che crea la migliore combinazione di geohashes su vari livelli per rappresentare il cerchio, a partire dal livello più alto e iterando fino a ottenere la miscela ottimale. L'accuratezza dei risultati rimane la stessa del livello iniziale di geohash, ma le dimensioni dei dati si riducono notevolmente, migliorando così la velocità e le prestazioni.

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.