用于在地图中查找附近的平行街道对的高效算法

时间:2015-07-02 19:10:51

标签: python algorithm openstreetmap

我正在开发一个处理来自Openstreetmap的地图数据的Python程序,我需要能够识别彼此接近并且并行的街道(路)对。现在,我使用的基本算法非常低效:

  1. 将所有街道(街道对象)放入一个大型列表
  2. 使用嵌套的for循环查找列表中每条可能的两条街道对;对于每对,在两条街道周围绘制一个矩形,并计算每条街道的朝向角度。
  3. 如果矩形重叠,重叠区域足够大,角度相似,两对街道被认为是平行的并且彼此靠近。
  4. 这适用于小地图,但是对于大地图,最大的问题显然是会有大量的对要迭代,因为一个城市可能有数千条街道。我希望能够在大面积(如城市)上运行程序,而无需将区域分割成更小的区域。

    我想到的一个想法是按纬度或经度对街道列表进行排序,并且仅比较列表中彼此相距50个位置的街道对。它可能会更有效但它似乎仍然不是很优雅;有没有更好的方法?

    每个Street都由Node对象组成,我可以轻松地检索Node对象和每个Node的纬度/经度位置。我也可以轻松找回街道所朝向的角度。

2 个答案:

答案 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)。

剩下的就是过滤出段对,尽管它们几乎是共线的,但不会重叠。这些段位于同一无限线的不同部分上或附近,但它们并不重要。这只是一个代数。

这里提到的所有算法都有相当不错的维基百科文章。如果他们不熟悉,请查看它们。