Wählen Sie Features aus, die sich in PostGIS NICHT überschneiden


41

Dies scheint mir eine so einfache Frage zu sein (und wahrscheinlich auch), aber ich kann kein Beispiel finden, das mir die Antwort gibt. Mit PostGIS möchte ich nur Punkte auswählen, die außerhalb von Polygonen liegen. Letztendlich ist dies die Umkehrung der ST_Intersects, soweit ich das beurteilen kann.

Beispiel: Ich habe einen Taxlot-Layer und einen Adresspunkt-Layer. Ich gehe davon aus, dass ich die ST_Intersects verwenden sollte, aber wie kann ich die umgekehrte Auswahl vornehmen? Ich dachte, ich füge vielleicht eine NOT- Anweisung vor dem Code unten hinzu, aber das hat nicht funktioniert.

CREATE table t_intersect AS
SELECT 
  hp.gid, 
  hp.st_address, 
  hp.city, 
  hp.st_num,
  hp.the_geom
FROM 
  public.parcel as par,
  public.housepoints as hp
WHERE 
  ST_Intersects(hp.the_geom,par.the_geom);

Ich hatte den gleichen Gedankengang, dachte, dass das NOT auch den Trick wie jede andere
Where-

Antworten:


41

Der Grund, warum es mit "Nicht schneidet" nicht funktioniert, ist, dass Sie Geometrien nur paarweise vergleichen. es wird das gleiche Problem mit disjunkt geben. Jeder Hauspunkt trennt einige Parzellen, selbst wenn er eine Parzelle schneidet.

Der Vorschlag von underdark hat dieses Problem nicht. Es gibt auch einen anderen Trick, mit dem Indizes wahrscheinlich effektiver genutzt werden können:

CREATE TABLE t_intersect AS
SELECT 
  hp.gid, 
  hp.st_address, 
  hp.city, 
  hp.st_num,
  hp.the_geom
FROM 
  public.housepoints AS hp LEFT JOIN
  public.parcel AS par ON
  ST_Intersects(hp.the_geom,par.the_geom)
WHERE par.gid IS NULL;

Die Idee ist, sie mit st_intersects zu verbinden und die Zeilen abzurufen, in denen die Paket-ID nicht vorhanden ist.

Die Indizes, die hier benötigt werden, sind ein räumlicher Index und ein Index für GID in Paketen (vorausgesetzt, die ID in der Pakettabelle wird auch GID genannt).


2
Vielen Dank! Nicklas ist genau richtig, dass ST_Disjoint nicht die richtigen Ergebnisse liefert. ST_Disjoint gibt alle Features zurück, da, wie er betonte, jeder Punkt mit einigen Flurstückpolygonen in der Tabelle disjunkt ist , während dieses Code-Snippet mir die erhofften Ergebnisse lieferte.
RyanDalton

Diese Abfrage wird genauso geplant wie die folgende: gis.stackexchange.com/a/136177/6052. Es ist also nur eine Frage des Stils, den Sie bevorzugen. =) Für die Einkaufsantworten.
Evan Carroll

14

Möglicherweise suchen Sie nach ST_Disjoint

ST_Disjoint - Gibt TRUE zurück, wenn sich die Geometrien nicht "räumlich schneiden" - wenn sie keinen gemeinsamen Raum haben.


2
Während ST_Disjoint dies tut, werden jedoch keine räumlichen Indizes verwendet. Du wirst noch eine Weile warten
nickves

9

Falls es keine spezialisierte Funktion gibt:

CREATE table t_intersect AS
SELECT 
  hp.gid, 
  hp.st_address, 
  hp.city, 
  hp.st_num,
  hp.the_geom
FROM
  public.housepoints as hp
WHERE
  hp.gid NOT IN 
  (
    SELECT 
      h.gid
    FROM 
      public.parcel as p,
      public.housepoints as h
    WHERE 
      ST_Intersects(h.the_geom,p.the_geom)
  ) AS foo

5

Hier verwenden wir NOT EXISTSund CREATE TABLE AS SELECT(CTAS)

CREATE table t_intersect
AS
  SELECT 
    hp.gid,
    hp.st_address,
    hp.city, hp.st_num,
    hp.the_geom
  FROM public.housepoints AS hp
  WHERE NOT EXISTS (
    SELECT 1
    FROM public.parcel AS par 
    WHERE ST_Intersects(hp.the_geom,par.the_geom)
  );

3

Wie wäre es mit ST_Disjoint? - Gibt TRUE zurück, wenn sich die Geometrien nicht "räumlich schneiden" - wenn sie keinen gemeinsamen Raum haben.


4
whoops - müssen Sie eine Seite aktualisieren, bevor Sie antworten :-)
Ian Turton

1

In einigen Fällen ist es sehr nützlich, LATERAL JOIN zu verwenden, es kann sehr schnell sein. Es sollte so aussehen

SELECT * FROM houses h
LEFT JOIN LATERAL (
   SELECT True t FROM parcels p
   WHERE ST_Intersects(p.geom, h.geom)
   LIMIT 1
) p ON True
WHERE p.t IS NULL;

1

Einfach NOT verwenden, bevor ST_Intersects den Trick macht:

Dadurch werden alle Adressen abgerufen, die sich nicht in der Nachbarschaft Nr. 62 befinden:

select 
a.*
from denver.neighborhoods as n
join denver.addresses as a on not ST_Intersects(n.geom, a.geom)
where n.nbhd_id = '62'

Beachten Sie die Reihenfolge der Geom-Spalten - Polygone zuerst, Punkte zweitens, was sich von der üblichen Verwendung von ST_Intersects umkehrt.

Schnell und einfach! Ich habe mich eine Weile gefragt, wie ich das richtig machen soll!


Arbeitete auch für "NOT ST_Within". Meine Abfrage wurde in ~ 30,0 Sekunden für NOT ST_Within und mithilfe eines Outer-Joins abgeschlossen. Dann wurde auf der rechten Seite nach Null gesucht, sodass keine Leistungseinbußen zu verzeichnen sind. Vielen Dank!
Nate Wanner

@NateWanner gut zu wissen! Ich kann nicht glauben, wie einfach und schnell das ist !!!
DPSSpatial

Dies ist eigentlich eine ziemlich schlechte Idee, weil Sie das kartesische Produkt bekommen
Evan Carroll

@EvanCarroll was bedeutet das?
DPSSpatial

Wenn Sie nicht nur 1 denver.address erhalten, erhalten Sie eine für jede nicht passende denver.neighborhood.
Evan Carroll

-1

Dies ist möglicherweise nicht die schnellste Lösung ... Aber ich betrüge normalerweise nur, indem ich alle Funktionen des anderen Tisches verbinde.

Create table blah as
select
  d.*
from
  data_i_want d,
  (select st_union(geom) geom from not_in_here) n
where
  st_disjoint(d.geom,n.geom);

Schön und bissig, wenn die not_in_here- Tabelle nicht so komplex ist.


Das ist nie bissig. Es ist einfach nicht so unglücklich, wie es wäre, wenn not_in_here komplex wäre. ;)
Evan Carroll
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.