Як створити лінії для візуалізації відмінностей між функціями полігону в 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

Як опублікував "нахил", я намагався, 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;

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

EDIT 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

Відповіді:


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, а лише до частини 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відстані. Можливо, існує кілька способів використання цих функцій, але ідея полягає в тому, щоб отримати координати з кожної геометрії, точні одна одній. Отже, дві геометрії після оснащення виглядають так:

Сніп Б відрізався

І щоб показати відмінності, ви можете скористатися ST_SymDifferenceабо ST_Difference. Вони обидва показують однаковий результат для цього прикладу.


Гарна відповідь. Мені було цікаво, що ви використовували для візуалізації результатів своїх посередницьких запитів. Це не відразу виглядало як qgis, і я, можливо, це щось, що робить трохи швидше?
RoperMaps

1
Я використовую JTS Testbuilder для перегляду та обробки геометрії. Це пов'язаний двигун геометрії з GEOS та Shapely, але має графічний інтерфейс на основі Java.
Майк Т

Чи є спосіб ігнорувати / пропускати проблеми "Незважене перехрещення між ЛІНІСТРІНГАМИ"? Усі вхідні багатокутники здаються нормальними (перевіряється засобом перевірки геометрії QGIS).
eclipsed_by_the_moon

1
"ST_Boundary (ST_SnapToGrid (geom, 0,001))" замість "ST_Boundary (geom)" вирішує проблему.
eclipsed_by_the_moon

6

Я думаю, що це трохи хитро, через різні набори вузлів обох полігонів (зелений багатокутник А, червоний різні відрізки багатокутника В). Порівнюючи відрізки обох багатокутників, дає зрозуміти, які сегменти багатокутника B будуть модифіковані.

Вузли багатокутника А

полі а

Вузли "різних" відрізків багатокутника B

сегмент розл

На жаль, це показує лише різницю в структурі сегмента, але я сподіваюся, що це відправна точка, і вона працює так:

Після процесу завантаження та розпакування я імпортував набір даних за допомогою 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;

Таблиця багатокутника 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

Ця ж процедура була застосована до багатокутника В.

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

Висновок: для отримання належного результату для невеликих маленьких сегментів, які ви позначили червоною стрілкою, обидва полігони повинні мати однакову структуру вузла та необхідний крок перетину на рівні вузла (вставлення вершин багатокутника 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;

Але з дивними результатами ...

скорочена версія


Дякуємо за ваші зусилля. Ну, результати дивні. Мені просто цікаво, чи може 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 (geoma, geomb) AND! ST_Touches (geoma, geomb)

Заперечення дотиків пояснюється тим, що функції також перекриваються, якщо лише їх межі мають однакові місця розташування вершин.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.