Как создать линии для визуализации различий между полигонами в PostGIS?

15

У меня есть таблица PostGIS polygon_bс некоторыми функциями многоугольника. Также есть таблица, polygon_aкоторая содержит те же полигоны, что и polygon_bс небольшими изменениями. Теперь я хочу создать линии, чтобы визуализировать различия между полигонами.

введите описание изображения здесь введите описание изображения здесь введите описание изображения здесь

Я предполагаю, что так ST_ExteriorRingи ST_Differenceсделаю свою работу, но предложение WHERE кажется довольно сложным.

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;

Может кто-нибудь мне помочь?

РЕДАКТИРОВАТЬ 1

Как и в «Tilt» я пытался, ST_Overlaps(polygon_a.geom, polygon_b.geom) AND NOT ST_Touches(polygon_a.geom, polygon_b.geom)но результат не так, как ожидалось.

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;

введите описание изображения здесь

РЕДАКТИРОВАТЬ 2

workupload.com/file/J0WBvRBb (пример набора данных)


Я пытался превратить полигоны в мультилинии перед использованием ST_Difference, но результаты все еще странные.

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;

введите описание изображения здесь

Лунное море
источник
Больше похоже на вопрос топологии. Вы хотите идентифицировать сегменты, которые не покрыты другим слоем. Я не очень много работал с топологией PostGIS и не могу дать вам прямой ответ, но я советую вам больше в этом разобраться.
Томас,
Интересно, у вас есть пример набора данных для скачивания?
huckfinn
workupload.com/file/J0WBvRBb
Лунное море

Ответы:

10

Вот несколько новых приемов, использующих:

  • EXCEPTудалить из любой таблицы одинаковые геометрии, поэтому мы можем сосредоточиться только на геометриях, уникальных для каждой таблицы ( A_onlyи B_only).
  • ST_Snap чтобы получить точное кодирование для операторов наложения.
  • Используйте ST_SymDifferenceоператор наложения, чтобы найти симметричное различие между двумя наборами геометрии, чтобы показать различия. Обновление : ST_Differenceпоказывает тот же результат для этого примера. Вы можете попробовать любую функцию, чтобы увидеть, что они получают.

Это должно получить то, что вы ожидаете:

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

три линии


Чтобы распаковать этот ответ немного больше, первый шаг с ST_Boundaryполучает границу каждого многоугольника, а не только внешность. Например, если бы были дыры, они были бы прослежены границей.

EXCEPTПункт используется для удаления геометрии из A , которые являются частью B и строки из B , которые являются частью A. Это уменьшает количество строк , которые являются частью только A и часть B только. Например, чтобы получить A_only:

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

Вот 6 строк A_only и 3 строки B_only: A_only B_only

Далее, ST_Union(DISTINCT A_only.geom)используется для объединения линий в единую геометрию, обычно MultiLineString.

ST_Snap используется для привязки узлов из одной геометрии в другую. Например ST_Snap(A, B, tol), возьмем геометрию A и добавим больше узлов из геометрии B или переместим их в геометрию B, если они находятся на tolрасстоянии. Возможно, есть несколько способов использовать эти функции, но идея состоит в том, чтобы получить координаты от каждой геометрии, которые являются точными друг к другу. Таким образом, две геометрии после привязки выглядят так:

Отрезал B огрызнулся

И показать различия, вы можете использовать либо ST_SymDifferenceили ST_Difference. Они оба показывают одинаковый результат для этого примера.

Майк Т
источник
Хороший ответ. Мне было интересно, что вы использовали для визуализации результатов ваших промежуточных запросов. Это не сразу выглядело как qgis, и я, возможно, это то, что рендерится немного быстрее?
RoperMaps
1
Я использую JTS Testbuilder для просмотра и обработки геометрии. Это связанный с Geos и Shapely геометрический движок, но с графическим интерфейсом на основе Java.
Майк Т
Есть ли способ игнорировать / пропустить проблемы «неузловое пересечение между LINESTRING»? Кажется, что все входные полигоны в порядке (проверено с помощью проверки геометрии QGIS).
eclipsed_by_the_moon
1
«ST_Boundary (ST_SnapToGrid (geom, 0.001))» вместо «ST_Boundary (geom)» решает проблему.
eclipsed_by_the_moon
6

Я думаю, что это немного сложно, из-за разных наборов узлов обоих полигонов (зеленый многоугольник A, красный разные сегменты многоугольника B). Сравнение сегментов обоих полигонов дает представление о том, какие сегменты полигона B будут изменены.

Узлы полигона А

поли

Узлы "разных" сегментов многоугольника B

seg diff

К сожалению, это показывает только разницу в структуре сегмента, но я надеюсь, что это отправная точка, и она работает так:

После загрузки и распаковки я импортировал набор данных, используя PostgrSQL 9.46, PostGIS 2.1 под Debian Linux Jessie с командами.

$ 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

Предполагая, что сегменты полигона A не находятся в B и наоборот, я пытаюсь построить разницу между сегментами обоих наборов полигонов, пренебрегая принадлежностью сегмента к полигонам в каждой группе (A или B). По дидактическим причинам я формулирую SQL в нескольких видах.

В соответствии с этим постом GIS-SE я разлагаю оба полигона на таблицы сегментов segments_aиsegments_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;

Сегмент таблицы многоугольника А:

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

Та же самая процедура была применена к многоугольнику 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;

Сегмент настольный многоугольник 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)
...                        

Я могу построить представление таблицы различий с именем segments_diff_{a,b}. Разница определяется отсутствием отсортированных начальных или конечных точек в сегментах A и 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

И дополнительные вещи:

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 в B). Пересечение может быть сделано:

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;

Но со странными результатами ...

урезанная версия

huckfinn
источник
Спасибо за ваши старания. Ну, результаты странные. Мне просто интересно, может ли ST_HausdorffDistance () помочь ответить на вопрос: gis.stackexchange.com/questions/180593/…
Лунное море
Хм, st_haudorffdistance дает вам меру сходства, а не искомые сегменты (красные стрелки указывают на).
huckfinn
Это просто идея, ST_HausdorffDistance можно использовать для сравнения геометрий обеих таблиц. Если бы полигоны не были пространственно равны, я должен создать линии. Я просто не знаю, как это сделать.
Лунное море
Кажется, это вопрос точности и топологии ( gis.stackexchange.com/a/182838/26213 и webhelp.esri.com/arcgisdesktop/9.2/… )
huckfinn
1

Глядя на пример, изменение подразумевает, что объекты из новой таблицы, которые были изменены, всегда будут перекрывать объекты из старой таблицы. Поэтому вы бы сделали с

ST_Overlaps (геома, геомба) И! ST_Touches (геома, геомба)

Отрицание касаний связано с тем, что элементы также перекрываются, если только их границы имеют одинаковые местоположения вершин.

наклон
источник