Finden der nächstgelegenen Geometrie in PostGIS

16

Ich habe mir die PostGIS "API" von Funktionen angesehen und festgestellt, dass die meisten von ihnen zwei Elemente zum Vergleichen benötigen. Beispielsweise benötigt die Funktion ST_Distance zwei Geometrie- / Geografieelemente, um die Entfernung zu ermitteln.

Es gibt keine Funktion, um etwas zu tun wie: "Gib mir bei gegebener Geometrie G die nächstgelegene Geometrie GClosest in Tabelle T, wobei G.id <> GClosest.id"

Mir ist klar, dass ich eine PL / PgSQL-Funktion schreiben könnte, um die Tabelle zu durchlaufen und ST_Distance für jedes Element aufzurufen, aber ich hoffe, dass es eine bessere, effizientere Lösung gibt.

Jmoney38
quelle
1
Wenn Sie an der Entfernung zur nächsten Geometrie interessiert sind, überprüfen Sie gis.stackexchange.com/questions/11979/…
underdark
lass es mich wissen, wenn ich es richtig verstanden habe ... möchtest du das nächste Feature, das die gleiche Entfernung hat wie das am nächsten liegende?
Falcacibar

Antworten:

7

Ihre Frage kann auch durch eine einzelne (wenn auch komplexe) Abfrage wie die folgende beantwortet werden, die den gesamten Datensatz und den Abstand zur Referenzgeometrie zurückgibt. Bitte beachten Sie, dass alle Datensätze zurückgegeben werden, wenn mehr als ein Datensatz mit der Mindestentfernung übereinstimmt .

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;

Ich habe diese Abfrage auf Adressentabelle getestet und es funktioniert. In der obigen Abfrage suche ich den nächstgelegenen Punkt mit id = 3.

unicoletti
quelle
Dies sind gute Informationen - danke ... Ich verstehe die Min (..) - Aggregatfunktion per Definition, bin aber verwirrt darüber, wie sie in Ihrem Beispiel verwendet wird. st_distance (X, Y) akzeptiert zwei Geometrietypen und gibt einen Abstand zwischen ihnen zurück, der ein einzelner Wert ist. Warum rufen Sie dann eine Aggregatfunktion für dieses Einzelwertergebnis auf? Vielleicht interpretiere ich die Anweisung inside select falsch ...
Jmoney38
Die Gruppierung nach befindet sich in der ga- Geometrie, die eine Konstante für die gesamte Ergebnismenge ist (denken Sie daran, dass ga durch id = 3 ausgewählt wird), sodass im Grunde nichts unternommen wird. Das ist nur ein Trick, um die ga- Geometrie in der st_distance der äußeren Abfrage verfügbar zu haben, ohne die Tabelle erneut zu verknüpfen . Heute dachte ich, dass ich mit der Partitionsklausel vielleicht die innere Abfrage insgesamt umgehen könnte . Das sollte auch die Leistung verbessern. Ich werde es versuchen und dich wissen lassen.
unicoletti
Leider wurden in 8.4 Fensterfunktionen eingeführt, und jetzt habe ich keinen Zugriff auf einen Server, auf dem sowohl postgis als auch diese Version installiert sind, sodass ich eine mit der Partitionsklausel umgeschriebene Abfrage nicht testen kann.
unicoletti
7

George MacKerron hat eine einfache Nearest Neighbor-Funktion geschrieben, die ich sehr nützlich fand. Diese Funktion gibt die ID des nächsten Nachbarn für ein bestimmtes Feature zurück:

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;

Anwendungsbeispiel:

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

... wählt für jeden Eintrag in my_point_table den nächsten Knoten in der Tabelle nw_node aus.

Es gibt auch eine allgemeinere Funktion auf der Boston GIS-Site .

Underdunkel
quelle
Ich beschäftige mich eher mit der Erstellung von Abfragen 1: N-Abfragen im Allgemeinen. Anstatt beispielsweise das der Geometrie G am nächsten liegende Element zu finden, möchte ich möglicherweise das erste Element finden, das sich mit G überschneidet. Vielen Dank für die Informationen, unabhängig davon. Der Link zu Boston GIS war sehr nützlich! Ich habe bereits einige der Spickzettel gedruckt :-)
Jmoney38
Vielleicht könnten Sie Ihre Frage umformulieren, um es ein wenig klarer zu machen, @ Jmoney48. Sie interessieren sich also nicht speziell für das Problem des nächsten Nachbarn, sondern für den Vergleich einer Geometrie mit allen Geometrien in einer Tabelle?
Underdunkel
Verwenden Sie IMMER die generische Funktion der Boston GIS-Site, die einfache Funktion ist für große Tabellen unglaublich langsam und der Aufwand, sie anzuwenden, ist nicht größer.
Vladtn