Trovare la geometria più vicina in PostGIS


16

Ho esaminato le "API" PostGIS delle funzioni e noto che la maggior parte di esse richiede due elementi per il confronto. Ad esempio, la funzione ST_Distance accetta due elementi geometria / geografia per trovare la distanza.

Non esiste una funzione per fare qualcosa del tipo: "Data una geometria G, dammi la geometria più vicina GClosest nella tabella T dove G.id <> GClosest.id"

Mi rendo conto di poter scrivere una funzione PL / PgSQL per iterare sul tavolo e chiamare ST_Distance su ciascun elemento, ma spero che ci sia una soluzione migliore, più efficiente.


1
Se siete interessati in lontananza alla geometria più vicino, Assegno gis.stackexchange.com/questions/11979/...
Sottosuolo

fammi sapere se ho capito bene ... vuoi la prossima funzione che abbia la stessa distanza della più vicina?
Falcacibar,

Risposte:


7

Alla tua domanda si può anche rispondere con una singola query (anche se complessa) come la seguente che restituisce l'intero record e la distanza alla geometria di riferimento. Se più di un record corrisponde alla distanza minima, vengono tutti restituiti.

SELECT 
  i.*,
  md.min_distance
FROM
  address AS i, 
  (SELECT 
     ga.address_geom,
     min( ST_Distance(
            ga.address_geom,
            gb.address_geom)
        ) AS min_distance
   FROM
     address AS ga,
     address AS gb 
   WHERE 
     ga.id <> gb.id 
   AND 
     ga.id = 3
   GROUP BY 
     ga.address_geom
  ) AS md 
WHERE 
  ST_Distance( i.address_geom, md.address_geom) = md.min_distance;

Ho testato questa query sulla tabella degli indirizzi e funziona. Nella query sopra sto cercando il punto più vicino a quello con id = 3.


Questa è una buona informazione - grazie ... Capisco la funzione aggregata min (..) per definizione, ma sono confuso su come viene usato nel tuo esempio. st_distance (X, Y) prende due tipi di geometria e restituisce una distanza tra loro, che è un singolo valore. Perché stai quindi chiamando una funzione aggregata su quel risultato a valore singolo? Forse sto fraintendendo l'istruzione select interna ...
Jmoney38

Il gruppo di è sulla geometria ga che è una costante per l'intero set di risultati (ricorda che ga è selezionato da id = 3), quindi praticamente non fa nulla. Questo è solo un trucco per avere la geometria ga disponibile nella st_distance della query esterna senza unire nuovamente la tabella due volte. Oggi stavo pensando che forse avrei potuto cavarmela del tutto con la query innner usando la clausola di partizione . Ciò dovrebbe anche migliorare le prestazioni. Ci proverò e ti farò sapere.
unicoletti,

Sfortunatamente le funzioni della finestra sono state introdotte in 8.4 e ora non ho accesso a un server che abbia sia Postgis che quella versione, quindi non posso testare una query riscritta con la clausola di partion.
unicoletti,

7

George MacKerron ha scritto una semplice funzione di vicinato più vicino che ho trovato abbastanza utile. Questa funzione restituisce l'ID del vicino più vicino a una determinata funzione:

create or replace function 
  nn(nearTo                   geometry
   , initialDistance          real
   , distanceMultiplier       real 
   , maxPower                 integer
   , nearThings               text
   , nearThingsIdField        text
   , nearThingsGeometryField  text)
 returns integer as $$
declare 
  sql     text;
  result  integer;
begin
  sql := ' select ' || quote_ident(nearThingsIdField) 
      || ' from '   || quote_ident(nearThings)
      || ' where st_dwithin($1, ' 
      ||   quote_ident(nearThingsGeometryField) || ', $2 * ($3 ^ $4))'
      || ' order by st_distance($1, ' || quote_ident(nearThingsGeometryField) || ')'
      || ' limit 1';
  for i in 0..maxPower loop
     execute sql into result using nearTo              -- $1
                                , initialDistance     -- $2
                                , distanceMultiplier  -- $3
                                , i;                  -- $4
    if result is not null then return result; end if;
  end loop;
  return null;
end
$$ language 'plpgsql' stable;

Esempio di utilizzo:

SELECT id, nn(pt_geom,0.00001,2,100,'nw_node','node_id','node_geom') FROM my_point_table;

... seleziona il nodo più vicino nella tabella nw_node per ogni voce in my_point_table.

Esiste anche una funzione più generica sul sito GIS di Boston .


Sono più interessato a come creare query 1: N in senso più generale. Ad esempio, invece di trovare l'elemento più vicino alla geometria G, potrei voler trovare il primo elemento che si sovrappone a G. Grazie per l'informazione, a prescindere. Il collegamento al Boston GIS è stato molto utile! Ho già stampato alcuni dei cheat sheet :-)
Jmoney38

Forse potresti riformulare la tua domanda per renderla un po 'più chiara, @ Jmoney48. Quindi non sei interessato in particolare al problema del vicino più vicino, ma piuttosto a come confrontare una geometria con tutte le geometrie in una tabella?
underdark

Utilizzate SEMPRE la funzione generica del sito GIS di Boston, quella semplice è incredibilmente lenta per i grandi tavoli e lo sforzo per applicarlo non è più grande.
Vladtn,
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.