连接线特征并确定最长线的长度


12

我有一个线要素(参见图片),它代表使用Stream_to_Feature工具创建的一条河流。属性表包含代表不同行的几条记录-问题是最长的行(在视觉上易于区分)在表中没有表示为单行,实际上是由许多较小的行组成的。线条似乎相互接触,尽管它们彼此不交叉。

如何合并这些线,然后使用ArcObjects或可以转换为ArcObjects的手动方法确定最长的线的长度?更好的解决方案将包括摆脱所有支流,而让我只留一条河道作为一条线。

线特征-河


1
他们有连接吗?您说它们不交叉,但这是否意味着它们不共享顶点?
纳撒努斯2011年

1
抱歉-我应该更清楚了。它们确实共享顶点,但彼此之间并没有完全交叉。
雷达

你知道河口在哪里吗?这条河是否一直是一棵树(从每个水源点到河口的唯一路径)?
Kirk Kuykendall

3
实际上,您希望“最长的线”的长度。那可能是一条从上游上游到达另一远上游上游的路线。当溪流的两个主要分支在其河口附近汇合时,就会发生这种情况。取而代之的是,您需要在口与流中任何其他端点之间最长路由。 (此特征甚至不需要将流表示为树:它可以编织并具有孤岛。)
whuber

@whuber-您的评估是正确的-知道我如何使用路线完成此操作吗?
雷达

Answers:


18

首先,有一点背景说明为什么这不是一个难题。流经河流的流量保证了它的线段(如果正确数字化)始终可以定向形成有向无环图(DAG)。反过来,当且仅当它是DAG时,才可以使用称为拓扑排序的技术对图进行线性排序。拓扑排序速度很快:其时间和空间要求均为O(| E | + | V |)(E =边数,V =顶点数),这与它获得的结果一样好。创建这样的线性排序将使查找主流床变得容易。

那么,这里是算法草图。溪流的河床位于其主要河床上。沿着连接到嘴的每个分支向上游移动(如果嘴是汇合处,则可能不止一个),然后递归找到通往该分支的主要床。选择总长度最大的分支:这就是沿主床的“反向链接”。

为了更清楚地说明这一点,我提供了一些(未经测试的)伪代码。输入是一组线段(或弧线)S(包含数字化流),每个线段都有两个不同的端点start(S)和end(S)以及正长度length(S);和河口p,这是一个点。输出是将嘴与最远的上游点结合在一起的一系列片段序列。

我们将需要处理“标记段”(S,p)。它们由段S之一及其两个端点p之一组成。我们将需要找到所有与探测点q共享一个端点的线段S,并用其他端点标记这些线段,然后返回集合:

Procedure Extract(q: point, A: set of segments): Set of marked segments.

如果找不到这样的段,则Extract必须返回空集。 副作用是, Extract必须从集合A中删除它返回的所有段从而修改A本身。

我没有提供Extract的实现:您的GIS将提供选择与q共享端点的段S的功能。标记它们只是将start(S)和end(S)与q进行比较,然后返回两个端点都不匹配的问题。

现在,我们准备解决问题​​。

Procedure LongestUpstreamReach(p: point, A: set of segments): (Array of segments, length)
    A0 = A                        // Optional: preserves A
    C = Extract(p, A0)            // Removes found segments from the set A0!
    L = 0; B = empty array
    For each (S,q) in C:          // Loop over the segments meeting point p
        (B0, M) = LongestUpstreamReach(q, A0)
        If (length(S) + M > L) then
            B = append(S, B0)
            L = length(S) + M
        End if
    End for
    Return (B, L)
End LongestUpstreamReach

过程“ append(S,B0)”将段S 粘贴在数组B0的末尾并返回新数组。

(如果流确实是一棵树:没有岛屿,湖泊,辫子等,那么您就可以省去将A复制到A0的步骤了。)

通过形成LongestUpstreamReach返回的段的并集来回答原始问题。

为了说明这一点,让我们考虑原始地图中的流。假设将其数字化为七个弧的集合。弧形a从上游的点0(在地图的顶部,在下图的右侧,已旋转)的嘴到点1处的第一汇合处。这是一个长弧,说长8个单位。弧b分支到左侧(在地图中)并且很短,大约2个单位长。弧c向右分支,大约4个单位长,依此类推。以“ b”,“ d”和“ f”表示在地图上从上到下的左侧分支,以“ a”表示, “ c”,“ e”和“ g”这两个其他分支,并对顶点0到7进行编号,我们可以抽象地将图表示为弧的集合

A = {a=(0,1), b=(1,2), c=(1,3), d=(3,4), e=(3,5), f=(5,6), g=(5,7)}

我将假定它们具有长度8,2,4,1,2,2,2为通过分别。嘴是顶点0。

数字

第一个示例是对Extract(5,{f,g})的调用。它返回标记的段集{(f,6),(g,7)}。请注意,顶点5位于弧fg(地图底部的两个弧)的交汇处,并且(f,6)和(g,7)用上游端点标记这些弧中的每一个。

下一个示例是对LongestUpstreamReach(0,A)的调用。采取的第一个动作是对Extract(0,A)的调用。此返回包含标记的段落(A,1)的一组它消除段一个从该组A0,其现在等于{B,C,d,E,F,G}。循环有一个迭代,其中(S,q)=(a,1)。在此迭代期间,将对LongestUpstreamReach(1,A0)进行调用。递归地,它必须返回序列(g,e,c)或(f,e,c):两者同等有效。的长度(M),它返回是4 + 2 + 2 = 8。(注意,LongestUpstreamReach并没有修改A0。)在循环结束时,段已附加到流化床中,并且长度已增加到8 + 8 =16。因此,第一个返回值由序列(g,e,c,a)或(f,e,c,a)组成,对于第二种返回值,两种情况下的长度均为16。这显示LongestUpstreamReach如何从嘴巴向上游移动,在每个汇合处选择距离最长的分支,并跟踪沿其路线遍历的线段。

在有许多辫子和孤岛的情况下,可以实现更有效的实现,但是对于大多数目的,如果严格按照所示方式实现LongestUpstreamReach,则几乎不会浪费任何精力,因为在每个汇合处,各个分支中的搜索之间没有重叠:计算时间(和堆栈深度)将与分段总数成正比。


+1现在,只要他们在命名密苏里河之前就知道这一点。
Kirk Kuykendall

1
@Kirk在1800年代初期对美国西部进行递归探索并不容易:-)。
ub

这非常有帮助!我将看看是否可以在GIS中获得此设置,并在工作后共享一些有用的代码。干杯!
雷达

尼斯回答whuber
鸭脚稗亚瑟Burhum

2

拆分线”工具可能对您要执行的操作很有帮助,尽管您将需要找出某种方法来区分一个流分支与另一个流分支(对于“溶解”字段)。不过,这假定您具有ArcInfo许可证。

如果您没有这样的许可证,则可以考虑使用ArcObjects方法获取每个顶点的XY,将其填充为一个IPointCollection,然后将IGeometryas 创建为PolyLineClass


1

您可以使用RivEX,它是9.1 ArcGIS工具(将在9.3和10中运行)。它具有识别河网拓扑问题的工具和许多处理工具。一种这样的工具找到了主干

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.