Выберите объекты, которые НЕ пересекаются в PostGIS

41

Мне кажется, что это такой простой вопрос (и, вероятно, это так), но я не могу найти пример, который дает мне ответ. Используя PostGIS, я просто хочу выбрать точки, которые выходят за пределы полигонов. В конечном счете, это обратное к ST_Intersects, насколько я понимаю.

Пример: у меня есть слой таксота и слой адресной точки. Я предполагаю, что должен использовать ST_Intersects, но как мне сказать, чтобы он делал обратный выбор? Я подумал, может быть, добавив оператор NOT перед кодом ниже, но это не сработало.

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
источник
У меня был такой же мыслительный процесс, я думал, что НЕ будет делать то же самое, что и любое другое, где условие
Luffydude

Ответы:

41

Причина, по которой он не работает с «не пересекаются», состоит в том, что вы сравниваете геометрию только парами; будет такая же проблема с дизъюнктом. Каждая точка дома будет разъединять некоторые посылки, даже если она пересекает одну посылку.

Предложение Подземья не имеет этой проблемы. Существует также еще одна хитрость, которая, вероятно, позволит более эффективно использовать индексы:

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;

Идея состоит в том, чтобы соединить их с st_intersects и получить строки, в которых нет идентификатора участка.

Здесь нужны индексы: пространственный индекс и индекс gid в посылках (при условии, что id в таблице посылок также называется gid).

Никлас Авен
источник
2
Большое спасибо! Никлас абсолютно прав, что ST_Disjoint не даст правильных результатов. ST_Disjoint возвращает все функции, потому что, как он указал, каждая точка не связана с некоторыми полигонами участков в таблице, в то время как этот фрагмент кода дал мне результаты, на которые я надеялся.
RyanDalton
Этот запрос будет спланирован так же, как и этот gis.stackexchange.com/a/136177/6052, так что это вопрос стиля, который вы предпочитаете. =) За эти покупки ответы.
Эван Кэрролл
14

Возможно, вы ищете ST_Disjoint

ST_Disjoint - возвращает TRUE, если геометрия не «пространственно пересекается» - если они не разделяют пространство вместе.

Джейсон Шайрер
источник
2
Хотя ST_Disjoint делает это, он, тем не менее, не использует пространственные индексы. Вы будете ждать более долгое время
nickves
9

В случае, если нет специализированной функции:

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

Здесь мы используем NOT EXISTSи 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

Как насчет ST_Disjoint? - Возвращает TRUE, если геометрия не "пространственно пересекается" - если они не разделяют пространство вместе.

Ян Тертон
источник
4
упс - нужно сделать обновление страницы перед ответом :-)
Ian Turton
1

В некоторых случаях очень полезно использовать LATERAL JOIN, оно может быть очень быстрым.

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

Простое использование NOT до того, как ST_Intersects добьется цели:

Это получает все адреса, которые не находятся в окрестности # 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'

Обратите внимание на порядок столбцов geom - сначала полигоны, затем точки второго, что отличается от обычного использования ST_Intersects.

Быстро и просто! Задумывались, как это сделать правильно какое-то время!

DPSSpatial
источник
Также работал для "НЕ ST_Within". Мой запрос завершился за ~ 30.0 секунд для NOT ST_Within и с использованием внешнего соединения, затем с правой стороны проверил наличие Null, так что, похоже, никакого снижения производительности не произошло. Благодарность!
Нейт Ваннер
@NateWanner приятно знать! Я не могу поверить, насколько это просто и быстро !!!
DPSSpatial
На самом деле это довольно плохая идея, потому что вы получаете декартово произведение
Эван Кэрролл
@EvanCarroll, что это значит?
DPSSpatial
Это означает, что если вы не просто получаете 1 denver.address, вы получаете один для каждого несоответствующего denver.neighborhood.
Эван Кэрролл
-1

Возможно, это не самое быстрое решение ... Но я обычно просто обманываю, объединяя все функции другой таблицы.

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

Хорошо и быстро, если таблица not_in_here не так сложна.

Джейми Попкин
источник
Это никогда не быстро. Это просто не так плохо, как если бы not_in_here сложный. ;)
Эван Кэрролл