使用栅格处理确定点是否被包围


9

我正在尝试为自然灾害模型改进当前极其繁琐的vector / python处理。目前,我们有一个冗长的脚本,该脚本会从给定点生成距离/方位线,以确定:

  1. 与其相交的多边形的类型(例如,森林,草丛,沼泽等)
  2. 到该多边形的距离
  3. 这些线中有多少条与多边形相交,以确定其“环绕”程度。

涉及的更多,但这就是要点。我正在尝试找到一种方法来改进此问题,目前已在第3部分中介绍。该想法是确定某个点是否完全被多边形包围,例如在200m之内PointA被包围,而PointB仅被包围约50%

因此,在我的附件图像中,我希望将点A标记为比点B具有更高的风险,因为它完全被多边形包围了。可以重复执行1300万个点,因此这不是一个小任务,我宁愿有一个表面来从中获取值,而不是运行脚本。我认为必须采用各种水文工具或成本路径,但我似乎无法解决。

我该怎么办?


1
Viewshed可以完成任务,但是当应用于1300万点时,将需要大量帮助!首先考虑一下如何清除周围环境易于检查的点,例如位于多边形外部区域的直径小于(例如)200m的点:可以排除“ A”但可能不排除“ B”。永远不会排除“ B”,因为它的视域(将多边形区域设置为非常“高”的位置来阻挡视图)距离B的位置超过200m。
ub

@whuber好点。当然,我可以减少按接近程度实际处理的总点数,实际上也可以减少唯一的经纬度(我在说地理编码的地址,因此公寓楼可以从50点减少为1),但是我仍然会在数百万个地点。如有必要,我也可以将所有内容分解为重叠的块。将调查视域。谢谢!
Loz 2014年

另一个快速屏幕是使用环形邻域来计算多边形的0-1指示器网格的焦点均值:在任何值为1的单元格中,多边形会占据半径的整个周长,因此必须将其包围。这是一种快速的计算方法,它可能会淘汰大多数点,具体取决于它们在哪里以及多边形的卷积如何。您还可以通过先将网格重新采样到较粗的分辨率(例如25-50 m左右)来加快初始筛选的速度。
ub

另一个潜在的处理步骤或预处理步骤是将点通过数据集的栅格化版本,以比较这些点周围的邻域的统计信息。您可以将“周围”要求抽象为点附近的统计信息,或者,如果需要“周围”,则可以使用栅格邻域来找到“简单”点(即,完全位于风险区域内的点),从所有点中解析出“简单”点,然后对其余点使用矢量分析。
DPierce 2014年

哇,我的查询肯定引起了很多兴趣!感谢所有提供建议和意见的人。我将通过他们的方式努力工作并做出回应,但他们都将需要一些时间来进行测试。我保证最终我会回应!
罗兹2014年

Answers:


6

有一个成本路径解决方案,但您必须自己编写代码。 将其应用于问题中图像的每个点时,可能看起来是这样(略微加粗以加快计算速度):

图0

黑色单元格是周围多边形的一部分。从浅橙色(短)到蓝色(长)的颜色显示了视距遍历可以达到的最大距离(最多50个像元),而不会拦截多边形像元。(此图像范围之外的任何单元格都将视为多边形的一部分。)

让我们讨论使用数据的栅格表示来实现此目的的有效方法。在此表示形式中,所有“周围”的多边形像元将具有非零值,并且任何可以“透视”的像元将具有零值。

步骤1:预计算邻域数据结构

您首先必须确定一个单元格阻止另一个单元格的含义。我能找到的最公平的规则之一是:对行和列使用整数坐标(并假设为正方形单元格),让我们考虑哪些单元格可能会从原点(0,0)的视图中阻止单元格(i,j)。我在坐标与i和j最多相差1的所有像元中,指定最接近将(i,j)连到(0,0)的线段的像元(i',j')。因为这并不总是产生唯一的解决方案(例如,当(i,j)=(1,2)时(0,1)和(1,1)都将同样有效地工作),需要一些解决关系的方法。对于关系的这种解析,最好尊重网格中圆形邻域的对称性:否定坐标或切换坐标会保留这些邻域。因此,我们可以决定哪个单元格会阻塞(i,

下面的原型代码用编写了说明此规则R。此代码返回一个数据结构,该数据结构将方便确定网格中任意单元的“环绕度”。

screen <- function(k=1) {
  #
  # Returns a data structure:
  #   $offset is an array of offsets
  #   $screened is a parallel array of screened offset indexes.
  #   $distance is a parallel array of distances.
  # The first index always corresponds to (0,0).
  #
  screened.by <- function(xy) {
    uv <- abs(xy)
    if (reversed <- uv[2] > uv[1]) {
      uv <- rev(uv)
    }
    i <- which.min(c(uv[1], abs(uv[1]-uv[2]), uv[2]))
    ij <- uv + c(floor((1-i)/3), floor(i/3)-1)
    if (reversed) ij <- rev(ij)
    return(ij * sign(xy))
  }
  #
  # For each lattice point within the circular neighborhood,
  # find the unique lattice point that screens it from the origin.
  #
  xy <- subset(expand.grid(x=(-k:k), y=(-k:k)), 
               subset=(x^2+y^2 <= k^2) & (x != 0 | y != 0))
  g <- t(apply(xy, 1, function(z) c(screened.by(z), z)))
  #
  # Sort by distance from the origin.
  #
  colnames(g) <- c("x", "y", "x.to", "y.to")
  ij <- unique(rbind(g[, 1:2], g[, 3:4]))
  i <- order(abs(ij[,1]), abs(ij[,2])); ij <- ij[i, , drop=FALSE]
  rownames(ij) <- 1:length(i)
  #
  # Invert the "screened by" relation to produce the "screened" relation.
  #
  # (Row, column) offsets.
  ij.df <- data.frame(ij, i=1:length(i))
  #
  # Distances from the origin (in cells).
  distance <- apply(ij, 1, function(u) sqrt(sum(u*u)))
  #
  # "Screens" relation (represented by indexes into ij).
  g <- merge(merge(g, ij.df), ij.df, 
             by.x=c("x.to", "y.to"), by.y=c("x","y"))
  g <- subset(g, select=c(i.x, i.y))
  h <- by(g$i.y, g$i.x, identity)

  return( list(offset=ij, screened=h, distance=distance) )
}

的值screen(12)用于描述这种筛选关系:箭头从细胞指向立即筛选它们的细胞。色相按与原点的距离成比例,原点位于该邻域的中间:

图1

这种计算速度很快,对于给定的邻域只需执行一次。例如,当在具有5 m单元的网格上观察200 m时,邻域大小将为200/5 = 40个单位。

步骤2:将计算应用于所选点

其余的方法很简单:要确定相对于此邻域数据结构,位于(x,y)(行和列坐标)处的像元是否被“包围”,请以偏移量(i,j)开始递归执行测试=(0,0)(邻域原点)。如果在(x,y)+(i,j)处的多边形网格中的值不为零,则可见性在那里被阻止。否则,我们将不得不考虑所有可能在偏移量(i,j)处被阻塞的偏移量(使用O返回的数据结构在O(1)时间中找到screen)。如果没有任何障碍物,我们就到达了边界并得出结论:(x,y)没有被包围,因此我们停止了计算(并且不必费心去检查附近的任何剩余点)。

通过跟踪算法中达到的最远视距,我们可以收集更多有用的信息。如果小于所需的半径,则包围该单元;否则,将其包围。否则,不是。

这是R此算法的原型。它比看起来更长,因为R它本身不支持实现递归所需的(简单)堆栈结构,因此堆栈也必须进行编码。实际的算法大约从三分之二开始,只需要十几行。(其中一半只处理网格边缘周围的情况,检查邻域内的索引超出范围。只需将多边形网格k按其周围的行和列扩展,消除任何重复,即可提高效率。需要进行索引范围检查,但要花更多的内存来保存多边形网格。)

#
# Test a grid point `ij` for a line-of-sight connection to the perimeter
# of a circular neighborhood.  
#   `xy` is the grid.
#   `counting` determines whether to return max distance or count of stack ops.
#   `perimeter` is the assumed values beyond the extent of `xy`.
#
# Grid values of zero admit light; all others block visibility
# Returns maximum line-of-sight distance found within `nbr`.
#
panvisibility <- function(ij, xy, nbr=screen(), counting=FALSE, perimeter=1) {
  #
  # Implement a stack for the algorithm.
  #
  count <- 0 # Stack count
  stack <- list(ptr=0, s=rep(NA, dim(nbr$offset)[1]))
  push <- function(x) {
    n <- length(x)
    count <<- count+n         # For timing
    stack$s[1:n + stack$ptr] <<- x
    stack$ptr <<- stack$ptr+n
  }
  pop <- function() {
    count <<- count+1         # For timing
    if (stack$ptr <= 0) return(NULL)
    y <- stack$s[stack$ptr]
    #stack$s[stack$ptr] <<- NA # For debugging
    stack$ptr <<- stack$ptr - 1
    return(y)
  }
  #
  # Initialization.
  #
  m <- dim(xy)[1]; n <- dim(xy)[2]
  push(1) # Stack the *indexes* of nbr$offset and nbr$screened.
  dist.max <- -1
  #
  # The algorithm.
  #
  while (!is.null(i <- pop())) {
    cell <- nbr$offset[i, ] + ij
    if (cell[1] <= 0 || cell[1] > m || cell[2] <= 0 || cell[2] > n) {
      value <- perimeter
    } else {  
      value <- xy[cell[1], cell[2]]
    }
    if (value==0) {
      if (nbr$distance[i] > dist.max) dist.max <- nbr$distance[i]
      s <- nbr$screened[[paste(i)]]
      if (is.null(s)) {
        #exited = TRUE
        break
      }
      push(s)
    }
  }
  if (counting) return ( count )
  return(dist.max)
}

图2:示例

在此示例中,多边形单元为黑色。对于非多边形单元,颜色给出最大视线距离(最多50个单元),范围从浅橙色(短距离)到深蓝色(最长距离)。(单元格是一个宽且高的单元。)明显可见的条纹是由位于“河”中间的小多边形“岛”形成的:每个单元都阻塞了其他单元格的长线。

算法分析

堆栈结构实现了邻居可见度图的深度优先搜索,以查找包围单元的证据。如果像元远离任何多边形,则此搜索仅需要检查半径为k的圆形邻域的O(k)个像元。最坏的情况发生在邻域内有少量分散的多边形像元,但即使如此,邻域的边界也无法完全到达时:这需要检查每个邻域中几乎所有像元,即O(k ^ 2)操作。

下面的行为是遇到的典型现象。 对于较小的k值,除非多边形填充了大部分网格,否则大多数非多边形像元显然不会被包围,并且算法的缩放比例类似于O(k)。对于中间值,缩放比例开始看起来像O(k ^ 2)。随着k变得非常大,大多数单元将被包围,并且可以在检查整个邻域之前确定该事实:该算法的计算量因此达到了实际极限。当邻域半径接近网格中最大的连接非多边形区域的直径时,可以达到此限制。

例如,我使用counting编码到原型中的选项screen来返回每个调用中使用的堆栈操作数。这测量了计算量。下图绘制了堆栈操作数的平均值与邻域半径的​​关系。它表现出预测的行为。

图3

我们可以使用它来估算在网格上评估1300万点所需的计算量。假设使用k = 200/5 = 40的邻域。那么平均将需要数百次堆栈操作(取决于多边形网格的复杂性以及相对于多边形的1300万个点的位置),这意味着在高效的编译语言中,最多只有几千个简单的数字运算将是必需的(加,乘,读,写,偏移量等)。大多数PC能够以该速率评估大约一百万个点的包围度。(R实现要慢得多,因为它不适合这种算法,这就是为什么只能将其视为原型的原因。)因此,我们可能希望以合理有效和适当的语言(C ++)进行有效实现和Python浮现在脑海中- 假设整个多边形网格都位于RAM中,则可以在一分钟或更短的时间内完成1300万个点的评估。

当网格太大而无法放入RAM时,可以将此过程应用于网格的平铺部分。它们仅需按k行和列重叠;拼接结果时,在重叠处取最大值。

其他应用

水体“获取”与其点的“环绕性”密切相关。实际上,如果我们使用等于或大于水体直径的邻域半径,我们将在水体中的每个点处创建一个(非定向)获取的网格。通过使用较小的邻域半径,我们至少将在所有最高获取点处获得较低的获取下限,这在某些应用程序中可能已经足够好了(并且可以大大减少计算量)。该算法的一种变体,其将“筛选依据”关系限制在特定方向上,将是一种有效地计算这些方向上的提取的方法。请注意,此类变体需要修改screen; 的代码;的代码panvisibility完全不变。


2

我绝对可以看到如何使用栅格解决方案来实现此目的,但是即使点数减少了,我也希望获得非常大的/高分辨率的图像,因此很难处理网格或一组网格。鉴于此,我想知道在gdb中利用拓扑是否可能更有效。您可以使用以下方法找到所有内部空隙:

arcpy.env.workspace = 'myGDB'
arcpy.CreateTopology_management('myGDB', 'myTopology', '')    
arcpy.AddFeatureClassToTopology_management('myTopology', 'myFeatures', '1','1')    
arcpy.AddRuleToTopology_management ('myToplogy', 'Must Not Have Gaps (Area)', 'myFeatures', '', '', '')    
arcpy.ValidateTopology_management('myTopology', 'Full_Extent')
arcpy.ExportTopologyErrors_management('myTopology', 'myGDB', 'topoErrors')
arcpy.FeatureToPolygon_management('topoErrors_line','topoErrorsVoidPolys', '0.1')`

您可以topoErrorsVoidPolys按照正常的模式Intersect_analysis()或其他方式使用。您可能需要弄乱从中提取感兴趣的多边形topoErrorsVoidPolys。@whuber在gis.stackexchange.com上的其他地方有很多关于此类内容的出色文章。


这是一个有趣的预处理想法。我认为可以很容易地适应200m的限制(通过缓冲和交叉等)。您对网格变得相当大的观点无疑是一个真正的问题。GIS中没有规则说问题的解决方案必须完全基于栅格或完全基于矢量(尽管有一个原则表明您应该有充分的理由将中间的一种表示形式转换为另一种表示形式)分析;如您所建议的那样,完全可以从中受益。
whuber

0

如果您真的想光栅化...我会按照此伪代码的方式做一些事情(不要勉强行事,因为很明显我是AML回退!:p)

  1. 栅格化点(“ pts_g”)和多边形(“ polys_g”(
  2. 无效= regiongroup(con(isnull(polys_g),1))
  3. 可能需要做一些事情来完善空隙以消除不需要的外部多边形/开放宇宙区域
  4. pts_surrounded = con(void,pts_g)

只是弥补了这一点,因此可能需要改进。


您的解决方案未参考极限距离(例如200m),因此似乎无法正确回答问题。
whuber

你是对的。这也适用于我的其他答案。我想一个人可以使用Expand(),但是到那时我会认为@radouxju的回答在功能上是等效的,并且可能更快。(没有针对视域的使用,只是不要使用太多)。
罗兰

时间用完时正在尝试编辑。想扩大在Expand()说这样做的pts_g,只是使用Con()交叉带polys_g
罗兰

0

如果您使用阈值距离值(此处约为200 m),则最佳解决方案是使用矢量分析:

1)在每个点周围创建一个200 m的缓冲区(插图中的黑色)

2)使用缓冲区和多边形之间的相交工具(分析)(插图中的蓝色)。如果在周围多边形的边界和缓冲区之间执行此操作,效果会更好,但是最终结果是相同的。

3)使用要素进行多边形(管理)以创建点完全包围的多边形(插图中为红色)

4)通过位置(管理)或空间连接(分析)选择图层以标识被包围的点。使用空间连接可以使您获得有关嵌入多边形的信息(多边形的区域,区域统计信息等),这对于进一步处理很有用。

备选方案2b)根据您的需要,您可以按位置选择200 m距离内的周围多边形,然后可以识别某些类型的“外壳”,但并不严格于2)。

在此处输入图片说明

考虑到“迷宫案”,这可能会有所帮助:评估从该位置“逃生”需要多长时间。

  • 您已经可以从分析中排除完全包含或完全免费的点

  • 然后将障碍物转换为栅格,并将其值设置为NoData(如果您有多边形),将其设置为不包含米的像元大小(这将使您的成本栅格化)。

  • 第三,您可以使用新生成的成本栅格来计算成本距离

  • 最后,根据转换为栅格(形成环面)的缓冲区边界,将区域统计信息用作表。如果可以全方位逃脱,则最小值应大约为200(取决于分析的像元大小)。但是,如果您在迷宫中,最大值将大于200。因此,区域统计的最大值减去200将是一个连续的值,指示“逃避”的难度。


请阐明您对“包围”的定义。问题中的描述建议,当多边形的某个部分围绕该点的所有方向上(在200 m的距离内)都可见时,应将该点视为“环绕” 。您究竟如何在步骤(3)中进行测试?(使用向量分析并不容易!)
whuber

我添加了一个小插图,这样更容易解释。如果缓冲区没有在所有方向上与多边形相交,则该循环将不会闭合。如果循环没有关闭,则不会形成多边形。
radouxju 2014年

我不确定您所说的“循环”或“封闭”是什么意思。请注意,即使一个点周围的半径r(小于200 m)的圆都没有完全包含在多边形内,也可以将其“环绕”。想想迷宫:多边形是迷宫中走廊以外的所有事物。一个人可以从迷宫中的任何一点开始逃脱,但是从迷宫的外部看不到它们的意义上说,大多数点都是“包围”的。
ub

据我了解,包围意味着您无法逃脱的地方。从图上可以看出,您可以从B逃脱,但不能从A逃脱。另一方面,如果使用视域,B似乎被包围了(好吧,因为图像上没有比例尺,所以可能不在200 m处,但是从各个方向看时都可以看到多边形边界)。我认为我们需要@Loz提供的更多详细信息
radouxju 2014年

如果要检查“无法逃脱”是一个标准,那么这将不是一个困难的问题:仅对多边形的补码进行区域分组,仅保留唯一的外部分量,然后检查其中是否包含。我认为,仔细阅读该问题(尤其是对所有可能的方位的引用)可以清楚地说明“包围”的含义,尽管我同意这个说法含糊其词。
ub
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.