在QGIS中在Voronoi多边形创建中考虑孔/约束吗?


12

我正在尝试在QGIS中创建voronoi多边形,该多边形将在一般范围内考虑“孔”。一个例子是:

在此处输入图片说明

实际上,我是通过GRASS命令使用QGIS在此图像中创建Voronois的,然后使用“差异”工具创建了孔。包含孔的范围的单独的多边形shapefile用作“差异”层。一个示例应用程序是在应从分析中排除的结构之间收集的采样点周围创建多边形。

这里出现两个问题:

  1. “差异”功能似乎无法100%正常工作,某些多边形边界延伸到“孔”中。可以通过在属性表中找到没有多边形ID号(或ID为“ 0”)的行来解决此问题。

  2. 这种类型的事后“打孔”可能会导致不连续的多边形,如图像中的红色箭头所示。

我的问题是:是否有一个Voronoi工具或插件可以一步一步地考虑在域中心出现“孔”,并且还消除了不连续多边形的生成?我设想,这样的工具将把多边形边界扩展到与另一个边界最近的相交点,除非其他边界先向“孔”边界猛冲。


这与在ArcGIS中使用环境遮罩类似(但我认为相反)。这样一来,您就可以将创建的多边形限制在特定的边界内。但是我不知道有什么工具可以利用复杂的边界/孔(尽管也许在ArcGIS中,遮罩可能是如此复杂-我尚未对其进行测试,如果有时间的话可以稍后再尝试)。
克里斯·W

我已经测试过ArcGIS理论,但无法使用。根据链接的问题,您可以将结果约束为外部形状。但是,所形成的多边形将忽略在形状上切割的孔。此外,如果该孔中也有一些点,则该工具将出错并无法运行。我无法用差异来解释您的第一个问题,但是第二个导致碎片的情况并不是完全意外的-毕竟,即使存在孔,该区域仍将分配给同一点。您可以使用该方法,然后使用清理方法将这些条合并到它们的邻居中。
克里斯W

2
您可以通过栅格化解决此问题。使用栅格蒙版时,欧几里得距离会从您的点开始,直到碰到从另一个点出来的像元或您的蒙版栅格(您的边界大满贯描述)。然后,您进行一些区域清理并将结果向量化以获得多边形。
克里斯·W

1
我可以通过运行v.clean来确保voronoi Geometry有效,然后检查geometry。最后,执行差分创建孔。
klewis

Voronoi对这些漏洞有什么看法?您不是要干净地打孔吗?为什么没有任何多边形图层呢?
mdsumner '16

Answers:


3

使用栅格可能是可能的。首先将点和边界多边形转换为高分辨率栅格。使用为您的边界设置遮罩r.mask。然后,r.grow.distance在GRASS中运行并使用Value= output。这将为您提供每个像素,这是最接近的点。将其转换回矢量多边形。可能需要额外的步骤才能消除条形多边形。


2

对于栅格,这当然是可能的。

该屏幕快照有望更清楚地显示问题。voronoi的B部分“随着乌鸦飞翔”而接近原始的voronoi中心,但这并未考虑到在建筑物周围走动会花费更长的时间这一事实。我对OP的问题的理解是,voronoi需要考虑到在建筑物周围走动的额外距离。

在此处输入图片说明

我喜欢@Guillaume的建议。但是,当我尝试使用它时,我在r.grow.distance兑现口罩方面遇到了问题(请参见下文。波纹不应穿过建筑物)。

我对Grass的了解不如可能强,所以也许我在做一些愚蠢的事情。绝对可以,先检查一下该建议,因为它比我的建议要少得多;-)

在此处输入图片说明

第1步-创建成本面

第一步是创建成本表面。这仅需要执行一次。

  • 创建可编辑的图层,孔和所有图层。
  • 添加一个名为“ unit”的字段,将其设置为1。
  • 使用“单位”字段在“打孔”矢量层(带有孔的层)上使用“多边形到栅格”。现在,您有了一个“蒙版”层,其中1是自由空间,0是建筑物。
  • 使用栅格计算器将其转换为成本面。我将“室外”设置为1,将“室内”设置为9999。这将使移动建筑物变得异常困难。

    ((“ mask @ 1” = 1)* 1)+((“ mask @ 1” = 0)* 9999)

您可以通过在成本表面添加一点噪声来获得更多的“有机”结果(例如,使用1到3之间的随机数,而不是室外像素使用1)。

步骤2.为每个voronoi中心创建累积成本栅格

现在,我们可以r.cost.coordinates针对成本表层运行GRASS算法(一次一个voronoi单元)。

对于起始坐标,请使用vornoi中心。对于结束坐标,请选择您所在区域的一个角。我建议使用“骑士之旅”,因为这样可以使结果更流畅。

结果显示从一个voronoi中心经过的行进时间相等。注意乐队如何环绕建筑物。

在此处输入图片说明

不知道如何最好地自动化它。也许处理批处理模式,或在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

该方法应产生一个栅格,其中每个栅格均根据其最靠近的voronoi中心进行分类,并考虑到障碍物。

然后,您可以使用栅格转多边形。然后,您可以使用Generalize插件从栅格中删除“阶梯”效果伪像。

抱歉在第2步和第3步上含糊不清...我希望有人提出一种更优雅的解决方案:)


1
感谢Steven,我有一个工作正常的GRASS栅格解决方法,但是我希望有一个赏金描述中提到的更优雅的解决方案。
昏暗

0

注意#1:我无法重现所建议的问题,因为差异工具在我执行的多个测试中对我来说效果很好(可能是由于问题的简单几何形状,或者因为问题被解决以来对此工具进行了改进1年前问)。

但是,我提出了一种在PyQGIS中的解决方法,以避免使用“ 差异”工具。一切都基于两个输入层之间的局部相交(请参见下图):

  1. 表示Voronoi多边形的多边形矢量层;
  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结果的不同之处在于,到现在为止,几何图形还没有损坏,可以无错误地“清理”它们


加长孔,例如像河流一样切穿整个地图,您会看到问题。向邻居添加银条会创建看起来与适当的约束Voronoi图完全不同的多边形。我试过了
Underdark

抱歉,我不明白:您在结果中发现任何错误吗?我只测试了多边形与问题中提出的多边形相似的情况下的代码。
mgri

不幸的是,现在无法测试代码,但是您可以显示在i.stack.imgur.com/Jpfra.png中绘制的孔的更改所获得的结果吗?
昏暗

如果将约束扩展到右侧的特征,则会获得this。相反,如果我直接移动约束,则会获得this
mgri

问题是我图形中红色箭头指向的小三角形。它不应该在那里,但也存在于您的结果中。似乎这种方法解决了问题#1的问题,但解决了#2的问题。
昏暗
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.