Selecione os recursos que NÃO se cruzam no PostGIS


41

Parece-me uma pergunta tão simples (e provavelmente é), mas não consigo encontrar um exemplo que me dê a resposta. Usando o PostGIS, eu só quero selecionar pontos que ficam fora dos polígonos. Em última análise, este é o inverso do ST_Intersects, tanto quanto eu posso ver.

Exemplo: Eu tenho uma camada de taxlot e uma camada de ponto de endereço. Eu suponho que eu deveria estar usando o ST_Intersects, mas como eu digo para fazer a seleção reversa? Eu pensei que talvez adicionando uma instrução NOT na frente do código abaixo, mas isso não funcionou.

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

Eu tive o mesmo processo de pensamento, o pensamento de que o NÃO também iria fazer o truque como qualquer outro em que condição
— Luffydude

Respostas:


41

O motivo pelo qual ele não funciona com "não se cruza" é que você só compara geometrias em pares; haverá o mesmo problema com a disjunção. Cada ponto doméstico separa algumas parcelas, mesmo que elas cruzem uma parcela.

A sugestão do underdark não tem esse problema. Há também outro truque que provavelmente fará um uso mais eficaz dos í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;

A idéia é juntá-los ao st_intersects e obter as linhas onde o ID do pacote não está presente.

Os índices necessários aqui são um índice espacial e um índice sobre gid em parcelas (assumindo que a tabela id em parcelas também seja chamada gid).


2
Muito obrigado! Nicklas está exatamente correto que ST_Disjoint não produzirá os resultados corretos. ST_Disjoint retorna todos os recursos porque, como ele apontou, cada ponto é separado por alguns polígonos de parcela na tabela, enquanto esse trecho de código me deu os resultados que eu esperava.
— perfil completo de RyanDalton

Essa consulta será planejada da mesma forma que esta gis.stackexchange.com/a/136177/6052, portanto, é uma questão de estilo que você prefere. =) Para aquelas respostas de compras.
— Evan Carroll

14

Você pode estar procurando por ST_Disjoint

ST_Disjoint - Retorna VERDADEIRO se as geometrias não se "interceptam espacialmente" - se não compartilharem nenhum espaço juntos.


2
Embora ST_Disjoint faça isso, ele não usa índices espaciais. Você vai esperar muito tempo
— nickves

9

Caso não haja função 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

Aqui usamos NOT EXISTSe 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

Que tal ST_Disjoint? - Retorna VERDADEIRO se as geometrias não "se interceptarem espacialmente" - se não compartilharem nenhum espaço juntos.


4
whoops - precisa fazer uma atualização da página antes de responder :-)
— Ian Turton

1

Em alguns casos, é muito útil usar LATERAL JOIN, pode ser muito rápido. Deve parecer

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

Simplesmente usando NOT antes que o ST_Intersects faça o truque:

Isso obtém todos os endereços que não estão no bairro # 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'

Observe a ordem das colunas geom - os polígonos primeiro, os pontos segundo, que são revertidos do uso usual de ST_Intersects.

Rápido e simples! Estive pensando como fazer isso corretamente por um tempo!


Também trabalhou para "NOT ST_Within". Minha consulta foi concluída em ~ 30,0 segundos para o NOT ST_Within e usando uma junção externa e, em seguida, verificando Null no lado direito, para que não pareça haver nenhum impacto no desempenho. Obrigado!
— Nate Wanner

@NateWanner bom saber! Não acredito como é fácil e rápido !!!
— precisa saber é o seguinte

Este é realmente um muito má idéia, porque você está recebendo o produto cartesiano
— Evan Carroll

@EvanCarroll, o que isso significa?
— precisa saber é o seguinte

Isso significa que, se você não está apenas recebendo 1 endereço denver, está recebendo um para cada bairro denver.não correspondente.
— Evan Carroll

-1

Esta pode não ser a solução mais rápida ... Mas geralmente trapaceio ao juntar todos os recursos da outra tabela.

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

Agradável e ágil se a tabela not_in_here não for tão complexa.


Isso nunca é rápido. Não é tão desagradável como seria se not_in_here for complexo. ;)
— Evan Carroll
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.