Sélectionnez les fonctionnalités qui ne se croisent pas dans PostGIS

41

Cela me semble être une question si simple (et c'est probablement le cas), mais je n'arrive pas à trouver un exemple qui me donne la réponse. À l’aide de PostGIS, je souhaite simplement sélectionner des points situés hors des polygones. En fin de compte, c'est l'inverse de ST_Intersects, autant que je puisse le voir.

Exemple: j'ai une couche taxlot et une couche de points d'adresses. Je suppose que je devrais utiliser ST_Intersects, mais comment puis-je lui demander de faire la sélection inverse? Je pensais peut-être ajouter une instruction NOT devant le code ci-dessous, mais cela n'a pas fonctionné.

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
la source
J'ai eu le même processus de pensée, pensais que le NON ferait aussi l'affaire comme n'importe quelle autre condition
Luffydude

Réponses:

41

La raison pour laquelle cela ne fonctionne pas avec "pas d'intersection" est que vous ne comparez les géométries que par paires; il y aura le même problème avec disjoint. Chaque point de séparation divisera certaines parcelles même s'il en coupe une.

La suggestion de Underdark n'a pas ce problème. Il existe également une autre astuce qui permettra probablement une utilisation plus efficace des index:

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;

L'idée est de les joindre avec st_intersects et d'obtenir les lignes où l'ID de parcelle n'est pas présent.

Les index nécessaires ici sont un index spatial et un index sur gid dans les parcelles (en supposant que l'id de la table parcels est également appelé gid).

Nicklas Avén
la source
2
Merci beaucoup! Nicklas a parfaitement raison de dire que ST_Disjoint ne produira pas les bons résultats. ST_Disjoint retourne toutes les entités car, comme il l'a fait remarquer, chaque point est disjoint de quelques polygones de parcelle dans la table, alors que cet extrait de code m'a donné les résultats que j'espérais.
RyanDalton
Cette requête sera planifiée de la même manière que celle-ci: gis.stackexchange.com/a/136177/6052, donc c'est une question de style que vous préférez. =) Pour ces réponses shopping.
Evan Carroll
14

Vous cherchez peut-être ST_Disjoint

ST_Disjoint - Retourne VRAI si les géométries ne "se croisent pas spatialement" - si elles ne partagent aucun espace ensemble.

Jason Scheirer
la source
2
Bien que ST_Disjoint le fasse, il n’utilise cependant pas d’index spatial. Vous allez attendre un bon bout de temps
nickves
9

S'il n'y a pas de fonction spécialisée:

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
sous-bois
la source
5

Ici nous utilisons NOT EXISTSet 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
la source
3

Que diriez-vous de ST_Disjoint? - Retourne VRAI si les géométries ne "se croisent pas spatialement" - si elles ne partagent aucun espace.

Ian Turton
la source
4
whoops - besoin de rafraîchir la page avant de répondre :-)
Ian Turton
1

Dans certains cas, il est très utile d’utiliser LATERAL JOIN, cela peut être très rapide.

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
la source
1

Il suffit de ne pas utiliser NOT avant ST_Intersects:

Cela donne toutes les adresses qui ne sont pas dans le voisinage n ° 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'

Notez l'ordre des colonnes geom - les polygones en premier, les points en second, qui sont inversés par rapport à l'usage habituel de ST_Intersects.

Rapide et simple! Je me demandais comment faire cela correctement depuis un moment!

DPSSpatial
la source
Également travaillé pour "NOT ST_Within". Ma requête s'est terminée en ~ 30,0 secondes à la fois pour NOT ST_Within et pour utiliser une jointure externe puis pour rechercher la valeur Null du côté droit, de sorte qu'il ne semble pas y avoir de perte de performances. Merci!
Nate Wanner
@NateWanner bon à savoir! Je ne peux pas croire à quel point c'est facile et rapide !!!
DPSSpatial le
C'est en fait une très mauvaise idée, car vous obtenez le produit cartésien
Evan Carroll le
@ EvanCarroll ça veut dire quoi?
DPSSpatial
Cela signifie que si vous n'obtenez pas seulement une adresse denver., vous en obtenez une pour chaque quartier ne correspondant pas.
Evan Carroll
-1

Ce n'est peut-être pas la solution la plus rapide ... Mais d'habitude, je ne fais que tricher en joignant toutes les fonctionnalités de l'autre table.

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

Sympa et accrocheur si la table not_in_here n'est pas si complexe.

Jamie Popkin
la source
Ce n'est jamais vif. Ce n'est tout simplement pas aussi désagréable que si ce n'était pas compliqué. ;)
Evan Carroll