iOS 百度地图路线绘制与小车平滑移动

百度地图的文件都没有上传,需要自己导入一下才可以使用.
https://github.com/dong136279559/-demo

项目中用到了百度地图,记录一下,为以后开发提供帮助

纹理绘制和分段颜色绘制

我们在利用百度地图计算出路线的点后可以在地图上绘制出自己想要的纹理路线或者分段颜色显示,通过自定义或者利用百度给出的类直接绘制。

我们在计算出路线之后就需要进行路线的绘制了,我们在路线的回调方法中进行设置

#pragma mark - 计算车辆路线结果回调
- (void)onGetDrivingRouteResult:(BMKRouteSearch *)searcher result:(BMKDrivingRouteResult *)result errorCode:(BMKSearchErrorCode)error

当获取到路线的点时,根据百度地图demo,我们可以在此方法中进行路线的自定义设置,可以是纹理图片,可以是分段颜色绘制,也可以是单一颜色,都可以。

     // 通过points构建BMKPolyline
     //构建分段颜色索引数组
     NSArray *colorIndexs = [NSArray arrayWithObjects:
     [NSNumber numberWithInt:0],nil];
     //构建BMKPolyline,使用分段颜色索引,其对应的BMKPolylineView必须设置colors属性
     myPolyline = [BMKPolyline polylineWithPoints:temppoints count: planPointCounts textureIndex:colorIndexs];       
      [_mapView addOverlay:myPolyline];


路线分段颜色绘制

#pragma mark - 路线分段颜色绘制
//根据overlay生成对应的View
- (BMKOverlayView *)mapView:(BMKMapView *)mapView viewForOverlay:(id <BMKOverlay>)overlay
{    
    if ([overlay isKindOfClass:[BMKPolyline class]])
    {
        BMKPolylineView* polylineView = [[BMKPolylineView alloc] initWithOverlay:overlay];
        if (overlay == colorfulPolyline) {
            polylineView.lineWidth = 5;
            /// 使用分段颜色绘制时,必须设置(内容必须为UIColor)
            polylineView.colors = [NSArray arrayWithObjects:
                                   [[UIColor alloc] initWithRed:0 green:1 blue:0 alpha:1],
                                   [[UIColor alloc] initWithRed:1 green:0 blue:0 alpha:1],
                                   [[UIColor alloc] initWithRed:1 green:1 blue:0 alpha:0.5], nil];
        } else {
            polylineView.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:1];
            polylineView.lineWidth = 20.0;
            [polylineView loadStrokeTextureImage:[UIImage imageNamed:@"arrowTexture"]];
        }
        return polylineView;
    }
    return nil;
}

路线纹理绘制

#pragma mark - 路线纹理绘制

//根据overlay生成对应的View
-(BMKOverlayView *)mapView:(BMKMapView *)mapView viewForOverlay:(id<BMKOverlay>)overlay{
  
    if ([overlay isKindOfClass:[BMKPolyline class]])
    {
        BMKPolylineView* polylineView = [[BMKPolylineView alloc] initWithOverlay:overlay];
        
        polylineView.lineWidth = 8;
        polylineView.isFocus = YES;
        polylineView.tileTexture = YES;
        
        // 加载分段纹理绘制 
        UIImage *image = [UIImage imageNamed:@"lv"];
        [polylineView loadStrokeTextureImages:@[image]];
        
        return polylineView;
    }
    
    return nil;
}



【纹理图片】

  • (百度demo中获取)----> 图片宽高必须为2的幂次方才可以正确的在项目中使用。

  • 这里写图片描述
  • 这里写图片描述

//////////////////////////////////////////////////////////////////////////////////

                            华丽的分割线

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

下面介绍一下小车的平滑移动,实现类似滴滴打车,小车移动的效果。

这里写图片描述

步骤1
我们需要先自定义Annotation类,为小车的平滑移动保存方向和Id数据,自定义大头针,方便设置小车方向的旋转 ,两个自定义的类都是为了之后的平滑移动提供帮助。

// 自定义DBMKPointAnnotation用来保存id和angle
@interface DBMKPointAnnotation : BMKPointAnnotation
/** ID  */
@property(strong,nonatomic)NSString *ID;
/** angle  */
@property(strong,nonatomic)NSString *Angle;
@end

// 自定义BMKAnnotationView,用于显示小车
@interface SportAnnotationView1 : BMKAnnotationView

@property (nonatomic, strong) UIImageView *imageView;

@end

@implementation SportAnnotationView1

@synthesize imageView = _imageView;

- (id)initWithAnnotation:(id<BMKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
    if (self) {
        //设置小车显示大小
        [self setBounds:CGRectMake(0.f, 0.f, 40.f, 40.f)];
        _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.f, 0.f, 40.f, 40.f)];
        _imageView.image = [UIImage imageNamed:@"小车图片"];
        _imageView.contentMode = UIViewContentModeScaleAspectFit;
        _imageView.userInteractionEnabled = YES;
        [self addSubview:_imageView];
    }
    return self;
}

@end

步骤2
实时的获取车辆的经纬度,方向,数据,进行展示.
百度给我们的方法非常简单,就是利用UIView动画,对大头针进行一个经纬度及反向转向的设置。下面是代码 --->

   [UIView animateWithDuration:0.5 animations:^{
        //转向                                                                         sportAnnotationView.imageView.transform=CGAffineTransformMakeRotation(angle);
} completion:^(BOOL finished) {
    //平移
    [UIView animateWithDuration:5.5 animations:^{
        annotation.coordinate = CLLocationCoordinate2DMake([dic[@"Lat"] floatValue], [dic[@"Lng"] floatValue]);
    }];
}];

至此小车的平滑移动,我们就可以简单的处理好了。但是,这样处理的话,会有一个bug,就是对地图进行缩放时,小车对跑出地图外,这个问题是怎么引起的呢?我们应该怎么解决呢?

引起这个问题的原因是运用的UIView动画,实际修改了属性的真实值,进行动画,UIView动画是一个过程动画,在缩放时会改变动画过程,导致平滑移动出路线外bug。这个问题就涉及到了UIView动画与核心动画的区别问题,在这个问题的处理上,我们用KeyAnimation动画进行处理(解决方案是根据百度官方人员回复处理的)。

这里创建一个显示当前位置的图层

#import <BaiduMapAPI_Map/BMKMapComponent.h>
#import <BaiduMapAPI_Utils/BMKUtilsComponent.h>

@interface CACoordLayer : CALayer

@property (nonatomic, assign) BMKMapView * mapView;

//定义一个BMKAnnotation对象
@property (nonatomic, strong) BMKPointAnnotation *annotation;

@property (nonatomic) double mapx;

@property (nonatomic) double mapy;

@property (nonatomic) CGPoint centerOffset;

@end

//
//  CACoordLayer.m
//

#import "CACoordLayer.h"

@implementation CACoordLayer

@dynamic mapx;
@dynamic mapy;

- (id)initWithLayer:(id)layer
{
    if ((self = [super initWithLayer:layer]))
    {
        if ([layer isKindOfClass:[CACoordLayer class]])
        {
            CACoordLayer * input = layer;
            self.mapx = input.mapx;
            self.mapy = input.mapy;
            [self setNeedsDisplay];
        }
    }
    return self;
}

+ (BOOL)needsDisplayForKey:(NSString *)key
{
    
    if ([@"mapx" isEqualToString:key])
    {
        return YES;
    }
    if ([@"mapy" isEqualToString:key])
    {
        return YES;
    }
    
    return [super needsDisplayForKey:key];
}

- (void)display
{
    
    CACoordLayer * layer = [self presentationLayer];
    BMKMapPoint mappoint = BMKMapPointMake(layer.mapx, layer.mapy);
    //根据得到的坐标值,将其设置为annotation的经纬度
    self.annotation.coordinate = BMKCoordinateForMapPoint(mappoint);
    //设置layer的位置,显示动画
    CGPoint center = [self.mapView convertCoordinate:BMKCoordinateForMapPoint(mappoint) toPointToView:self.mapView];
    self.position = center;
}

@end


CACoordLayer类的关键代码needsDisplayForKey方法和图层重绘方法display,就是当mapx,mapy改变时会调用图层的重绘功能,为动画做铺垫。为大头针提供动画的能力。

根据创建好的显示当前位置的图层进行移动,下面主要说下关键代码,在自定义的大头针中设置使用的图层为我们刚才创建的图层CACoordLayer,当我们获取到车辆经纬度,方向等信息传给大头针时,我们需要的操作为

- (void)addTrackingAnimationForPoints:(NSArray *)points duration:(CFTimeInterval)duration
{
    if (![points count])
    {
        return;
    }
    
    CACoordLayer * mylayer = ((CACoordLayer *)self.layer);
    
    //preparing
    NSUInteger num = 2*[points count] + 1;
    NSMutableArray * xvalues = [NSMutableArray arrayWithCapacity:num];
    NSMutableArray *yvalues = [NSMutableArray arrayWithCapacity:num];
    
    NSMutableArray * times = [NSMutableArray arrayWithCapacity:num];
    
    double sumOfDistance = 0.f;
    //dis保存的为地址
    double * dis = malloc(([points count]) * sizeof(double));
//    NSLog(@"%p",dis);
    
    
    //the first point is set by the destination of last animation.
    BMKMapPoint preLoc;
    if (!([self.animationList count] > 0 || isAnimatingX || isAnimatingY))
    {
        lastDestination = BMKMapPointMake(mylayer.mapx, mylayer.mapy);
    }
    preLoc = lastDestination;
        
    [xvalues addObject:@(preLoc.x)];
    [yvalues addObject:@(preLoc.y)];
    [times addObject:@(0.f)];
    
    //set the animation points.
    for (int i = 0; i<[points count]; i++)
    {
        TracingPoint * tp = points[i];
        
        //position
        BMKMapPoint p = BMKMapPointForCoordinate(tp.coordinate);
        [xvalues addObjectsFromArray:@[@(p.x), @(p.x)]];//stop for turn
        [yvalues addObjectsFromArray:@[@(p.y), @(p.y)]];
        
        //distance
        dis[i] = BMKMetersBetweenMapPoints(p, preLoc);
        sumOfDistance = sumOfDistance + dis[i];
        dis[i] = sumOfDistance;
        
        //record pre
        preLoc = p;
    }
    
    //set the animation times.
    double preTime = 0.f;
    double turnDuration = TurnAnimationDuration/duration;
    for (int i = 0; i<[points count]; i++)
    {
        double turnEnd = dis[i]/sumOfDistance;
        double turnStart = (preTime > turnEnd - turnDuration) ? (turnEnd + preTime) * 0.5 : turnEnd - turnDuration;
        
        [times addObjectsFromArray:@[@(turnStart), @(turnEnd)]];

        preTime = turnEnd;
    }
    
    //record the destination.
    TracingPoint * last = [points lastObject];
    lastDestination = BMKMapPointForCoordinate(last.coordinate);

    free(dis);
    
    // add animation.
    CAKeyframeAnimation *xanimation = [CAKeyframeAnimation animationWithKeyPath:MapXAnimationKey];
    xanimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
    xanimation.values   = xvalues;
    xanimation.keyTimes = times;
    xanimation.duration = duration;
    xanimation.delegate = self;
    xanimation.fillMode = kCAFillModeForwards;
    
    CAKeyframeAnimation *yanimation = [CAKeyframeAnimation animationWithKeyPath:MapYAnimationKey];
    yanimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
    yanimation.values   = yvalues;
    yanimation.keyTimes = times;
    yanimation.duration = duration;
    yanimation.delegate = self;
    yanimation.fillMode = kCAFillModeForwards;
    
    [self pushBackAnimation:xanimation];
    [self pushBackAnimation:yanimation];
    
    mylayer.mapView = [self mapView];
}
#pragma mark - Animation Delegate

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
    if ([anim isKindOfClass:[CAKeyframeAnimation class]])
    {
        CAKeyframeAnimation * keyAnim = ((CAKeyframeAnimation *)anim);
        if ([keyAnim.keyPath isEqualToString:MapXAnimationKey])
        {
            isAnimatingX = NO;

            CACoordLayer * mylayer = ((CACoordLayer *)self.layer);
            mylayer.mapx = ((NSNumber *)[keyAnim.values lastObject]).doubleValue;
            currDestination.x = mylayer.mapx;
            
            [self updateAnnotationCoordinate];
            [self popFrontAnimationForKey:MapXAnimationKey];
        }
        else if ([keyAnim.keyPath isEqualToString:MapYAnimationKey])
        {
            isAnimatingY = NO;

            CACoordLayer * mylayer = ((CACoordLayer *)self.layer);
            mylayer.mapy = ((NSNumber *)[keyAnim.values lastObject]).doubleValue;
            currDestination.y = mylayer.mapy;
            
            [self updateAnnotationCoordinate];
            [self popFrontAnimationForKey:MapYAnimationKey];
        }
        animateCompleteTimes++;
        if (animateCompleteTimes % 2 == 0) {
            if (_animateDelegate && [_animateDelegate respondsToSelector:@selector(movingAnnotationViewAnimationFinished)]) {
                [_animateDelegate movingAnnotationViewAnimationFinished];
            }
        }
    }
}

这个方法所做的就是将经纬度坐标转换为投影后的直角地理坐标,然后进行CAKeyframeAnimation动画,移动的属性是自定义CACoordLayer中的mapx,mapy,而不是UIView动画中的经纬度,我们都知道核心动画都是假象,并不是真正的移动,所以在移动完之后,重新为mapx赋值,更新大头针位置,避免出现小车移动的起始位置错乱的问题,简易的流程应该是这样的

st=>start: KeyFrame动画对mapx属性动画
e=>end: 赋值改变mapx,mapy,的值,保证下次动画起始位置的正确性
op=>operation: needsDisplayForKey中设置了当前指定的属性key改变是否需要重新绘制,所以产生了动画
cond=>condition: 当动画结束后

st->op->cond
cond(yes)->e

//////////////////////////////////////////////////////////////////////////////////

                            华丽的分割线

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

内边距的设置

我们的项目还要实现,当路线绘制出来后,需要显示这个路线,并且需要有内边距,当我调用这个接口时

   [_mapView setVisibleMapRect:polyLine.boundingMapRect edgePadding:UIEdgeInsetsMake(20, 20, 280, 20) animated:YES];

地图缩放的显示范围并不是我设置的内边距,有误差,类似于这种


这里写图片描述

通过与百度地图技术人员的询问,得知在调取polyLine.boundingMapRect 不能够获取到数值,技术人员给出了另一个方法获取到polyline的MapRect,然后设置了内边距

//根据polyline设置地图范围
- (void)mapViewFitPolyLine:(BMKPolyline *) polyLine {
    CGFloat ltX, ltY, rbX, rbY;
    if (polyLine.pointCount < 1) {
        return;
    }
    BMKMapPoint pt = polyLine.points[0];
    ltX = pt.x, ltY = pt.y;
    rbX = pt.x, rbY = pt.y;
    for (int i = 1; i < polyLine.pointCount; i++) {
        BMKMapPoint pt = polyLine.points[i];
        if (pt.x < ltX) {
            ltX = pt.x;
        }
        if (pt.x > rbX) {
            rbX = pt.x;
        }
        if (pt.y < ltY) {
            ltY = pt.y;
        }
        if (pt.y > rbY) {
            rbY = pt.y;
        }
    }
    BMKMapRect rect;
    rect.origin = BMKMapPointMake(ltX , ltY);
    rect.size = BMKMapSizeMake(rbX - ltX, rbY - ltY);
    [_mapView setVisibleMapRect:rect edgePadding:UIEdgeInsetsMake(0, 0, 100, 0) animated:YES];
    _mapView.zoomLevel = _mapView.zoomLevel - 0.3;
}

而且根据百度技术人员的说法,这个方法也会有误差,是因为SDK设置了边距的级别,目前暂不能根据边距的大小进行精确设置

不过要按照项目要求实现,如果有更好的方法,可以告诉我,谢谢。










如有错误的地方❌,请指正✅。。。

第一次写博客,玻璃心求轻喷,希望自己能坚持下去,当做一种知识总结也能分享给一些新手朋友们.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容