Vous utilisez ST_Difference pour supprimer les fonctionnalités qui se chevauchent?

11

J'essaie d'utiliser ST_Difference pour créer un ensemble de polygones (processing.trimmedparcelsnew) qui ne contiennent aucune des zones couvertes par un autre ensemble de polygones (test.single_geometry_1) à l'aide de PostGis 2.1 (et Postgres SQL 9.3). Voici ma requête:

CREATE TABLE processing.trimmedparcelsnew AS
SELECT
    orig.id, ST_Difference(orig.geom, cont.geom) AS difference
FROM 
    test.single_geometry_1 cont,
    test.multi_geometry_1 orig;

Mais les polygones résultants n'ont pas été rognés, mais semblent avoir été séparés à leur intersection avec l'autre couche. J'ai essayé d'exécuter la sélection sans mettre le résultat dans un tableau et tout ce à quoi je peux penser, mais je n'arrive pas à faire fonctionner cette fonction.

J'ai joint une photo du résultat

entrez la description de l'image ici


Après les commentaires, j'ai essayé d'ajouter une clause WHERE. Je veux que les parcelles qui n'ont pas d'intersections et les zones d'intersection des autres parcelles soient supprimées (la couche test.single_geometry représente la contamination que je veux supprimer de mes parcelles). J'ai essayé une intersection mais bien sûr je veux en fait les non intersections donc j'essaye maintenant une disjonction. J'ai également essayé d'ajouter l'orig à ma table, mais la documentation de ST_Difference ( http://postgis.net/docs/ST_Difference.html ) dit qu'elle renvoie la géométrie exacte dont j'ai besoin (une géométrie qui représente la partie de la géométrie A qui n'intersecte pas la géométrie B), je ne comprends donc pas pourquoi je voudrais plutôt le polygone d'origine dans ma table. Quoi qu'il en soit, voici mon code modifié:

CREATE TABLE processing.trimmedparcelsnew AS
SELECT
    orig.id, ST_Difference(orig.geom, cont.geom) AS difference, orig.geom AS geom
FROM 
    test.single_geometry_1 cont,
    test.multi_geometry_1 orig
WHERE ST_Disjoint(orig.geom, cont.geom);

Suite à la réponse de dbaston, j'ai maintenant essayé:

CREATE TABLE processing.parcels_trimmed AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                         FROM test.single_geometry_1 b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         AND a.id != b.id)), a.geom)
FROM test.multi_geometry_1 a;

Le résultat est juste une copie de test.multi_geometry_1. Bien que maintenant le fractionnement ne se produise plus.

J'ai essayé la version précédente, mais encore une fois, obtenez une copie de test.multi_geometry_1:

CREATE TABLE processing.parcels_trimmed_no_coalesce AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                         FROM test.single_geometry_1 b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         AND a.id != b.id)), a.geom)
FROM test.multi_geometry_1 a;

Je commence à me demander s'il y a autre chose que je fais mal? L'énoncé de procédure est le suivant:

DROP TABLE IF EXISTS processing.parcels_trimmed_no_coalesce;

Et j'exécute les requêtes depuis la fenêtre de requête SQL PostgreSQL et Openjump.

La déclaration que j'utilise pour voir le tableau est:

SELECT * FROM processing.parcels_trimmed_no_coalesce;

Dans un souci de simplification, j'ai maintenant réduit cette requête à:

SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                         FROM test.geometriestocutagainst b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         AND a.id != b.id)), a.geom)
FROM test.geometriestocut a;

Il en résulte toujours uniquement les polygones d'origine (test.geometriestocut) lorsque le résultat souhaité est l'original ajusté par rapport à test.geometriestocutagainst.

Marché
la source
Vous n'avez pas spécifié de WHEREclause, vous pouvez donc avoir une expansion polynomiale dans la table résultante. Combien de lignes y a- trimmedparcelsnewt-il?
Vince
Si vous ne voulez que la différence où ils se croisent, vous pouvez essayer d'ajouter WHERE ST_Intersects (orig.geom, cont.geom). Sinon, la différence de deux polygones qui ne se coupent pas est le polygone d'origine.
John Powell
Il y a 24 lignes dans la parcelle rognée neuves, je veux la différence même lorsqu'elles ne se croisent pas, alors devrais-je corriger que je dois utiliser orig.geom dans le tableau plutôt que différence?
Mart
Un test disjoint devrait produire une expansion polynomiale - chaque entité cont apparaîtra une fois pour chaque entité d'origine qui ne se chevauche pas, et la différence ne changera jamais une géométrie d'entrée
Vince
Ok merci pour la clarification, mais je ne sais toujours pas pourquoi le code d'origine ne fonctionne pas. Si ST_Difference (orig.geom, cont.geom) renvoie les géométries dans a qui ne se coupent pas avec b, pourquoi la table contient-elle les géométries divisées plutôt que les géométries dans a qui ne coupent pas b.
Mart

Réponses:

14

Une auto-jointure vous permet d'opérer sur la relation entre des paires de deux fonctionnalités. Mais je ne pense pas que les paires vous intéressent: pour chaque entité, vous voulez opérer sur la relation entre cette entité et toutes les autres entités de votre jeu de données. Vous pouvez accomplir cela avec une expression de sous-requête:

CREATE TABLE parcels_trimmed AS
SELECT id, ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                FROM parcels b
                                WHERE ST_Intersects(a.geom, b.geom)
                                  AND a.id != b.id))
FROM parcels a;

Vous pourriez cependant voir quelque chose d'étrange dans les résultats. Les colis qui n'ont pas de chevauchement sont entièrement abandonnés! C'est parce que l' ST_Unionagrégat sur un jeu d'enregistrements vide va être NULL, et ST_Difference(geom, NULL)est NULL. Pour faciliter cela, vous devez encapsuler votre ST_Differenceappel dans un COALESCE:

CREATE TABLE parcels_trimmed AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                         FROM parcels b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         AND a.id != b.id)), a.geom)
FROM parcels a;

Cela signifie que si le résultat de ST_Differenceest NULL, l'expression fusionnée sera évaluée selon la géométrie d'origine.

La requête ci-dessus supprimera entièrement les zones qui se chevauchent de votre domaine. Si vous souhaitez plutôt choisir un gagnant, vous pouvez le faire a.id < b.id, ou un autre critère, au lieu de a.id != b.id.

dbaston
la source
Merci pour la réponse, malheureusement, j'ai du mal à obtenir que cela fonctionne pour moi, au lieu de cela, je me retrouve avec le polygone d'origine (a). Je vais modifier ma question avec plus d'informations. Merci encore.
Mart
2

J'ai eu le même problème que toi. Je ne sais pas si vous avez déjà trouvé une solution à votre problème, mais j'ai modifié la réponse acceptée ci-dessus et j'ai obtenu ce que je voulais.

CREATE TABLE parcels_trimmed AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Collect(b.geom) 
                                         FROM parcels b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         )), a.geom)
FROM parcels a;
Dag
la source
1

J'utilise ST_DifferenceAgg () des extensions PostGIS . Vous devez fusionner les deux tables ensemble, avoir un identifiant unique et un index sur la colonne de géométrie. Voici un petit exemple:

WITH overlappingtable AS (
  SELECT 1 id, ST_GeomFromText('POLYGON((0 1, 3 2, 3 0, 0 1), (1.5 1.333, 2 1.333, 2 0.666, 1.5 0.666, 1.5 1.333))') geom
  UNION ALL
  SELECT 2 id, ST_GeomFromText('POLYGON((1 1, 3.8 2, 4 0, 1 1))')
  UNION ALL
  SELECT 3 id, ST_GeomFromText('POLYGON((2 1, 4.6 2, 5 0, 2 1))')
  UNION ALL
  SELECT 4 id, ST_GeomFromText('POLYGON((3 1, 5.4 2, 6 0, 3 1))')
  UNION ALL
  SELECT 5 id, ST_GeomFromText('POLYGON((3 1, 5.4 2, 6 0, 3 1))')
)
SELECT a.id, ST_DifferenceAgg(a.geom, b.geom) geom
FROM overlappingtable a,
     overlappingtable b
WHERE a.id = b.id OR -- Make sure to pass at least once the polygon with itself
      ((ST_Contains(a.geom, b.geom) OR -- Select all the containing, contained and overlapping polygons
        ST_Contains(b.geom, a.geom) OR
        ST_Overlaps(a.geom, b.geom)) AND
       (ST_Area(a.geom) < ST_Area(b.geom) OR -- Make sure bigger polygons are removed from smaller ones
        (ST_Area(a.geom) = ST_Area(b.geom) AND -- If areas are equal, arbitrarily remove one from the other but in a determined order so it's not done twice.
         a.id < b.id)))
GROUP BY a.id
HAVING ST_Area(ST_DifferenceAgg(a.geom, b.geom)) > 0 AND NOT ST_IsEmpty(ST_DifferenceAgg(a.geom, b.geom));

Cela fusionnera les parties qui se chevauchent avec le plus grand polygone qui se chevauchent. Si vous souhaitez conserver la partie se chevauchant, regardez l'exemple de ST_splitAgg ().

Pierre Racine
la source