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

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


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.


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

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

3

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


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;

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!


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.


Eso nunca es rápido. Simplemente no es tan desagradable como lo sería si not_in_here es complejo. ;)
Evan Carroll
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.