Seleccione características que NO se cruzan en PostGIS

41

Esto me parece una pregunta tan simple (y probablemente lo sea), pero parece que no puedo encontrar un ejemplo que me dé la respuesta. Con PostGIS, solo quiero seleccionar puntos que quedan fuera de los polígonos. En última instancia, esto es lo contrario de ST_Intersects, por lo que puedo ver.

Ejemplo: tengo una capa de taxlot y una capa de punto de dirección. Supongo que debería estar usando ST_Intersects, pero ¿cómo le digo que haga la selección inversa? Pensé que tal vez agregar una declaración NOT delante del código a continuación, pero eso no funcionó.

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);
RyanDalton
fuente
Tuve el mismo proceso de pensamiento, pensé que el NOT también haría el truco como cualquier otra condición
Luffydude

Respuestas:

41

La razón por la que no funciona con "no se cruza" es que solo compara geometrías en pares; Habrá el mismo problema con disjuntas. Cada punto de la casa desunirá algunas parcelas, incluso si se cruza con una parcela.

La sugerencia de Underdark no tiene ese problema. También hay otro truco que probablemente hará un uso más efectivo de los índices:

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;

La idea es unirlos con st_intersects y obtener las filas donde la identificación de la parcela no está presente.

Los índices necesarios aquí son un índice espacial y un índice de gid en parcelas (suponiendo que id en la tabla de parcelas también se llama gid).

Nicklas Avén
fuente
2
¡Muchas gracias! Nicklas es exactamente correcto que ST_Disjoint no producirá los resultados correctos. ST_Disjoint devuelve todas las características porque, como señaló, cada punto está desunido con algunos polígonos de parcela en la tabla, mientras que este fragmento de código me dio los resultados que esperaba.
RyanDalton
Esta consulta se planificará de la misma manera que esta gis.stackexchange.com/a/136177/6052, por lo que es pura cuestión de estilo lo que prefiera. =) Para esas respuestas de compras.
Evan Carroll
14

Quizá estés buscando ST_Disjoint

ST_Disjoint: devuelve VERDADERO si las Geometrías no se "intersecan espacialmente", si no comparten ningún espacio juntos.

Jason Scheirer
fuente
2
Si bien ST_Disjoint hace eso, sin embargo, no utiliza índices espaciales. Vas a esperar muchísimo tiempo
nickves
9

En caso de que no haya una función especializada:

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
bajo oscuro
fuente
5

Aquí usamos NOT EXISTSy 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)
  );
Carl Sena Afenu
fuente
3

¿Qué tal ST_Disjoint? - Devuelve VERDADERO si las Geometrías no se "cruzan espacialmente" - si no comparten ningún espacio juntos.

Ian Turton
fuente
44
whoops - necesita actualizar la página antes de responder :-)
Ian Turton
1

En algunos casos es muy útil usar LATERAL JOIN, puede ser muy rápido. Debería verse como

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;
Jelen
fuente
1

Simplemente usando NOT antes de que ST_Intersects haga el truco:

Esto obtiene todas las direcciones que no están dentro del vecindario # 62:

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'

Tenga en cuenta el orden de las columnas geom: polígonos primero, puntos segundos, que se invierte del uso habitual de ST_Intersects.

¡Rápido y simple! ¡Me he estado preguntando cómo hacer esto correctamente por un tiempo!

DPSSpacial
fuente
También funcionó para "NOT ST_Within". Mi consulta se completó en ~ 30.0 segundos tanto para NOT ST_Within como para usar una combinación externa y luego buscar Null en el lado derecho, por lo que no parece haber ningún impacto en el rendimiento. ¡Gracias!
Nate Wanner
@NateWanner es bueno saberlo! ¡No puedo creer lo fácil y rápido que es!
DPSEspacial
Esta es realmente una muy mala idea porque estás obteniendo el producto cartesiano
Evan Carroll
@EvanCarroll, ¿qué significa eso?
DPSEspacial
Significa que si no solo obtienes 1 denver.address, obtendrás uno por cada denver.neighborhood que no coincida.
Evan Carroll
-1

Puede que esta no sea la solución más rápida ... Pero generalmente solo hago trampa al unir todas las características de la otra tabla.

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);

Agradable y ágil si la tabla not_in_here no es tan compleja.

Jamie Popkin
fuente
Eso nunca es rápido. Simplemente no es tan desagradable como lo sería si not_in_here es complejo. ;)
Evan Carroll