iOS路径跟随:将坐标列表转换为贝塞尔曲线

时间:2014-03-12 14:09:39

标签: ios objective-c uibezierpath cllocation bezier

我有一个代表用户路线的坐标数组。我希望能够重绘这条路线并让其他用户完全遵循它。虽然我只想在路线改变方向时保存重要的坐标(航路点)。

有没有办法可以将坐标数组转换为贝塞尔曲线并使用它,所以我只得到重要的坐标,而不是将每个坐标保存在一个数组中(大多数坐标方向相同且不需要)

我希望能够重建路线以提供航路点。这些航路点将用于引导用户沿着路线前进,因为最终我将使用MKDirections将它们引导到每个航路点。

2 个答案:

答案 0 :(得分:2)

您不应该使用MKDirections,因为它不是为了遵循预先计算的路径,而是为您提供到达目的地的最快路线!因此我将改用MKMapOverlayRenderer!

有几件事你可以做到这一点。我想谈谈用于删除不必要的航路点的简单算法:

NSArray* path = YOURPATH
NSMutableArray* waypoints = [path mutableCopy];

CGFloat smoothness = 0.01;

for(NSInteger scale=1;scale<log2(waypoints.count);scale++)
{
    for(NSInteger index = 0; index<waypoints.count-2;index++)
    {
        CLLocation *start = [waypoints objectAtIndex:index];
        CLLocation *middle = [waypoints objectAtIndex:index+1];
        CLLocation *end = [waypoints objectAtIndex:index+2];

        CGFloat dist_start_end = [end distanceFromLocation:start];
        CGFloat dist_start_middle_end = [middle distanceFromLocation:start]+[end distanceFromLocation:middle];

        CGFloat diff = dist_start_end-dist_start_middle_end;
        CGFloat ratio = diff/(dist_start_end);

        if (ratio<1+smoothness) {
            [waypoints removeObjectAtIndex:index+1];
            index-=1;
        }
    }
}

这应该删除不需要的点。您应该使用'smoothness'变量进行游戏。值越大,删除的细节越多! (因此压缩效果更好)

现在使用此数组,您可以构建贝塞尔曲线路径!

我不会详细介绍如何将bezier路径实际放到地图上,在Stackoverflow上有很多其他问题,还有互联网上的教程。但我将简要介绍如何创建/使用贝塞尔路径的两个控制点:

鉴于我们在上面创建的航点阵列,您可能会添加一些额外的控制点,以避免驾驶方向在建筑物中使用快捷方式。因此,您应该计算路径上两个实际点之间的控制点,如下所示:

CGFloat cornerRadius = 5;
NSMutableArray* pathPoints = [NSMutableArray new];

if (waypoints.count>1) {
    [pathPoints addObject:[waypoints objectAtIndex:0]];
    [pathPoints addObject:[waypoints objectAtIndex:0]];
    [pathPoints addObject:[waypoints objectAtIndex:1]];
    [pathPoints addObject:[waypoints objectAtIndex:1]];
}
for (NSInteger index = 1;index<waypoints.count-1;index++) {
    CLLocation* lastLocation = [waypoints objectAtIndex:index-1];
    CLLocation* currentLocation = [waypoints objectAtIndex:index];
    CLLocation* nextLocation = [waypoints objectAtIndex:index+1];

    [pathPoints addObject:currentLocation];

    CGFloat latDiff = currentLocation.coordinate.latitude - lastLocation.coordinate.latitude;
    CGFloat longDiff = currentLocation.coordinate.longitude - lastLocation.coordinate.longitude;
    CGFloat dist = [currentLocation distanceFromLocation:lastLocation];
    CGFloat controlPointALat = cornerRadius*latDiff/dist;
    CGFloat controlPointALong = cornerRadius*longDiff/dist;

    CLLocation* controlPointA =
    [[CLLocation alloc] initWithLatitude:controlPointALat longitude:controlPointALong];
    [pathPoints addObject:controlPointA];

    if (index<waypoints.count-2) {
        CLLocation* nextNextLocation = [waypoints objectAtIndex:index+2];

        latDiff = nextNextLocation.coordinate.latitude - nextLocation.coordinate.latitude;
        longDiff = nextNextLocation.coordinate.longitude - nextLocation.coordinate.longitude;
        dist = [nextNextLocation distanceFromLocation:nextLocation];
        CGFloat controlPointBLat = cornerRadius*latDiff/dist;
        CGFloat controlPointBLong = cornerRadius*longDiff/dist;

        CLLocation* controlPointB =
        [[CLLocation alloc] initWithLatitude:controlPointBLat longitude:controlPointBLong];
        [pathPoints addObject:controlPointB];
    }else{
        [pathPoints addObject:controlPointA];
    }
    [pathPoints addObject:nextLocation];
}

现在您可以使用此pathArray通过简单的for循环构建bezierPaths:

for(NSInteger index=0;index<pathPoints.count-3;index+=4)
{
   CLLocation *start = [pathPoints objectAtIndex:index];
   CLLocation *controlPointA = [pathPoints objectAtIndex:index+1];
   CLLocation *controlPointB = [pathPoints objectAtIndex:index+2];
   CLLocation *end = [pathPoints objectAtIndex:index+3];

   //BEZIER CURVE HERE
}

您可以使用UIBezierPath并使用CGPath属性创建MKMapOverlayRenderer,也可以直接使用CGPath。稍后将在此处描述添加地图路径: MKMapOverlayRenderer

此处描述了创建UIBezierPath: UIBezierPath

答案 1 :(得分:0)

您可以使用NSValues数组,因为您无法将CGPoint直接添加到数组中

这就是我们这样做的方式

NSMutableArray *arrayOfPoints = [[NSMutableArray alloc] init];
[arrayOfPoints addObject:[NSValue valueWithCGPoint:CGPointMake(10, 20)]];
// here point is the cgpoint you want to add 

现在,您已将点添加到数组中,您可以执行的操作是遍历每个对象并将它们添加到您的UIBezierPath

  NSInteger  i = 0;
   UIBezierPath _path1 = [UIBezierPath bezierPath];

for(NSValue *val in arrayOfPoints){
    if (i==0){
        [_path1 moveToPoint:[val CGPointValue]];
    }
    else{
        [_path1 addLineToPoint:[val CGPointValue]];
    }
    i++;
}

我希望这有助于解决您的问题