Ottimizzare il calcolo del vicino più vicino con PostGIS


13

Sto usando PostGIS per calcolare i vicini più vicini di poligoni. Quello che voglio calcolare è la distanza minima da ciascun poligono al poligono più vicino.

Finora ho ricevuto un grande aiuto dalla risposta di Mike Toews (che cito con una piccola modifica) qui:

SELECT 
  a.hgt AS a_hgt,
  b.hgt AS b_hgt,
  ST_Distance(a.the_geom, b.the_geom) AS distance_between_a_and_b
FROM 
  public."TestArea" AS a, public."TestArea" AS b
WHERE
  a.hgt !=  b.hgt AND ST_Distance(a.the_geom, b.the_geom) < 400

Quindi ho calcolato il minimo:

SELECT a_hgt, MIN(distance_between_a_and_b)
FROM public."lon_TestArea"
GROUP BY a_hgt

Tuttavia, la mia sfida è calcolare questo per un gran numero di poligoni (1.000.000). Dato che il calcolo sopra riportato confronta ogni poligono con ogni altro poligono, mi chiedevo come avrei potuto migliorare il calcolo in modo da non dover eseguire calcoli 10 ^ 12.

Pensavo di dover bufferizzare ogni poligono, quindi calcolare i vicini più vicini di tutti i valori all'interno del buffer per quel poligono e registrare il minimo. Non sono sicuro se questo sia l'approccio migliore o se in PostGIS sia presente una funzione che dovrei utilizzare.


EDIT: Utilizzando uno dei suggerimenti di Nicklas, sto sperimentando ST_Dwithin():

CREATE TABLE mytable_withinRange AS SELECT 
  a.hgt AS a_hgt,
  b.hgt AS b_hgt,
  ST_DWithin(a.the_geom, b.the_geom, 400)
FROM 
  public."lon_TestArea" AS a, public."lon_TestArea" AS b

inserisci qui la descrizione dell'immagine

Ciò restituisce una tabella dell'ID di ciascun poligono e se si trova entro una certa distanza o meno. È possibile costruire un'istruzione di IF/ELSEtipo usando SQL? (Ho letto sull'uso della CASEcondizione) O dovrei provare a unire la tabella che produco alla tabella originale e quindi eseguire nuovamente la query usando ST_Distance?


dai un'occhiata al secondo esempio nel link Boston Boston nella mia risposta. dovresti usare st_dwithin nella parte dove della query.
Nicklas Avén,

Risposte:


7

C'è una grande sezione "Il vicino più vicino" nella pagina BostonGIS .


MODIFICARE:

Che ne dite di

CREATE TABLE mytable_withinRange AS SELECT 
 a.hgt AS a_hgt,
 b.hgt AS b_hgt
FROM 
 public."lon_TestArea" AS a, public."lon_TestArea" AS b
WHERE 
 ST_DWithin(a.the_geom, b.the_geom, 400)

Per quanto riguarda la dichiarazione CASE :

SELECT a,
   CASE WHEN a=1 THEN 'one'
        WHEN a=2 THEN 'two'
        ELSE 'other'
   END
FROM test;

Sai se la linea WHERE ST_DWithin(a.the_geom, b.the_geom, 400)impedirà che distanze maggiori 400vengano calcolate o appena registrate? Inoltre, è possibile utilizzare un'istruzione case per i calcoli numerici? per esempio:CASE WHEN ST_DWithin(a.the_geom, b.the_geom, 400) == TRUE THEN ST_DWithin(a.the_geom, b.the_geom)
djq

1
@celenius Se la distanza è superiore a 400 m, non verrà calcolato nulla nella parte selezionata. Non capisco perché vuoi mettere il caso nel mix.
Nicklas Avén,

@Nicklas ok - ho capito. Pensavo che avrebbe potuto significare che erano state memorizzate solo distanze inferiori a 400; questo rende molto più facile di quanto pensassi. Grazie!
DJ

3

ciao

Ci sono alcune cose da considerare per rendere le cose più veloci e alcune cose che potrebbero essere possibili in futuro.

Innanzitutto , hai detto che stai pensando di utilizzare un buffer per trovare poligoni in un intervallo minimo per evitare di calcolare tutte le combinazioni.

Come discusso in un altro link di Boston, il modo giusto per farlo in PostGIS è usare ST_Dwithin . ST_Dwithin utilizza l'indice per trovare i vicini in un determinato intervallo.

Dipende ovviamente dal set di dati se è sufficiente utilizzare un valore fisso per st_DWithin per tutti i poligoni o se è necessario fare qualcosa come underdark e wildintellect che stanno discutendo.

Una seconda cosa è usare PostGIS 1.5+ qui. Questo perché i calcoli da poligono a poligono sono molto più veloci dall'1.5 se i loro rettangoli non si intersecano. Puoi leggere di più al riguardo qui. .

La terza cosa da menzionare è il futuro.

In PostgreSQL 9.1 ci sarà qualcosa chiamato knn-gist. Questo è un indice che può non solo rispondere sì o no ma anche restituire il risultato ordinato direttamente dall'indice. Puoi leggerlo qui .

Ma ci sarà ancora molto lavoro da fare sul lato PostGIS prima che Knn Gist possa aiutare in cose come questa. C'è un biglietto per questo qui.

Saluti

Nicklas


Grazie per i suggerimenti Nicklas; poiché ho trovato difficile far funzionare pgAdmin / PostGIS, penso che al momento eviterò di usare 1.5. Sembra che ST_Dwithin () sia un modo per risolverlo.
djq,

2
l'installazione di 1.5 non influirà sulla relazione tra postgresql e pgadmin. puoi avere più di una versione di postgis nel server di database e quindi caricarne una nel database. quindi puoi avere un database 1.4 e uno 1.5 uno stesso server di database.
Nicklas Avén,

1

Le pagine seguenti relative al lavoro dei maestri di Nathan Kerr forniscono alcune buone informazioni su questo problema diretto. Il mio collega ha provato il metodo Bostongis qui e qui , ma ha avuto alcuni problemi a farlo funzionare correttamente.

Un altro approccio a cui pensare che è simile al buffer è fare un rettangolo in espansione / contrazione. Fondamentalmente passa 1 esegui un rettangolo di selezione (è una unità + + diritta alla bbox del poligono originale) che ritieni possa catturare almeno un incrocio. Per i dati che hanno ottenuto un'intersezione, eseguire una query secondaria che verifica quelle corrispondenze per il più vicino. Per i dati non corrispondenti, espandere il rettangolo di selezione e ripetere.

È chiaramente un problema di programmazione ricorsiva e potrebbe essere fatto meglio in Python con Shapely del 100% direttamente in postgis.

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.