检查用户是否正在遵循路线(iPhone)

4
我正在制作一款基于导航的应用程序。在这个应用程序中,我根据用户选择的点绘制路径。如果用户没有按路径行驶,我需要重新计算路径。
为了计算路径,我使用了谷歌方向 API。为了绘制路径,我使用了以下代码:
- (void) drawRoute:(NSArray *) path
{
    NSInteger numberOfSteps = path.count;
    [self.objMapView removeOverlays: self.objMapView.overlays];

    CLLocationCoordinate2D coordinates[numberOfSteps];
    for (NSInteger index = 0; index < numberOfSteps; index++)
    {
        CLLocation *location = [path objectAtIndex:index];
        CLLocationCoordinate2D coordinate = location.coordinate;

        coordinates[index] = coordinate;
    }

    for( id <MKOverlay> ovr in [self.objMapView overlays])
    {
        MKPolylineView *polylineView = [[MKPolylineView alloc] initWithPolyline:ovr];


        if (polylineView.tag == 22)
        {
            [self.objMapView removeOverlay:ovr];
        }
        [polylineView release];
    }

    MKPolyline *polyLine = [MKPolyline polylineWithCoordinates:coordinates count:numberOfSteps];
    [self.objMapView addOverlay:polyLine];


}

到目前为止一切都好。
现在,如果用户超出路线(超过100米),我想要收到通知,并且我也可以获得通知。
问题是:如果道路是直的(超过100米),那么我无法在道路上获取点。为了解释这个问题,我附上了图片...
在这张图片中,假设黑线是我的路径(折线),红色圆圈是我从谷歌API中获得的点。但在蓝色圆圈所示的直线路径上,我无法获取要比较的点,并且在此路径上调用重新计算函数。
有人能告诉我如何获得路线上的所有点,即使它是一条笔直的道路吗?

通常情况下,Google API返回的多边形点位于连接处或曲线上,而不是直线路径上,它会提供直线路径的起点和终点。 - iphonic
这就是问题所在,iphonic。当距离大于100米时,OP需要沿直线有一些中间点。 - James Webster
在这种情况下,您可以找出点之间距离的平均值,如果任何点之间的距离超过了该平均值,则将用户位置放置在该点。 - iphonic
感谢大家,但我不知道如何在起点和终点之间获取多边形点。 - Nirav Gadhiya
2个回答

4
我知道这是一个旧的线程,但最近遇到了同样的问题,并找到了一个可以的解决方案。其概念是不计算到每个线段的距离,而只计算到与最接近点相连的两个线段的距离。
1. 计算您当前位置与MKPolyline中所有点的距离,并从中取最小值。(可能有一些优化的好方法,例如不在每次位置更新时迭代所有点,但现在没有时间深入研究)。 2. 现在,您知道最近的折线点的距离。然而,该点可能仍然很远,而折线本身(连接此点和前一个或下一个点)可能更接近。因此,计算您当前位置与这两个线段之间的距离,就可以得到最接近的距离。
现在,这并不是完全可靠的。虽然它最小化了API调用的数量,在某些情况下(如果您在MKPolyline中拥有疯狂的弯曲),它可能会在不需要时调用API,但是嘿,那么同一条线将再次绘制,没有损坏。在我的测试中,它运行良好,您还可以调整准确度。我已将其设置为200m(0.2km),如下面的代码所示。
//Get Coordinates of points in MKPolyline
NSUInteger pointCount = routeLineGuidanceTurn.pointCount;
CLLocationCoordinate2D *routeCoordinates = malloc(pointCount * sizeof(CLLocationCoordinate2D));
[routeLineGuidanceTurn getCoordinates:routeCoordinates
                         range:NSMakeRange(0, pointCount)];
NSLog(@"route pointCount = %d", pointCount);


//Determine Minimum Distance and GuidancePoints from
double MinDistanceFromGuidanceInKM = 1000;
CLLocationCoordinate2D prevPoint;
CLLocationCoordinate2D pointWithMinDistance;
CLLocationCoordinate2D nextPoint;

for (int c=0; c < pointCount; c++)
{
    double newDistanceInKM = [self distanceBetweentwoPoints:Currentcordinate.latitude longitude:Currentcordinate.longitude Old:routeCoordinates[c].latitude longitude:routeCoordinates[c].longitude];
    if (newDistanceInKM < MinDistanceFromGuidanceInKM) {
        MinDistanceFromGuidanceInKM = newDistanceInKM;
        prevPoint = routeCoordinates[MAX(c-1,0)];
        pointWithMinDistance = routeCoordinates[c];
        nextPoint = routeCoordinates[MIN(c+1,pointCount-1)];
    }
}
free(routeCoordinates);


NSLog(@"MinDistanceBefore: %f",MinDistanceFromGuidanceInKM);

//If minimum distance > 200m we might have to recalc GuidanceLine.
//To be sure we take the two linesegments connected to the point with the shortest distance and calculate the distance from our current position to that linedistance.
if (MinDistanceFromGuidanceInKM > 0.2) {
    MinDistanceFromGuidanceInKM = MIN(MIN([self lineSegmentDistanceFromOrigin:Currentcordinate onLineSegmentPointA:prevPoint pointB:pointWithMinDistance], [self lineSegmentDistanceFromOrigin:Currentcordinate onLineSegmentPointA:pointWithMinDistance pointB:nextPoint]),MinDistanceFromGuidanceInKM);

    if (MinDistanceFromGuidanceInKM > 0.2) {
        // Call the API and redraw the polyline.
    }
}

这里有一个有趣的计算两点之间距离的方法。我知道有一个内置的函数可以实现,但我已经在我的代码中了。

-(double)distanceBetweentwoPoints:(double)Nlat longitude:(double)Nlon Old:(double)Olat longitude:(double)Olon  {
    //NSLog(@"distanceBetweentwoPoints");
    double Math=3.14159265;
    double radlat1 = Math* Nlat/180;
    double radlat2 = Math * Olat/180;
    double theta = Nlon-Olon;
    double radtheta = Math * theta/180;
    double dist = sin(radlat1) * sin(radlat2) + cos(radlat1) * cos(radlat2) * cos(radtheta);
    if (dist>1) {dist=1;} else if (dist<-1) {dist=-1;}
    dist = acos(dist);
    dist = dist * 180/Math;
    dist = dist * 60 * 1.1515;
    return dist * 1.609344;
}

这里是计算一个点和两个其他点之间线段距离的代码片段。我从这里获取了这段代码:https://stackoverflow.com/a/28028023/3139134,稍作修改以适应CLLocationCoordinate2D并返回距离。

- (CGFloat)lineSegmentDistanceFromOrigin:(CLLocationCoordinate2D)origin onLineSegmentPointA:(CLLocationCoordinate2D)pointA pointB:(CLLocationCoordinate2D)pointB {

    CGPoint dAP = CGPointMake(origin.longitude - pointA.longitude, origin.latitude - pointA.latitude);
    CGPoint dAB = CGPointMake(pointB.longitude - pointA.longitude, pointB.latitude - pointA.latitude);
    CGFloat dot = dAP.x * dAB.x + dAP.y * dAB.y;
    CGFloat squareLength = dAB.x * dAB.x + dAB.y * dAB.y;
    CGFloat param = dot / squareLength;

    CGPoint nearestPoint;
    if (param < 0 || (pointA.longitude == pointB.longitude && pointA.latitude == pointB.latitude)) {
        nearestPoint.x = pointA.longitude;
        nearestPoint.y = pointA.latitude;
    } else if (param > 1) {
        nearestPoint.x = pointB.longitude;
        nearestPoint.y = pointB.latitude;
    } else {
        nearestPoint.x = pointA.longitude + param * dAB.x;
        nearestPoint.y = pointA.latitude + param * dAB.y;
    }

    CGFloat dx = origin.longitude - nearestPoint.x;
    CGFloat dy = origin.latitude - nearestPoint.y;
    return sqrtf(dx * dx + dy * dy) * 100;

}

我在KM中得到了混合的结果,似乎有些奇怪。我也不明白lineSegmentDistanceFromOrigin最后的* 100是什么意思。你能给我解释一下吗? - Sjoerd Perfors
嗨,Sjoerd。你能详细说明一下混合结果吗?你是什么意思?老实说,我不记得100是为什么了。可能是因为我想要结果以100米为单位。不确定。最好自己进行一些测试,以确定从该方法中获取的单位。 - guido
我将此函数与 CLLocation 的 distanceFromLocation 函数比较,用于计算两点之间的距离。由于某些原因,我并不能总是得到正确的结果。现在,我已经转而采用另一种解决方案,该方案使用 MKMapPoints 并获得更好的结果:http://stackoverflow.com/questions/26240183/how-to-determine-the-next-poi-in-a-navigation-route - 感谢回复! - Sjoerd Perfors
我想在我的应用程序中实现相同的功能,但是找不到解决方案,能帮助我们吗? - Pramod Shukla

2

对于每一步中的每一对点,您可以使用勾股定理计算它们之间的距离:

distance = sqrt(  pow((point1.x - point2.x), 2)   +   pow((point1.y - point2.y), 2)  )

然后,如果距离大于100米,则在线段上添加中间点。

谢谢您的回答。但是我对地图不熟悉,所以能否请您解释一下如何获取线段上的中间点。 - Nirav Gadhiya
谢谢,我已经通过一条线绘制算法得到了所有的点。 但是我遇到了另一个问题,请查看这个问题,请帮帮我。 - Nirav Gadhiya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接