Vous effectuez une exploration / superposition de polygones dans PostGIS?

8

Je suis confronté à un défi avec PostGIS que je n'arrive pas à comprendre. Je sais que je peux résoudre ce problème en utilisant un langage de programmation (et c'est mon plan de sauvegarde), mais j'aime vraiment résoudre ce problème dans PostGIS. J'ai essayé de chercher, mais je n'ai pas trouvé de réponses qui correspondent à mon problème, c'est peut-être parce que je ne suis pas sûr de mes termes de recherche, alors s'il vous plaît excusez-le et dirigez-moi dans la bonne direction car il y a bien une réponse.

Mon problème est le suivant:

  • J'ai une table avec des polygones mixtes / multi-polygones
  • Chaque (multi) polygone a un attribut qui le classe (priorité)
  • Chaque polygone a également une valeur que j'aimerais connaître
  • J'ai une zone de recherche (polygone)
  • Pour ma zone de requête, je veux trouver la zone couverte par chaque valeur de polygone

Exemple:

Disons que j'ai les trois polygones représentés en rouge, vert et indigo ici: Exemple

Et que le petit rectangle bleu est mon polygone de requête

De plus, les attributs sont

geom   | rank | value
-------|------|----  
red    |  3   | 0.1
green  |  1   | 0.2
indigo |  2   | 0.2

Ce que je veux, c'est sélectionner ces géométries, de sorte que le plus haut rang (vert) remplisse toute la zone qu'il peut (c'est-à-dire l'intersection entre mon géom de requête et ce géom), puis le plus haut suivant (indigo) remplit l'intersection entre le géom de requête et le geom MOINS le déjà couvert) etc.

Quelque chose comme ça: entrez la description de l'image ici

J'ai trouvé cette question: utiliser ST_Difference pour supprimer les fonctionnalités qui se chevauchent? mais il ne semble pas faire ce que je veux.

Je peux moi-même comprendre comment calculer des zones et autres, donc une requête qui me donne les trois géométries décrites dans la deuxième image est très bien!

Informations supplémentaires: - Ce n'est pas un grand tableau (~ 2000 lignes) - il peut y avoir zéro ou plusieurs chevauchements (pas seulement trois) - il peut ne pas y avoir de polygones dans ma zone de requête (ou seulement dans certaines parties) - i ' m exécuter postgis 2.3 sur postgres 9.6.6

Ma solution de secours consiste à faire une requête comme celle-ci:

SELECT 
ST_Intersection(geom, querygeom) as intersection, rank, value
FROM mytable
WHERE ST_Intersects(geom, querygeom)
ORDER by rank asc

Et puis "couper" itérativement des parties des géométries dans le code. Mais, comme je l'ai dit, j'aimerais vraiment le faire dans PostGIS

atlefren
la source
2
ne peut pas vous donner de réponse pour le moment, mais si vous êtes prêt à WITH RECURSIVE ...
essayer
1
oh et vérifiez cela
geozelot
Merci! J'essaierai cela demain si personne d'autre ne se sent obligé de fournir une solution complète.
atlefren le

Réponses:

7

Je pense que cela fonctionne.

C'est une fonction de fenêtrage, obtenant la différence entre l'intersection de chaque intersection de géométries avec la zone de requête et l'union des géométries précédentes.

La fusion est nécessaire car l'union des géométries précédentes pour la première géométrie est nulle, ce qui donne un résultat nul, au lieu de ce qui est souhaité.

WITH a(geom, rank, val) AS
(
    VALUES
    ('POLYGON((1 1, 1 5, 5 5, 5 1, 1 1))'::geometry,3,0.1),
    ('POLYGON((2 3, 2 8, 4 8, 5 3,2 3))'::geometry,1,0.2),
    ('POLYGON((4 4, 4 6, 6 6, 6 4,4 4))'::geometry,2,0.2)
)
,q AS
(
    SELECT 'POLYGON((3 3, 3 4.5, 12 4.5,12 3, 3 3))'::geometry geom
) 
SELECT 
  ST_Difference(
    ST_Intersection(a.geom, q.geom), 
    COALESCE(ST_Union(a.geom) 
           OVER (ORDER BY rank ROWS BETWEEN UNBOUNDED PRECEDING and 1 PRECEDING),
       'POLYGON EMPTY'::geometry)
  ) geom 
FROM a,q
WHERE ST_Intersects(a.geom, q.geom);

Je ne sais pas cependant comment cela fonctionne. Mais puisque ST_Union et ST_Intersection sont marqués comme immuables, ce n'est peut-être pas si mal.

Nicklas Avén
la source
Cela a fonctionné comme un charme! Il suffit d'envelopper votre requête dans une autre requête afin de supprimer les collections de géométrie vides
atlefren
5

Un peu d'une approche différente à cela. Il y a une mise en garde que je ne sais pas comment cela évoluera en termes de performances, mais sur une table indexée, cela devrait être correct. Il fonctionne à peu près comme la requête de Nicklas (un peu plus lent?), Mais la mesure sur un si petit échantillon est lourde.

Il semble beaucoup plus laid que la requête de Nicklas, mais il évite la récursivité dans la requête.

WITH 
    a(geom, rank, val) AS
    (
        VALUES
        ('POLYGON((1 1, 1 5, 5 5, 5 1, 1 1))'::geometry,3,0.1),
        ('POLYGON((2 3, 2 8, 4 8, 5 3, 2 3))'::geometry,1,0.2),
        ('POLYGON((4 4, 4 6, 6 6, 6 4, 4 4))'::geometry,2,0.2)
    ),
    polygonized AS (
        -- get the basic building block polygons
        SELECT (ST_Dump(         -- 5. Dump out the polygons
            ST_Polygonize(line)) -- 4. Polygonise the linework
            ).geom AS mypoly
        FROM (
            SELECT 
                ST_Node(                  -- 3. Node lines on intersection
                    ST_Union(             -- 2. Union them for noding
                        ST_Boundary(geom) -- 1. Change to linestrings
                    ) 
                ) 
                AS line
            FROM a
        ) line
    ),
    overlayed AS ( 
        -- Overlay with original polygons and get minimum rank.
        -- Union and dissolve interior boundaries for like ranks.
        SELECT (ST_Dump(ST_UnaryUnion(geom))).geom, rank 
        FROM ( 
            -- union up the polygons by rank, unary union doesn't count as an aggregate function?
            SELECT ST_Union(mypoly) geom, rank 
            FROM ( 
                -- get the minimum rank for each of the polygons
                SELECT p.mypoly, min(rank) rank
                FROM polygonized p 
                    INNER JOIN a ON ST_Contains(a.geom,ST_PointOnSurface(p.mypoly))
                GROUP BY p.mypoly
                ) g
            GROUP BY rank
            ) r
    )
-- get the intersection of the query area with the overlayed polygons
SELECT ST_Intersection(o.geom,'POLYGON((3 3, 3 4.5, 12 4.5,12 3, 3 3))'::Geometry), rank
FROM overlayed o
WHERE ST_Intersects(o.geom,'POLYGON((3 3, 3 4.5, 12 4.5,12 3, 3 3))'::Geometry) and
    -- Intersection can do funky things
    GeometryType(ST_Intersection(o.geom,'POLYGON((3 3, 3 4.5, 12 4.5,12 3, 3 3))'::Geometry)) like '%POLYGON';
MickyT
la source
1

Depuis que je bavarde, WITH RECURSIVEje vais ajouter une réponse rapide et sale en l'utilisant.

Cela fonctionne à peu près aussi bien que la solution de @ NicklasAvén sur trois Polygones, impossible à tester lors d'une mise à l'échelle.
Dans l'état actuel des deux solutions, celle-ci présente un petit avantage par rapport à l'autre: si, par exemple, le polygone avec rang = 2 est contenu par celui de rang = 1 , les ...WHERE GeometryType = 'POLYGON'filtres disparaissent alors qu'autrement il y aura un GEOMETRYCOLLECTION EMPTY(j'ai changé la géométrie du polygone respectif dans ma solution en conséquence pour donner un exemple; cela est également vrai pour d'autres cas où aucune intersection avec la différence n'est trouvée). Cependant, cela est facilement inclus dans les autres solutions et pourrait même ne pas être préoccupant.

WITH RECURSIVE
    a(geom, rank, val) AS (
        VALUES
           ('POLYGON((1 1, 1 5, 5 5, 5 1, 1 1))'::geometry,3,0.1),
           ('POLYGON((2 3, 2 8, 4 8, 5 3,2 3))'::geometry,1,0.2),
           ('POLYGON((2.1 3.1, 2.1 7.9, 3.9 7.9, 4.9 3.1,2.1 3.1))'::geometry,2,0.2)
    ),
    q AS (
        SELECT 'POLYGON((3 3, 3 4.5, 12 4.5,12 3, 3 3))'::geometry geom
    ),
    src AS (
        SELECT ROW_NUMBER() OVER(ORDER BY rank) AS rn,
               ST_Intersection(q.geom, a.geom) AS geom,
               rank,
               val
        FROM a
        JOIN q
           ON ST_Intersects(a.geom, q.geom)
    ),
    res AS (
        SELECT s.geom AS its,
               ST_Difference(q.geom, s.geom) AS dif,
               s.rank,
               s.val,
               2 AS icr
        FROM src AS s,
             q
        WHERE s.rn = 1
        UNION ALL
        SELECT ST_Intersection(s.geom, r.dif) AS its,
               ST_Difference(r.dif, s.geom) AS dif,
               s.rank,
               s.val,
               icr + 1 AS icr
        FROM src AS s,
             res AS r
        WHERE s.rank = r.icr
    )

SELECT its AS geom,
       rank,
       val
FROM res
WHERE GeometryType(its) = 'POLYGON'
geozelot
la source