我正在开发一个处理来自Openstreetmap的地图数据的Python程序,我需要能够识别彼此接近并且并行的街道(路)对。现在,我使用的基本算法非常低效:
for
循环查找列表中每条可能的两条街道对;对于每对,在两条街道周围绘制一个矩形,并计算每条街道的朝向角度。这适用于小地图,但是对于大地图,最大的问题显然是会有大量的对要迭代,因为一个城市可能有数千条街道。我希望能够在大面积(如城市)上运行程序,而无需将区域分割成更小的区域。
我想到的一个想法是按纬度或经度对街道列表进行排序,并且仅比较列表中彼此相距50个位置的街道对。它可能会更有效但它似乎仍然不是很优雅;有没有更好的方法?
每个Street都由Node对象组成,我可以轻松地检索Node对象和每个Node的纬度/经度位置。我也可以轻松找回街道所朝向的角度。
答案 0 :(得分:3)
我认为首先要做的是按角度对所有街道进行排序,从而使所有平行街道彼此靠近。
然后,假设您可以确定角度完全,并且您需要具有完全相同角度的街道对。 (这不是一个真实的情况,因为浮点精度和所需的街道可能在数据中不完全平行,但让我们暂时忘记这一点。)
然后,所有排序的街道可以分成相同方向的组。在每个这样的组内存在如下定义的自然顺序。考虑垂直于具有相同方向的所有街道的另一条线。对于任何这样的街道,考虑与该垂直线的交叉点,并通过该交叉点对所有这些街道进行排序(我假设所有街道都是无限长的)。
这种排序可以在没有任何交叉点的情况下轻松完成,您只需计算每条街道与原点(或任何其他固定点)到该街道线的距离,并按此距离对街道进行排序。 (如果您有一条由标准直线方程式Ax+By+C=0
定义的街道,则距离原点的距离为C/sqrt(A*A+B*B)
。)
现在,按此排序顺序,您可以将所有需要的并行关闭街道彼此非常接近。如果所有的街道都是无限长的,那么这些最接近的对将总是一个接一个地走;街道数量有限,两者之间可能还有其他街道,但我认为在任何真实数据中都会有很少的街道。因此,你可以采取一些距离差异的阈值,并检查其中的所有对。
现在让我们记住,角度没有精确定义。我可以建议以下方法。为街道维护二进制搜索树(类似于C ++的std::map
),搜索关键字是从原点到街道线的距离。按顺序沿着街道行进,因为它们按角度排序。在树中,我们将保持所有街道的角度相差不到一些阈值。因此,在树的每个街道的每个时间,树中的邻居将具有相差小于阈值的两个角度,并且距离原点的距离相差小于某个阈值。因此,对于每条街道,请执行以下操作:
第一个点是O(log N)
,第二个点是{@ 1}}每个已删除的街道,如果你只是让另一个指针沿着排序的角度数组运行,指向要删除的街道,第三个是{{每个邻居街道考虑1}}。
非常粗略的伪代码:
O(log N)
此处O(log N)
找到树中的前一条街道,即最大距离小于给定街道距离的街道,sort lines by angle
r = 0 // the street to be deleted from the tree
for l=0..n-1
tree.add(street[l])
while street[r].angle<streel[l].angle-angle_threshold
tree.remove(street[r])
other_street=tree.prev(street[l])
while other_street.dist>street[l].dist-dist_threshold
process(street[l], other_street)
other_street = tree.prev(other_street)
other_street=tree.next(street[l])
while other_street.dist<street[l].dist+dist_threshold
process(street[l], other_street)
other_street = tree.next(other_street)
同样找到下一条街道。这两项操作都可以在tree.prev
中完成。
这不会“循环”数组,即不考虑一对位于排序数组末尾的街道对,另一对位于最开始,但这很容易修复。
答案 1 :(得分:3)
您在垃圾箱中处理细分市场的想法并不错。您需要仔细考虑遍历区域边界的路段会发生什么。
另一个想法是霍夫改变所有的路段。每个线段所在的无限线对应于2d霍夫空间中的点:线的极角是一个轴,并且到线最近点的原点的距离是另一个。从一个线上的两个点到Hough点的转换是简单的代数。
现在,您可以使用最近点对算法检测近乎共线的路段。令人高兴的是,这可以在O(n log n)预期时间内完成。例如。使用k-d树。插入树中的所有点。使用标准k-d树算法查找每个点的最近邻居。对对距离进行排序,并将结果的前缀作为成对考虑,停止对距离太远以满足您的标准&#34;附近和平行&#34;。这些最近邻居对中有O(n)。
剩下的就是过滤出段对,尽管它们几乎是共线的,但不会重叠。这些段位于同一无限线的不同部分上或附近,但它们并不重要。这只是一个代数。
这里提到的所有算法都有相当不错的维基百科文章。如果他们不熟悉,请查看它们。