Враховуючи дірки / обмеження у створенні полігону Вороного в QGIS?


12

Я намагаюся створити багатокутники voronoi в QGIS, які б розглядали "дірки" в загальній області. Прикладом може бути:

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

Я фактично створив Voronois на цьому зображенні за допомогою QGIS за допомогою команди GRASS, потім використовуючи інструмент «Різниця» для створення дірок. В якості шару «Різниця» був використаний окремий формуляр багатокутника, який містить розрізи отворів. Прикладом програми може бути створення багатокутників навколо точок відбору проб, зібраних між структурами, які слід виключити з аналізу.

Тут виникають дві проблеми:

  1. Здається, функція "різниця" не працює на 100% належним чином, деякі межі багатокутника поширюються на "дірки". Це можна виправити, знайшовши рядок у таблиці атрибутів, у якому немає ідентифікаційного номера полігона (або ідентифікатора "0").

  2. Цей тип фактичного «пробивання дірок» може призвести до переривчастого багатокутника, як показано червоною стрілкою на зображенні.

Моє запитання: чи є інструмент або плагін Voronoi, який може розглядати наявність «дірок» в центрі домену, як поетапний процес, а також усунути генерацію розривних полігонів? Я передбачаю, що такий інструмент поширить межу полігона до найближчого перетину з іншою границею, якщо спочатку ці кордони не вдаряться до межі "дірки".


Це було б схоже на, але протилежне (я думаю) використання маски навколишнього середовища в ArcGIS . Це дозволить вам обмежити створені багатокутники в межах певної межі. Однак я не знаю жодного інструменту, який би використовував складні межі / отвори (хоча, можливо, в ArcGIS маска може бути такою складною - я не перевіряв її, і, можливо, спробую пізніше спробувати, якщо встигну).
Кріс Ш

Я перевірив теорію ArcGIS, і вона не буде працювати. Відповідно до пов'язаного питання, ви можете обмежити результати зовнішньою формою. Однак отвір, вирізане у формі, ігнорується отриманим полісом. Крім того, якщо в цьому отворі є також деякі точки, інструмент помиляється і не запускається. Я не можу пояснити ваше перше питання різницею, але друге, що спричиняє тріски, не зовсім несподіване - зрештою, ця область все одно буде виділена в ту саму точку, навіть якщо є отвір. Ви можете використовувати цей метод, а потім включити перемикачі до своїх сусідів методом очищення.
Кріс Ш

2
Ви могли б вирішити це, перейшовши на растр. За допомогою растрової маски евклідова відстань буде виходити з ваших точок, поки вона не потрапить або на клітинки, що виходять з іншої точки, або на ваш растр маски (ваш опис граничного шламу). Потім ви робите зональне очищення і векторизуєте результат, щоб отримати багатокутники.
Кріс Ш

1
Я би переконався, що геометрія voronoi є дійсною, запустивши v.clean і перевіримо геометрію. Нарешті, виконайте "Різниця", щоб створити отвори.
klewis

Що таке Вороний про ці дірки? Ви не хочете чітко пробивати отвори? Чому б не зробив жоден багатокутний шар?
mdsumner

Відповіді:


3

Це може бути можливо за допомогою растру. Спочатку конвертуйте свої точки та граничні багатокутники в растр високої роздільної здатності. Встановіть маску для своїх меж за допомогою r.mask. Потім запустіть r.grow.distanceу GRASS та використовуйте Value= output. Це дасть вам кожен піксель, який є найближчим моментом. Перетворіть це назад у векторні багатокутники. Можуть бути потрібні додаткові кроки, щоб позбутися косого полігонів.


2

Це, звичайно, можливо при растрах.

Цей скріншот, сподіваємось, чіткіше показує проблему. Частина B вороного ближче "як ворона летить" до початкового вороного центру, але це не враховує того факту, що ходити довше будівлі буде потрібно довше. Я розумію питання ОП в тому, що вороному потрібно врахувати цю додаткову відстань, щоб пройти навколо будівлі.

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

Мені подобається пропозиція від @Guillaume. Однак, коли я спробував це, у мене виникли проблеми r.grow.distanceз дотриманням маски (див. Нижче. Пульсації не повинні проходити крізь будівлі).

Мої знання про траву не такі сильні, як це могло б бути, тому, можливо, я роблю щось дурне. Однозначно, спочатку перевірте цю пропозицію, оскільки це буде набагато менше роботи, ніж моя ;-)

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

Крок 1 - Створіть поверхню витрат

Перший крок - створити поверхню витрат. Це потрібно зробити лише один раз.

  • створити редагований шар, отвори та інше.
  • додайте поле під назвою "одиниця", встановіть його на 1.
  • використовуючи полігон до растру на вашому "пробитому" векторному шарі (той, у якому є отвори), використовуючи поле "одиниця". Тепер у вас є шар "маска", де 1 - це вільний простір, а 0 - це будівництво.
  • використовуйте растровий калькулятор, щоб перетворити це на поверхню витрат. Я встановлю "на відкритому повітрі" 1 і "в приміщенні" на 9999. Це зробить переміщення по будівлях надзвичайно важким.

    (("маска @ 1" = 1) * 1) + (("маска @ 1" = 0) * 9999)

Ви можете отримати більш "органічні" результати, додавши трохи шуму на поверхню витрат (наприклад, використовуйте випадкове число від 1 до 3, а не просто 1 для зовнішніх пікселів.)

Крок 2. Створіть сукупні растрові витрати для кожного вороного центру

Тепер ми можемо запустити (для однієї вороної комірки одночасно) алгоритм GRASS відповідно r.cost.coordinatesдо нашого поверхневого шару витрат.

Для початку координати скористайтеся центром vornoi. Для кінцевої координати виберіть один з кутів вашої області. Я пропоную використовувати "Найт-тур", оскільки це дає більш плавні результати.

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

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

Не знаєте, як найкраще це автоматизувати. Можливо обробка пакетного режиму, або виконана в pyqgis.

Крок 3. Об'єднайте растри

Можливо, для цього знадобиться код. Алгоритм був би

create a raster 'A' to match the size of your cumulative cost images
fill raster 'A' with a suitably high number e.g. 9999
create an array of the same size as the raster.
for each cumulative cost raster number 1..N
    for each cell in image
        if cell < value in raster 'A'
            set value in raster 'A' to cell value
            set corresponding cell in array to cum. cost image number
write out array as a raster

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

Потім можна використовувати растр-до-багатокутник. Потім ви можете використовувати плагін Generalize для видалення артефактів ефекту "ступінь" з растру.

Вибачте за невизначеність на кроках 2 і 3 ... Я сподіваюся, що хтось зазіхає на більш елегантне рішення :)


1
Дякую, Стівен, у мене працює растерна растра GRASS, але я сподівався на більш елегантне рішення, як згадувалося в описі щедрості.
underdark

0

Примітка №1 : Я не зміг відтворити запропоновану проблему, оскільки інструмент « Різниця » працював для мене в декількох тестах, які я виконував (можливо, це було пов’язано з простою геометрією питання або через те, що інструмент було вдосконалено з моменту запитання запитав 1 рік тому).

Однак я пропоную вирішити в PyQGIS, щоб уникнути використання інструменту " Різниця" . Все базується на локальному перетині між двома вхідними шарами (див. Малюнок нижче):

  1. багатокутний векторний шар, що представляє багатокутники Вороного;
  2. багатокутний векторний шар, що представляє отвори / обмеження, які необхідно виключити з аналізу.

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

Примітка №2 : Оскільки я не хочу використовувати інструмент " Різниця" , я не в змозі уникнути створення "слайверів" (див. Далі), тому мені потрібно було запустити v.cleanінструмент для їх усунення. Крім того, як сказав @Chris W,

[...] але друге, що спричиняє заслінки, не зовсім несподіване - зрештою, ця площа все одно буде виділена до тієї ж точки, навіть якщо отвір є. Ви можете використовувати цей метод, а потім включити перемикачі до своїх сусідів методом очищення .

Після цих необхідних приміщень я розміщую свій код:

##Voronoi_Polygons=vector polygon
##Constraints=vector polygon
##Voronoi_Cleaned=output vector

from qgis.core import *

voronoi = processing.getObject(Voronoi_Polygons)
crs = voronoi.crs().toWkt()
ex = voronoi.extent()
extent = '%f,%f,%f,%f' % (ex.xMinimum(), ex.xMaximum(), ex.yMinimum(), ex.yMaximum())

constraints = processing.getObject(Constraints)

# Create the output layer
voronoi_mod = QgsVectorLayer('Polygon?crs='+ crs, 'voronoi' , 'memory')
prov = voronoi_mod.dataProvider()
fields = voronoi.pendingFields() # Fields from the input layer
prov.addAttributes(fields) # Add input layer fields to the outLayer
voronoi_mod.updateFields()

# Spatial index containing all the 'constraints'
index_builds = QgsSpatialIndex()
for feat in constraints.getFeatures():
    index_builds.insertFeature(feat)

final_geoms = {}
final_attrs = {}

for feat in voronoi.getFeatures():
    input_geom = feat.geometry()
    input_attrs = feat.attributes()
    final_geom = []
    multi_geom = input_geom.asPolygon()
    input_geoms = [] # edges of the input geometry
    for k in multi_geom:
        input_geoms.extend(k)
    final_geom.append(input_geoms)
    idsList = index_builds.intersects(input_geom.boundingBox())
    mid_geom = [] # edges of the holes/constraints
    if len(idsList) > 0:
        req = QgsFeatureRequest().setFilterFids(idsList)
        for ft in constraints.getFeatures(req):
            geom = ft.geometry()
            hole = []
            res = geom.intersection(input_geom)
            res_geom = res.asPolygon()
            for i in res_geom:
                hole.extend(i)
                mid_geom.append(hole)
        final_geom.extend(mid_geom)
    final_geoms[feat.id()] = final_geom
    final_attrs[feat.id()] = input_attrs

# Add the features to the output layer
outGeom = QgsFeature()
for key, value in final_geoms.iteritems():
    outGeom.setGeometry(QgsGeometry.fromPolygon(value))
    outGeom.setAttributes(final_attrs[key])
    prov.addFeatures([outGeom])

# Add 'voronoi_mod' to the Layers panel
QgsMapLayerRegistry.instance().addMapLayer(voronoi_mod)

# Run 'v.clean'
processing.runalg("grass7:v.clean",voronoi_mod, 2, 0.1, extent, -1, 0.0001, Voronoi_Cleaned, None)

# Remove 'voronoi_mod' to the Layers panel
QgsMapLayerRegistry.instance().removeMapLayer(voronoi_mod)

що призводить до цього результату:

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

Тільки для зрозумілості це буде результат без використання v.cleanінструменту:

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

Відмінність результату від @LeaningCactus полягає в тому, що на сьогоднішній день геометрії не порушені, і їх можна було "очистити" без помилок .


Зробіть прорізи довше, наприклад, прорізаючи всю карту, як річка, і ви побачите проблему. Додавання слайсів до сусідів створює багатокутники, які виглядають зовсім інакше, ніж правильна обмежена діаграма Вороного. Я спробував це.
underdark

Вибачте, я не розумію: ви знайшли помилку в результатах? Я перевіряв код лише для тих випадків, коли багатокутники були подібні до тих, які запропоновані у питанні.
mgri

На жаль, зараз не можна перевірити код, але чи могли ви показати результати, отримані зі зміною отворів, накреслених у i.stack.imgur.com/Jpfra.png ?
underdark

Якщо я поширюю обмеження на функцію праворуч, я отримую це . Натомість, якщо я прямо переміщую обмеження, я отримую це .
mgri

Маленький трикутник, на який вказує червона стрілка на моєму малюнку, - це питання. Його не повинно бути там, але це також у ваших результатах. Схоже, такий підхід вирішує проблему №1 питання, але залишає №2 невирішеною.
underdark
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.