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

Posted

技术标签:

【中文标题】iOS 路径如下:将坐标列表转换为贝塞尔曲线【英文标题】:iOS path following: convert list of coordinates to a bezier curve 【发布时间】:2014-03-12 14:09:39 【问题描述】:

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

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

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

【问题讨论】:

Drawing Smooth Curves - Methods Needed的可能重复 【参考方案1】:

您不应该使用 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;
        
    

这应该会删除不需要的点。您应该使用“平滑度”变量。值越大,删除的细节越多! (因此更好的压缩)

现在有了这个数组,你就可以构建你的贝塞尔路径了!

我不会详细介绍如何将贝塞尔路径实际放置到地图上,*** 上还有很多其他问题以及互联网上的教程。但我很快会谈到你应该如何创建/使用贝塞尔路径的两个控制点:

鉴于我们在上面创建的航路点数组,您可能会添加一些额外的控制点,以避免行车路线走捷径穿过建筑物。因此,您应该像这样计算路径上两个实际点之间的控制点:

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

【讨论】:

感谢您的回答。我仍然认为我需要 MKDirections,而不是路线上的终点位置……而是为用户提供从当前位置到下一个重要点的方向。 请问第一段代码是什么index-=1;是为了? 谢谢,@Sarah92 它应该在刹车片内,我改了!现在它允许在需要时删除多个点 感谢您如此详细的回答! :) 希望我的路线指示现在能正常工作【参考方案2】:

您可以使用 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++;

我希望这能帮助并解决您的问题

【讨论】:

这不会创建一个贝塞尔路径,而只是一个线路径

以上是关于iOS 路径如下:将坐标列表转换为贝塞尔曲线的主要内容,如果未能解决你的问题,请参考以下文章

如何使用贝塞尔曲线沿路径为图像设置动画

iOS ~ 贝塞尔曲线:二阶曲线图📈,ShapeLayer

coreldraw的所有工具的详细作用?

iOS 使用贝塞尔曲线绘制路径

IOS 贝塞尔曲线详解

iOS开发 贝塞尔曲线的使用总结