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.