Como criar linhas para visualizar as diferenças entre os recursos de polígono no PostGIS?


15

Eu tenho uma tabela PostGIS polygon_bcom alguns recursos de polígono. Há também uma tabela polygon_aque contém os mesmos polígonos, polygon_bmas com pequenas alterações. Agora, quero criar linhas para visualizar as diferenças entre os recursos de polígono.

insira a descrição da imagem aqui insira a descrição da imagem aqui insira a descrição da imagem aqui

Suponho que ST_ExteriorRinge ST_Differencefaça o trabalho, mas a cláusula WHERE parece ser bastante complicada.

CREATE VIEW line_difference AS SELECT
row_number() over() AS gid,
g.geom::geometry(LineString, yourSRID) AS geom
FROM 
    (SELECT
    (ST_Dump(COALESCE(ST_Difference(ST_ExteriorRing(polygon_a.geom), ST_ExteriorRing(polygon_b.geom))))).geom AS geom
    FROM polygon_a, polygon_b
    WHERE 
    -- ?
    ) AS g;

Alguém pode me ajudar?

EDIT 1

Conforme publicado por 'tilt', tentei, ST_Overlaps(polygon_a.geom, polygon_b.geom) AND NOT ST_Touches(polygon_a.geom, polygon_b.geom)mas o resultado não é o esperado.

CREATE VIEW line_difference AS SELECT
row_number() over() AS gid,
g.geom::geometry(LineString, your_SRID) AS geom
FROM 
    (SELECT
    (ST_Dump(COALESCE(ST_Difference(ST_ExteriorRing(polygon_a.geom), ST_ExteriorRing(polygon_b.geom))))).geom AS geom
    FROM polygon_a, polygon_b
    WHERE 
    ST_Overlaps(polygon_a.geom, polygon_b.geom) AND NOT ST_Touches(polygon_a.geom, polygon_b.geom))
     AS g;

insira a descrição da imagem aqui

EDIT 2

workupload.com/file/J0WBvRBb (exemplo de conjunto de dados)


Tentei transformar os polígonos em multilinhas antes de usar ST_Difference, mas os resultados ainda são estranhos.

CREATE VIEW multiline_a AS SELECT
row_number() over() as gid,
ST_Union(ST_ExteriorRIng(polygon_a.geom))::geometry(multilinestring, 4326) AS geom
FROM
polygon_a;

CREATE VIEW multiline_b AS SELECT
row_number() over() as gid,
ST_Union(ST_ExteriorRIng(polygon_b.geom))::geometry(multilinestring, 4326) AS geom
FROM
polygon_b;

CREATE VIEW line_difference AS SELECT
row_number() over() as gid,
g.geom
FROM
    (SELECT
    (ST_Dump(COALESCE(ST_Difference(multiline_a.geom, multiline_b.geom)))).geom::geometry(linestring, 4326) AS geom
    FROM
    multiline_a, multiline_b)
As g;

insira a descrição da imagem aqui


Parece mais uma pergunta de topologia. Você deseja identificar segmentos que não são cobertos pela outra camada. Eu não trabalhei muito com a topologia do PostGIS e não posso dar uma resposta direta, mas sugiro que você analise mais isso.
— Thomas

Interessante, você tem um exemplo de conjunto de dados para download?
— huckfinn

Respostas:


10

Aqui estão alguns novos truques, usando:

  • EXCEPTpara remover geometrias das tabelas iguais, para que possamos focar apenas nas geometrias exclusivas de cada tabela ( A_onlye B_only).
  • ST_Snap para obter acenos exatos para operadores de sobreposição.
  • Use o ST_SymDifferenceoperador de sobreposição para encontrar a diferença simétrica entre os dois conjuntos de geometria para mostrar as diferenças. Atualização :ST_Difference mostra o mesmo resultado para este exemplo. Você pode tentar qualquer uma das funções para ver o que elas recebem.

Isso deve obter o que você espera:

-- CREATE OR REPLACE VIEW polygon_SymDifference AS
SELECT row_number() OVER () rn, *
FROM (
  SELECT (ST_Dump(ST_SymDifference(ST_Snap(A, B, tol), ST_Snap(B, A, tol)))).*
  FROM (
    SELECT ST_Union(DISTINCT A_only.geom) A, ST_Union(DISTINCT B_only.geom) B, 1e-5 tol
    FROM (
      SELECT ST_Boundary(geom) geom FROM polygon_a
      EXCEPT SELECT ST_Boundary(geom) geom FROM polygon_b
    ) A_only,
    (
      SELECT ST_Boundary(geom) geom FROM polygon_b
      EXCEPT SELECT ST_Boundary(geom) geom FROM polygon_a
    ) B_only
  ) s
) s;

 rn |                                        geom
----+-------------------------------------------------------------------------------------
  1 | LINESTRING(206.234028204842 -92.0360704110685,219.846021625456 -92.5340701703592)
  2 | LINESTRING(18.556700448873 -36.4496098325257,44.44438533894 -40.5104231486146)
  3 | LINESTRING(-131.974995802602 -38.6145334122719,-114.067738329597 -39.0215165366584)
(3 rows)

três linhas


Para descompactar um pouco mais esta resposta, o primeiro passo com ST_Boundary obtém o limite de cada polígono, e não apenas o exterior. Por exemplo, se houvesse buracos, eles seriam rastreados pelo limite.

A EXCEPTcláusula é usada para remover geometrias de A que fazem parte de B e linhas de B que fazem parte de A. Isso reduz o número de linhas que fazem parte apenas de A e apenas parte de B. Por exemplo, para obter A_only:

SELECT ST_Boundary(geom) geom FROM polygon_a
EXCEPT SELECT ST_Boundary(geom) geom FROM polygon_b

Aqui estão as 6 linhas de A_only e 3 linhas de B_only: A_only B_only

Próximo, ST_Union(DISTINCT A_only.geom) é usado para combinar o trabalho de linha em uma única geometria, geralmente uma MultiLineString.

ST_Snap é usado para ajustar nós de uma geometria para outra. Por exemplo ST_Snap(A, B, tol), pegue a geometria A e adicione mais nós da geometria B ou mova-os para a geometria B, se estiverem dentro da toldistância. Provavelmente, existem várias maneiras de usar essas funções, mas a idéia é obter coordenadas de cada geometria que sejam exatas entre si. Portanto, as duas geometrias após o snap são assim:

Um estalou B estalou

E para mostrar diferenças, você pode optar por usar ST_SymDifferenceou ST_Difference. Ambos mostram o mesmo resultado para este exemplo.


Boa resposta. Gostaria de saber o que você usou para visualizar os resultados de suas consultas intermediárias. Não parecia imediatamente com qgis, e eu talvez seja algo que renderize um pouco mais rápido?
— RoperMaps

1
Eu uso o JTS Testbuilder para visualizar e processar geometrias. É um mecanismo de geometria relacionado ao GEOS e Shapely, mas tem uma GUI baseada em Java.
— Mike T

Existe uma maneira de ignorar / pular problemas de 'Interseção sem assentimento entre LINESTRING'? Todos os polígonos de entrada parecem estar bem (verificado com o verificador de geometria QGIS).
— 44520 Eclipse_by_the_moon #

1
'ST_Boundary (ST_SnapToGrid (geom, 0,001))' em vez de 'ST_Boundary (geom)' resolve o problema.
— eclipsed_by_the_moon

6

Eu acho que é um pouco complicado, por causa dos diferentes conjuntos de nós dos dois polígonos (polígono verde A, segmentos diferentes vermelhos do polímero B). A comparação dos segmentos de ambos os polígonos fornece uma pista de quais segmentos do polígono B serão modificados.

Polígono de nós A

poli a

Nós do polígono B de segmentos "diferentes"

seg diff

Infelizmente, isso mostra apenas a diferença na estrutura do segmento, mas espero que seja um ponto de partida e funcione assim:

Após um processo de download e descompactação, importei o conjunto de dados usando o PostgrSQL 9.46, o PostGIS 2.1 no Debian Linux Jessie com os comandos.

$ createdb gis-se
$ psql gis-se < /usr/share/postgis-2.1/postgis.sql
$ psql gis-se < /usr/share/postgis-2.1/spatial_ref_sys.sql
$ shp2pgsql -S polygon_a | psql gis-se
$ shp2pgsql -S polygon_b | psql gis-se

Supondo que os segmentos do polígono A não estejam em B e vice-vera, tento criar a diferença entre os segmentos de ambos os conjuntos de polígonos, negligenciando a associação do segmento aos polígonos de cada grupo (A ou B). Por razões didáticas, formulo o material SQL em várias visualizações.

Correspondendo a este post do GIS-SE , decomponho os dois polígonos em tabelas de segmentos segments_aesegments_b

-- Segments of the polygon A
CREATE VIEW segments_a AS SELECT sp, ep
FROM
   -- extract the endpoints for every 2-point line segment for each linestring
   (SELECT
      ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) as sp,
      ST_PointN(geom, generate_series(2, ST_NPoints(geom)  )) as ep
    FROM
    -- extract the individual linestrings
     (SELECT (ST_Dump(ST_Boundary(geom))).geom
      FROM polygon_a
     ) AS linestrings
    -- be sure that nothing is scrambled
    ORDER BY sp, ep
) AS segments;

O polígono da tabela de segmentos A:

SELECT 
  st_astext(sp) AS sp, 
  st_astext(ep) AS ep 
FROM segments_a 
LIMIT 3;
                    sp                     |                 ep
-------------------------------------------+--------------------------------------------
POINT(-292.268907321861 95.0342877387557)  | POINT(-287.118411917425 99.4165242769195)
POINT(-287.118411917425 99.4165242769195)  | POINT(-264.62129248575 93.2470010145007)
POINT(-277.459563916327 -44.5629543976138) | POINT(-292.268907321861 95.03428773875

O mesmo procedimento foi aplicado ao polígono B.

-- Segments of the polygon B
CREATE VIEW segments_b AS SELECT sp, ep
FROM
   -- extract the endpoints for every 2-point line segment for each linestring
   (SELECT
      ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) as sp,
      ST_PointN(geom, generate_series(2, ST_NPoints(geom)  )) as ep
    FROM
    -- extract the individual linestrings
     (SELECT (ST_Dump(ST_Boundary(geom))).geom
      FROM polygon_b
     ) AS linestrings
    -- be sure that nothing is scrambled
    ORDER BY sp, ep
) AS segments;

O polígono da tabela de segmentos B

SELECT
  st_astext(sp) AS sp, 
  st_astext(ep) AS ep 
FROM segments_b 
LIMIT 3;
                    sp                     |                    ep
-------------------------------------------+-------------------------------------------
POINT(-292.268907321861 95.0342877387557)  | POINT(-287.118411917425 99.4165242769195)
POINT(-287.118411917425 99.4165242769195)  | POINT(-264.62129248575 93.2470010145007)
POINT(-277.459563916327 -44.5629543976138) | POINT(-292.268907321861 95.0342877387557)
...                        

Eu posso criar uma exibição de tabela de diferença chamada segments_diff_{a,b}. A diferença é dada pela não ocorrência de pontos de início ou de fim classificados no conjunto de segmentos A e B.

CREATE VIEW segments_diff_a AS
SELECT st_makeline(b.sp, b.ep) as geom
FROM segments_b as b
LEFT JOIN segments_a as a ON (a.sp=b.sp and a.ep = b.ep)
-- filter segments without corresponding stuff in polygon A
WHERE a.sp IS NULL;

segs diff b

E o material complementar:

CREATE VIEW segments_diff_b AS
SELECT st_makeline(a.sp, a.ep) as geom
FROM segments_a as a
LEFT JOIN segments_b as b ON (a.sp=b.sp and a.ep = b.ep)
-- filter segments without corresponding stuff in polygon B
WHERE b.sp IS NULL;

segs diff a

Conclusão: Para obter um resultado adequado para os pequenos pequenos segmentos marcados com a seta vermelha, os dois polígonos devem ter a mesma estrutura do nó e uma etapa de interseção no nível do nó (é necessário inserir vértices do polígono A em B). A interseção pode ser feita por:

CREATE VIEW segments_bi AS 
SELECT distinct sp, ep
FROM (
 SELECT
      ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) as sp,
      ST_PointN(geom, generate_series(2, ST_NPoints(geom)  )) as ep
 FROM (
   SELECT st_difference(b.seg, a.seg) as geom FROM 
      segments_diff_a as a, segments_diff_b as b 
      WHERE st_intersects(a.seg, b.seg)
    ) as cut
  ) as segments
  WHERE sp IS NOT NULL AND ep IS NOT NULL
ORDER BY sp, ep;

Mas com resultados estranhos ...

versão cortada


Obrigado por seus esforços. Bem, os resultados são estranhos. Gostaria de saber se ST_HausdorffDistance () pode ajudar a responder à pergunta: gis.stackexchange.com/questions/180593/…
— Mar Lunar

Hum, st_haudorffdistance fornece uma medida de similaridade, não os segmentos desejados (setas vermelhas apontando para).
— Huckfinn 29/02

É apenas uma ideia, ST_HausdorffDistance pode ser usado para comparar as geometrias de ambas as tabelas. Os polígonos não são iguais espacialmente? Tenho que criar linhas. Eu simplesmente não sei como fazer isso.
— Mar Lunar

Parece ser uma questão de precisão e topologia ( gis.stackexchange.com/a/182838/26213 e webhelp.esri.com/arcgisdesktop/9.2/… )
— huckfinn

1

Observando o exemplo, a alteração implica que os recursos da nova tabela que foram alterados sempre estarão sobrepondo-os à tabela antiga. Portanto, você seria feito com

ST_Overlaps (geoma, geomb) AND! ST_Touches (geoma, geomb)

A negação dos toques ocorre porque os recursos também se sobrepõem se apenas suas bordas compartilharem os mesmos locais de vértices.

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.