高效算法,用于查找Polyline的哪个部分包含在另一个折线内

时间:2014-02-28 11:16:34

标签: android algorithm google-polyline

我正在尝试将Polyline - overview_polylineGoogle Directions API与一组已存在的折线进行比较,并查看新折线的哪一部分已包含在其中一条折线内。对我来说,折线是一种驾驶路线表示,从Google Directions API中检索。它基本上是世界上任何地方的任何路线。为了简化,我们总能找到属于具体城市或国家的路线,并且只能比较这个路线。此外,目前最长可达250公里。这是一些例子:

enter image description here

enter image description here

这里存在哪条路线并且哪条路线是新的并不重要。在任何情况下我都希望得到结果,这条路线是相似的(好吧,可能是它们不是90%相似,但我们假设它们是。)

目前我正在使用暴力强制将新折线逐个与现有折线进行比较。在此之前,我使用this算法将折线分成点并比较每个点以查看是否存在匹配。如果distance between此点小于100米,我会将点数视为相同。

如果我发现已经存在一些折线,主要是覆盖新的折线,我会停止处理。

看起来像这样:

Polyline findExistingPolyline(Polyline[] polylines, Polyline polyline) {
  LatLng[] polylinePoints = PolylineDecoder.toLatLng(polyline);
  for (Polyline existing: polylines) {
    LatLng[] existingPoints = PolylineDecoder.toLatLng(existing);
    if (isMostlyCovered(existingPoints , polylinePoints)) {
       return existing;
    }
  }

  return null;

}

boolean isMostlyCovered(LatLng[] existingPoints, LatLng[] polylinePoints) {
  int initialSize = polylinePoints.length;
  for (LatLng point: polylinePoints) {
    for (LatLng existingPoint: existingPoints) {
       if (distanceBetween(existingPoint, point) <= 100) {
         polylinePoints.remove();// I actually use iterator, here it is just demosnstration
       }
    }
  }
  // check how many points are left and decide if polyline is mostly covered
  // if 90% of the points is removed - existing polylines covers new polyline
  return (polylinePoints.length * 100 / initialSize) <= 10;
}

显然,这个算法很糟糕(特别是在最坏的情况下,当新的折线没有匹配时),因为有两个很多周期并且可能有太多的点无法比较。

所以,我想知道,是否有更有效的方法来比较折线。

1 个答案:

答案 0 :(得分:1)

您似乎只比较折线的点,而不是两者之间的线。这意味着直线和具有附加中心点的相同线将不匹配。或者我错过了什么? (如果我的假设是正确的,那我认为这是你方法的弱点。)

您使用的距离计算涉及椭球三角法,可能很昂贵。但是,这里不需要精确的测量,您只想匹配两个节点。如果你需要覆盖一个与杆子不接近的众所周知的范围,你可以将纬度/经度视为平面坐标,也许可以对经度进行校正。

boolean isWithin100m(LatLng a, LatLng b) {
    double dy = (a.lat - b.lat) * R * pi / 180.0;

    if (dy < -100 || dy > 100) return false;

    double dmid = 0.5 * (a.lat + b.lat) * pi / 180.0;
    double dx = (a.lng - b.lng) * R * pi / 180.0 / cos(dmid);
    return dx*dx + dy*dy <= 10000.0;
}

在这里,R是地球的半径。该方法应该比您的确切解决方案更快。如果你最北端和最南端的余弦相似,你甚至可以将它们排除在外,只需将固定的余额余弦作为经度的恒定因子。

此外,您还可以通过每次比较解码新的折线。您只能在findExistingPolyline中执行此操作一次,并将LatLng[]传递给isMostlyCovered。如果您可以预先计算现有折线的数据,则将其存储为LatLng[]也会有所帮助。保持每个折线的极端纬度和经度以及线长可以帮助您在早期排除明显的不匹配。

也许你应该超越它:除了经度和纬度之外,存储Earth-Centered, Earth-fixed坐标并将它们保存在k-d Tree中以便于最近邻查找。这是我对算法最佳加速的赌注,代价是额外的数据。

最好不要为每条折线创建一个新的列表,然后从中删除,但要保持列表的完整性,并保留一个本地的“二手”设置,这个设置应该比删除点更快。