You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
839 lines
33 KiB
839 lines
33 KiB
// |
|
// TrackInfoViewController.m |
|
// watch |
|
// |
|
// Created by xTT on 2017/7/21. |
|
// Copyright © 2017年 xTT. All rights reserved. |
|
// |
|
|
|
#import "TrackInfoViewController.h" |
|
#import "CustomAnnotationView.h" |
|
#import "MKPointAnnotation+NewTemp.h" |
|
//高德 |
|
#import <AMapFoundationKit/AMapFoundationKit.h> |
|
#import <AMapSearchKit/AMapSearchKit.h> |
|
|
|
#define kAnimationDuration 3 |
|
#define kSpeed 50 // 每秒多少像素 |
|
|
|
//关键帧旋转动画 |
|
#define RotationAnimationKey @"transform.rotation.z" |
|
//关键帧位移动画 |
|
#define PositionAnimationKey @"position" |
|
//关键帧划线动画 |
|
#define StrokeEndAnimationKey @"strokeEnd" |
|
|
|
#import "TracingPoint.h" |
|
#import "Util.h" |
|
@interface TrackInfoViewController ()<CAAnimationDelegate,MKMapViewDelegate,AMapSearchDelegate> |
|
{ |
|
//当前选中的 位置 |
|
Locate *curLocate; |
|
} |
|
//动画持续的时间 (不超过5秒) 时速 每秒 320 |
|
@property(nonatomic,assign) NSTimeInterval durationTime; |
|
// 路线动画 |
|
@property(nonatomic,strong) CAAnimation *shapeLayerAnimation; |
|
// 头部光点动画 |
|
@property(nonatomic,strong) CAKeyframeAnimation *headLayerAnimation; |
|
|
|
@property(nonatomic,assign) NSInteger currentIndex ; |
|
//路径点的指针数组 |
|
@property(nonatomic,assign) CGPoint *pathPoints; |
|
//路径点的指针数组 |
|
@property(nonatomic,strong) NSMutableArray <TracingPoint *>*pathPointsModels; |
|
// angele数组 每个点的指向 |
|
@property(nonatomic,strong) NSMutableArray <NSNumber *>*angeleValues; |
|
|
|
@property(nonatomic,assign) double SumJuli; |
|
@property(nonatomic,strong) MKPolyline *routeLine; |
|
@property(nonatomic,strong) MKPolylineRenderer *routeLineView; |
|
|
|
@property(nonatomic,strong) NSMutableArray<MKPointAnnotation*>* pointAnnotationArr; |
|
// |
|
@property(nonatomic,strong) NSArray *animations; |
|
@property(nonatomic,strong) NSArray *juliArr; |
|
/** |
|
轨迹回放的layer |
|
*/ |
|
@property(nonatomic,strong) CAShapeLayer * shapeLayer; |
|
/** |
|
头部的光点 |
|
*/ |
|
//@property(nonatomic,strong) CAShapeLayer * headView; |
|
@property(nonatomic,strong) UIImageView * headView; |
|
|
|
@property(nonatomic,strong) NSArray<UIBezierPath*>* beziePaths; |
|
/** |
|
轨迹回放图层 |
|
*/ |
|
@property(nonatomic,strong) UIView *trackView; |
|
|
|
@property (nonatomic, strong) AMapSearchAPI *searchAPI; |
|
|
|
@end |
|
|
|
@implementation TrackInfoViewController |
|
|
|
- (NSMutableArray *)mapPoints{ |
|
if (!_mapPoints) { |
|
_mapPoints = [[NSMutableArray alloc] init]; |
|
} |
|
return _mapPoints; |
|
} |
|
-(NSMutableArray *)pointAnnotationArr{ |
|
if(!_pointAnnotationArr){ |
|
_pointAnnotationArr = [NSMutableArray array]; |
|
} |
|
return _pointAnnotationArr; |
|
} |
|
-(UIView *)trackView{ |
|
if(!_trackView){ |
|
_trackView = [[UIView alloc] initWithFrame: self.mapView.bounds]; |
|
_trackView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.1]; |
|
[self.mapView addSubview:_trackView]; |
|
} |
|
return _trackView; |
|
} |
|
-(void)viewDidDisappear:(BOOL)animated{ |
|
[super viewDidDisappear:animated]; |
|
//做一些必要的清除 |
|
self.startAndStopBtn.selected = NO; |
|
[self startAndStopBtnAction:self.startAndStopBtn]; |
|
if(self.headView){ |
|
[self.headView.layer removeAllAnimations]; |
|
self.headView = nil; |
|
} |
|
if(self.pathPoints){ |
|
free(self.pathPoints); |
|
self.pathPoints = nil; |
|
} |
|
[self.mapView removeFromSuperview]; |
|
|
|
|
|
} |
|
- (void)viewDidLoad { |
|
[super viewDidLoad]; |
|
self.zx_hideBaseNavBar = YES; |
|
self.mapView.userInteractionEnabled = NO; |
|
self.mapView.delegate = self; |
|
//禁止旋转 |
|
self.mapView.rotateEnabled = NO; |
|
|
|
self.searchAPI = [[AMapSearchAPI alloc] init]; |
|
self.searchAPI.delegate = self; |
|
|
|
|
|
|
|
|
|
|
|
self.startAndStopBtn.layer.masksToBounds = YES; |
|
self.startAndStopBtn.layer.cornerRadius = 10; |
|
[self.startAndStopBtn addTarget:self action:@selector(startAndStopBtnAction:) forControlEvents:UIControlEventTouchUpInside]; |
|
[self.view bringSubviewToFront: self.startAndStopBtn]; |
|
|
|
//放大和缩小 VIew |
|
self.zoomBtnView.layer.cornerRadius = 10; |
|
self.zoomBtnView.layer.masksToBounds = YES; |
|
|
|
} |
|
//MARK: 重新加载地图轨迹数据 |
|
- (void)loadWithInfoTrack:(Track*)track Back:(BlockCall)blockCall{ |
|
self.startAndStopBtn.selected = NO; |
|
[self startAndStopBtnAction:self.startAndStopBtn]; |
|
self.title = track.date; |
|
[self.mapView removeAnnotations:self.pointAnnotationArr]; |
|
[self.mapView removeOverlay:self.routeLine]; |
|
[self.mapPoints removeAllObjects]; |
|
self.mapPoints = nil; |
|
self.trackView = nil; |
|
self.routeLine = nil; |
|
self.routeLineView = nil; |
|
self.pointAnnotationArr = nil; |
|
[self.headView removeFromSuperview]; |
|
self.headView = nil; |
|
WEAKSELF |
|
[track getTrackInfoSuccess:^(NSMutableArray *arr) { |
|
[weakSelf.mapPoints addObjectsFromArray:arr]; |
|
[weakSelf addLocate:arr]; |
|
if(arr.count>1){ |
|
weakSelf.startAndStopBtn.selected = YES; |
|
[weakSelf startAndStopBtnAction:weakSelf.startAndStopBtn]; |
|
} |
|
//默认选中 起点 |
|
CustomAnnotationView *tempView = [CustomAnnotationView new]; |
|
tempView.annotation = weakSelf.pointAnnotationArr.lastObject; |
|
[weakSelf mapView:weakSelf.mapView didSelectAnnotationView:tempView]; |
|
if(blockCall){ |
|
blockCall(); |
|
} |
|
} failure:^{ |
|
if(blockCall){ |
|
blockCall(); |
|
} |
|
}]; |
|
} |
|
//缩小地图 |
|
- (IBAction)subtractBtnAction:(id)sender { |
|
if(!self.startAndStopBtn.isSelected){ |
|
return; |
|
} |
|
|
|
//设置地图范围 |
|
//设置中心点为当前地图范围的中心点 |
|
CLLocationCoordinate2D center = self.mapView.region.center; |
|
//设置跨度为当前地图范围的跨度 * 比例系数 |
|
MKCoordinateSpan span = MKCoordinateSpanMake(self.mapView.region.span.latitudeDelta * 2, self.mapView.region.span.longitudeDelta * 2); |
|
|
|
//设置范围 |
|
@try { |
|
MKCoordinateRegion region = MKCoordinateRegionMake(center, span); |
|
xLog(@"span %@",[NSValue valueWithMKCoordinateSpan:span]) |
|
xLog(@"center %@",[NSValue valueWithMKCoordinate:center]) |
|
|
|
[self.mapView setRegion: region animated:YES]; |
|
|
|
} @catch (NSException *exception) { |
|
xLog(@"error: %@",exception.description); |
|
} @finally { |
|
|
|
} |
|
|
|
|
|
} |
|
|
|
//放大地图 |
|
- (IBAction)addZoomBtnAction:(id)sender { |
|
if(!self.startAndStopBtn.isSelected){ |
|
return; |
|
} |
|
// 设置范围 |
|
// 设置中心点 |
|
CLLocationCoordinate2D center = self.mapView.region.center; |
|
|
|
// 设置跨度 : 跨度变小,地图显示缩大 (地图的范围跨度 * 比例系数) |
|
MKCoordinateSpan span = MKCoordinateSpanMake(self.mapView.region.span.latitudeDelta * 0.5, self.mapView.region.span.longitudeDelta * 0.5); |
|
//设置地图范围 |
|
[self.mapView setRegion:MKCoordinateRegionMake(center, span) animated:YES]; |
|
|
|
} |
|
|
|
-(void)startAndStopBtnAction:(UIButton *)sender{ |
|
|
|
if(sender.isSelected){ |
|
[self.mapView removeAnnotations:self.pointAnnotationArr]; |
|
[self.mapView removeOverlay:self.routeLine]; |
|
self.currentIndex = 0; |
|
//经纬度的区域坐标 |
|
if(self.routeLine){ |
|
MKMapRect mapShowRect = [self.routeLine boundingMapRect]; |
|
xLog(@"停止按钮:mapShowRect X: %f Y:%f Width: %f, Height: %f", mapShowRect.origin.x,mapShowRect.origin.y,mapShowRect.size.width,mapShowRect.size.height); |
|
//显示区域 |
|
[self.mapView setVisibleMapRect:mapShowRect edgePadding:UIEdgeInsetsMake(50, 50, 50, 50) animated:NO]; |
|
} |
|
[self initShapeLayerWithPath:[self pathForPoints:self.pathPoints count:self.pointAnnotationArr.count]]; |
|
}else{ |
|
// [self.mapView removeAnnotations:self.pointAnnotationArr]; |
|
// [self.mapView removeOverlay:self.routeLine]; |
|
|
|
[self.trackView removeFromSuperview]; |
|
self.trackView = nil; |
|
self.currentIndex = 0; |
|
[self.mapView addAnnotations:self.pointAnnotationArr]; |
|
} |
|
[sender setSelected:!sender.isSelected]; |
|
self.mapView.userInteractionEnabled = sender.isSelected; |
|
} |
|
#pragma mark - 构造shapeLayer |
|
- (void)initShapeLayerWithPath:(CGPathRef)path{ |
|
if(_shapeLayer){ |
|
[self.shapeLayer removeAllAnimations]; |
|
[self.shapeLayer removeFromSuperlayer]; |
|
[self.headView.layer removeAllAnimations]; |
|
[self.headView.layer removeFromSuperlayer]; |
|
[self.headView removeFromSuperview]; |
|
self.shapeLayer = nil; |
|
self.headView = nil; |
|
|
|
} |
|
self.shapeLayer = [[CAShapeLayer alloc] init]; |
|
self.shapeLayer.frame = self.trackView.bounds; |
|
self.shapeLayer.frame = self.mapView.bounds; |
|
self.shapeLayer.strokeColor = mainColor.CGColor; |
|
self.shapeLayer.fillColor = [UIColor clearColor].CGColor; |
|
self.shapeLayer.lineJoin = kCALineJoinRound; |
|
self.shapeLayer.lineCap = kCALineCapRound; |
|
self.shapeLayer.lineWidth = 3; |
|
self.shapeLayer.path = path; |
|
self.shapeLayer.lineDashPhase = 10; |
|
NSArray *arr = @[@(10),@(10)]; |
|
self.shapeLayer.lineDashPattern = arr; |
|
|
|
self.headView = [[UIImageView alloc] init]; |
|
self.headView.frame = CGRectMake(-20, -20, 10, 10); |
|
self.headView.backgroundColor = [UIColor whiteColor]; |
|
self.headView.layer.cornerRadius = 5; |
|
self.headView.layer.masksToBounds = YES; |
|
// self.headView.image = [UIImage imageNamed:@"icon_enter_the_footprint"]; |
|
|
|
if(self.angeleValues.count>1){ |
|
self.headView.transform = CGAffineTransformMakeRotation(self.angeleValues[self.angeleValues.count-1].doubleValue); |
|
} |
|
[self.trackView.layer addSublayer:self.shapeLayer]; |
|
[self.trackView.layer addSublayer: self.headView.layer]; |
|
|
|
self.shapeLayerAnimation = [self constructShapeLayerAnimation]; |
|
self.shapeLayerAnimation.delegate = self; |
|
self.shapeLayerAnimation.removedOnCompletion = NO; |
|
self.shapeLayerAnimation.fillMode = kCAFillModeForwards; |
|
[self.shapeLayer addAnimation:self.shapeLayerAnimation forKey:@"shape"]; |
|
|
|
self.animations = [self constructHeadLayerAnimationWithBeziePaths:self.beziePaths]; |
|
if(self.animations.count != 0){ |
|
self.headLayerAnimation = self.animations[self.currentIndex]; |
|
self.headLayerAnimation.removedOnCompletion = NO; |
|
self.headLayerAnimation.delegate = self; |
|
[self.headView.layer addAnimation:self.headLayerAnimation forKey:[NSString stringWithFormat:@"headPosition%d",self.currentIndex]]; |
|
// [self.mapView addAnnotation:self.pointAnnotationArr.lastObject]; |
|
|
|
[self addTrackViewPoint:self.pathPoints[0] Type:0 Angle:0]; |
|
} |
|
|
|
|
|
|
|
} |
|
//MARK: 动画的代理方法 ----------CAAnimationDelegate------------ |
|
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{ |
|
|
|
if([anim isKindOfClass:[CABasicAnimation class]]){ |
|
if(self.routeLine){ |
|
[self.mapView addOverlay:self.routeLine]; |
|
} |
|
} |
|
if([anim isKindOfClass:[CAKeyframeAnimation class]]){ |
|
self.currentIndex++; |
|
if (self.currentIndex <self.pointAnnotationArr.count){ |
|
MKPointAnnotation * tempAnn = (MKPointAnnotation *)[[self.pointAnnotationArr reverseObjectEnumerator] allObjects][self.currentIndex]; |
|
|
|
CustomAnnotationView * tempView = [CustomAnnotationView new]; |
|
tempView.annotation = tempAnn; |
|
[self mapView:self.mapView didSelectAnnotationView:tempView]; |
|
} |
|
if(self.currentIndex >= self.animations.count){ |
|
self.currentIndex = 0; |
|
if(self.pathPoints){ |
|
[self addTrackViewPoint:self.pathPoints[self.pointAnnotationArr.count-1] Type:2 Angle:0]; |
|
[self.headView.layer removeFromSuperlayer]; |
|
if(!self.startAndStopBtn.isSelected){ |
|
[self startAndStopBtnAction:self.startAndStopBtn]; |
|
} |
|
} |
|
return; |
|
} |
|
CAKeyframeAnimation *nextAnim = self.animations[self.currentIndex]; |
|
nextAnim.removedOnCompletion = NO; |
|
nextAnim.delegate = self; |
|
// [self.headView addAnimation:nextAnim forKey:[NSString stringWithFormat:@"headPosition%d",currentIndex]]; |
|
[self.headView.layer addAnimation:nextAnim forKey:@"headPosition"]; |
|
if(self.pathPoints){ |
|
CGPoint currentPoint = self.pathPoints[self.currentIndex]; |
|
if (self.currentIndex == 0){ |
|
//第一 |
|
[self addTrackViewPoint:currentPoint Type:0 Angle:0]; |
|
double angle = self.angeleValues[self.angeleValues.count-1].doubleValue; |
|
self.headView.transform = CGAffineTransformMakeRotation(angle); |
|
}else if(self.currentIndex == self.pointAnnotationArr.count-1){ |
|
//最后一个 |
|
[self addTrackViewPoint:currentPoint Type:2 Angle:0]; |
|
}else{ |
|
NSInteger tempIndex = self.angeleValues.count-self.currentIndex-1; |
|
if(tempIndex < 0){ |
|
tempIndex = 0; |
|
} |
|
double angle = self.angeleValues[tempIndex].doubleValue; |
|
// if(self.currentIndex == 1){ |
|
// self.headView.transform = CGAffineTransformMakeRotation(angle); |
|
// } |
|
[self addTrackViewPoint:currentPoint Type:1 Angle:angle ]; |
|
self.headView.transform = CGAffineTransformMakeRotation(angle); |
|
} |
|
|
|
|
|
} |
|
|
|
} |
|
} |
|
|
|
//MARK: 添加 在TrackView 上的点 |
|
/** |
|
添加 在TrackView 上的点 |
|
|
|
@param centerPoint 点的坐标 |
|
@param type 类型 起点 0 平常 1 终点 2 |
|
@param angle 图标旋转的 弧度 |
|
*/ |
|
-(void)addTrackViewPoint:(CGPoint)centerPoint Type:(int)type Angle:(double)angle{ |
|
if(!_trackView){ |
|
return; |
|
} |
|
CGFloat Width = 12; |
|
CGFloat height = 12; |
|
|
|
__block UIImageView *view = [UIImageView new]; |
|
view.contentMode = UIViewContentModeScaleAspectFit; |
|
switch (type) { |
|
case 0: |
|
view.frame = CGRectMake(0, 0, 30, 30); |
|
view.image = [UIImage imageNamed:@"起点"]; |
|
view.center = CGPointMake(centerPoint.x, centerPoint.y-15); |
|
break; |
|
case 1: |
|
view.frame = CGRectMake(0, 0, Width,height); |
|
view.image = [UIImage imageNamed:@"icon_enter_the_footprint"]; |
|
// xLog(@"radian: %f", angle); |
|
view.transform = CGAffineTransformRotate(view.transform, angle); |
|
view.center = centerPoint; |
|
break; |
|
case 2: |
|
view.frame = CGRectMake(0, 0, 30, 30); |
|
view.image = [UIImage imageNamed:@"终点"]; |
|
view.center = CGPointMake(centerPoint.x, centerPoint.y-15); |
|
break; |
|
|
|
default: |
|
break; |
|
} |
|
|
|
[_trackView addSubview:view]; |
|
|
|
|
|
//自定义动画 |
|
// view.transform = CGAffineTransformScale(view.transform, 0.5, 0.5); |
|
// // 弹簧动画,参数分别为:时长,延时,弹性(越小弹性越大),初始速度 |
|
// [UIView animateWithDuration:1 delay:0 usingSpringWithDamping:0.3 initialSpringVelocity:0.3 options:UIViewAnimationOptionLayoutSubviews animations:^{ |
|
// //因为先缩放了所以得 2倍 0.5*2 = 1 |
|
// view.transform = CGAffineTransformScale(view.transform, 2, 2); |
|
// } completion:^(BOOL finished) { |
|
// |
|
// }]; |
|
|
|
|
|
} |
|
|
|
|
|
#pragma mark - 经纬度转屏幕坐标, 调用者负责释放内存! |
|
- (CGPoint *)pointsForCoordinates:(CLLocationCoordinate2D *)coordinates count:(NSUInteger)count{ |
|
if (coordinates == NULL || count <= 1) |
|
{ |
|
return NULL; |
|
} |
|
|
|
/* 申请屏幕坐标存储空间. */ |
|
CGPoint *points = (CGPoint *)malloc(count * sizeof(CGPoint)); |
|
|
|
/* 经纬度转换为屏幕坐标. */ |
|
for (int i = 0; i < count; i++) |
|
{ |
|
points[i] = [self.mapView convertCoordinate:coordinates[i] toPointToView:self.mapView]; |
|
} |
|
return points; |
|
} |
|
|
|
#pragma mark - 构建path, 调用者负责释放内存! |
|
- (CGPathRef)pathForPoints:(CGPoint *)points count:(NSUInteger)count{ |
|
if (points == NULL || count <= 1){ |
|
return NULL; |
|
} |
|
UIBezierPath *layerPathBezier = [UIBezierPath bezierPath]; |
|
[layerPathBezier moveToPoint:points[0]]; |
|
|
|
//贝塞尔路线数组 |
|
NSMutableArray *mbeziePaths = [NSMutableArray array]; |
|
//上一个点 |
|
CGPoint lastPoint = points[0]; |
|
double sumJuli = 0; |
|
NSMutableArray *juliArr = [NSMutableArray array]; |
|
for (int i = 1 ; i < count; i++) { |
|
UIBezierPath *beziePath = [UIBezierPath bezierPath]; |
|
[beziePath moveToPoint:lastPoint]; |
|
[beziePath addLineToPoint:points[i]]; |
|
[layerPathBezier addLineToPoint:points[i]]; |
|
CGPoint nowPiont = points[i]; |
|
CGFloat xDist = lastPoint.x - nowPiont.x; |
|
CGFloat yDist = lastPoint.y - nowPiont.y; |
|
double juli = sqrt((xDist*xDist)+(yDist*yDist)); |
|
[juliArr addObject:@(juli)]; |
|
//计算总的距离(相对在屏幕上的距离) |
|
sumJuli += juli; |
|
lastPoint = points[i]; |
|
[mbeziePaths addObject:beziePath]; |
|
} |
|
|
|
self.juliArr = [juliArr copy]; |
|
self.SumJuli = sumJuli; |
|
self.durationTime = sumJuli/(kSpeed); //每秒 320/2 dp 距离 |
|
// if(self.durationTime > 5.00){ |
|
// self.durationTime = 5.00; |
|
// } |
|
self.beziePaths = [mbeziePaths copy]; |
|
return layerPathBezier.CGPath; |
|
} |
|
|
|
#pragma mark - 构建shapeLayer的basicAnimation |
|
- (CAAnimation *)constructShapeLayerAnimation{ |
|
CABasicAnimation *theStrokeAnimation = [CABasicAnimation animationWithKeyPath:StrokeEndAnimationKey]; //画线 |
|
theStrokeAnimation.duration = self.durationTime; |
|
theStrokeAnimation.fromValue = @0.f; |
|
theStrokeAnimation.toValue = @1.f; |
|
theStrokeAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; |
|
return theStrokeAnimation; |
|
} |
|
#pragma mark - 构建动画轨迹头部发光点 关键帧动画 |
|
- (NSArray *)constructHeadLayerAnimationWithBeziePaths:(NSArray<UIBezierPath*>*)beziePaths{ |
|
if(self.juliArr.count != beziePaths.count){ |
|
return [NSArray array]; |
|
} |
|
NSMutableArray *arr = [NSMutableArray array]; |
|
CAAnimationGroup *group = [CAAnimationGroup animation]; |
|
for (int i=0; i<beziePaths.count; i++) { |
|
CAKeyframeAnimation *headKeyAnimation = [CAKeyframeAnimation animationWithKeyPath:PositionAnimationKey]; //位置 |
|
|
|
NSNumber *julinumber = self.juliArr[i]; |
|
double juli = julinumber.doubleValue; |
|
double duration = juli/self.SumJuli* self.durationTime; |
|
headKeyAnimation.duration = duration; |
|
headKeyAnimation.path = beziePaths[i].CGPath; |
|
// headKeyAnimation.values = [arr copy]; |
|
headKeyAnimation.calculationMode = kCAAnimationPaced ; |
|
headKeyAnimation.fillMode = kCAFillModeForwards ; |
|
// headKeyAnimation.rotationMode = kCAAnimationRotateAutoReverse; |
|
// headKeyAnimation.rotationMode = kCAAnimationRotateAuto; |
|
[arr addObject:headKeyAnimation]; |
|
} |
|
|
|
group.animations = arr; |
|
|
|
|
|
return arr; |
|
} |
|
|
|
- (void)addLocate:(NSArray<Locate *>*)arr{ |
|
WEAKSELF |
|
NSMutableArray *pointArr = [NSMutableArray array]; |
|
self.pathPointsModels = [NSMutableArray array]; |
|
self.angeleValues = [NSMutableArray array]; |
|
|
|
TracingPoint * firstTp = [[TracingPoint alloc] init]; |
|
Locate *firstLocate = arr.firstObject; |
|
firstTp.coordinate = CLLocationCoordinate2DMake([firstLocate.lat doubleValue], |
|
[firstLocate.lon doubleValue]); |
|
|
|
// |
|
firstTp.course = [self.pathPointsModels firstObject].course; |
|
[self.pathPointsModels addObject:firstTp]; |
|
[self.angeleValues addObject:@(0)]; |
|
for (int i = 1; i<arr.count; i++) |
|
{ |
|
//此点坐标 |
|
Locate *tempLocate = arr[i]; |
|
CLLocationCoordinate2D tempCoordinate = CLLocationCoordinate2DMake([tempLocate.lat doubleValue], |
|
[tempLocate.lon doubleValue]); |
|
//上一个点的坐标 (arr.length > 2) |
|
Locate *tempLocate2 = arr[i-1]; |
|
CLLocationCoordinate2D tempCoordinate2 = CLLocationCoordinate2DMake([tempLocate2.lat doubleValue], |
|
[tempLocate2.lon doubleValue]); |
|
TracingPoint * tp = [[TracingPoint alloc] init]; |
|
//此点的坐标 |
|
tp.coordinate = tempCoordinate; |
|
//此点相对于上个点的偏移角 |
|
tp.course = [Util calculateCourseFromCoordinate:tempCoordinate to:tempCoordinate2]; |
|
// 角度转换 |
|
double radian = [Util fixNewDirection: tp.course basedOnOldDirection:0]; |
|
//计算弧度 角度*弧度值 DegToRad |
|
[self.angeleValues addObject:@(radian*DegToRad)]; |
|
[self.pathPointsModels addObject:tp]; |
|
} |
|
|
|
// self.angeleValues = [NSMutableArray array]; |
|
// NSArray *tempArr = [self.pathPointsModels copy]; |
|
// for (int i = 0; i < tempArr.count; i++) { |
|
// TracingPoint *tp = tempArr[i]; |
|
// //angle |
|
// double currDir = [Util fixNewDirection:tp.course basedOnOldDirection:0]; |
|
// //DegToRad 1弧度对应多少度 |
|
// [self.angeleValues addObject:@(currDir * DegToRad)]; |
|
// } |
|
|
|
|
|
|
|
[arr enumerateObjectsUsingBlock:^(Locate * _Nonnull locate, NSUInteger idx, BOOL * _Nonnull stop) { |
|
MKPointAnnotation *pointAnnotation = [[MKPointAnnotation alloc] init]; |
|
pointAnnotation.tempLocate = locate; |
|
pointAnnotation.tracingPoint = self.pathPointsModels[idx]; |
|
// xLog(@"idx: %d, tp.course:%f ", idx, pointAnnotation.tracingPoint.course); |
|
CLLocationCoordinate2D tempCoordinate = CLLocationCoordinate2DMake([locate.lat doubleValue], |
|
[locate.lon doubleValue]); |
|
pointAnnotation.coordinate = tempCoordinate; |
|
NSDate *date = [NSDate dateWithTimeIntervalSince1970:[locate.timestamp doubleValue]]; |
|
NSString *dateStr = [myHelper getDateFormatWithStr:@"M月d日 HH:mm" |
|
date:date]; |
|
if (locate.loc_type) { |
|
pointAnnotation.title = [NSString stringWithFormat:@"定位类型:%@ %@",locate.loc_type,dateStr]; |
|
}else{ |
|
pointAnnotation.title = [NSString stringWithFormat:@"%@",dateStr]; |
|
} |
|
pointAnnotation.subtitle = locate.address; |
|
|
|
CLLocation * pointLocation = [[CLLocation alloc] initWithLatitude:[locate.lat doubleValue] longitude:[locate.lon doubleValue]]; |
|
|
|
[pointArr addObject:pointLocation]; |
|
|
|
// [weakSelf setMapZoom:pointAnnotation.coordinate]; |
|
|
|
[weakSelf.pointAnnotationArr addObject:pointAnnotation]; |
|
// [weakSelf.mapView addAnnotation:pointAnnotation]; |
|
}]; |
|
if(arr.count > 1){ |
|
[self drawLineWithLocationArray:[pointArr copy]]; |
|
[self.startAndStopBtn setHidden:NO]; |
|
}else{ |
|
[self.startAndStopBtn setHidden:YES]; |
|
if(self.pointAnnotationArr && self.pointAnnotationArr.count > 0){ |
|
[self.mapView addAnnotations:self.pointAnnotationArr]; |
|
self.mapView.userInteractionEnabled = YES; |
|
[self.mapView showAnnotations:self.pointAnnotationArr animated:YES]; |
|
[self.mapView selectAnnotation:self.pointAnnotationArr[0] animated:YES]; |
|
} |
|
|
|
} |
|
|
|
} |
|
#pragma mark - 画线 |
|
- (void)drawLineWithLocationArray:(NSArray *)locationArray |
|
{ |
|
NSInteger pointCount = [locationArray count]; |
|
CLLocationCoordinate2D *coordinateArray = (CLLocationCoordinate2D *)malloc(pointCount * sizeof(CLLocationCoordinate2D)); |
|
|
|
for (int i = 0; i < pointCount; ++i) { |
|
CLLocation *location = [locationArray objectAtIndex:i]; |
|
coordinateArray[(pointCount-1)-i] = [location coordinate]; |
|
} |
|
|
|
self.routeLine = [MKPolyline polylineWithCoordinates:coordinateArray count:pointCount]; |
|
|
|
|
|
//经纬度的区域坐标 |
|
MKMapRect mapShowRect = [self.routeLine boundingMapRect]; |
|
xLog(@"mapShowRect X: %f Y:%f Width: %f, Height: %f", mapShowRect.origin.x,mapShowRect.origin.y,mapShowRect.size.width,mapShowRect.size.height); |
|
//显示区域 |
|
[self.mapView setVisibleMapRect:mapShowRect edgePadding:UIEdgeInsetsMake(50, 50, 50, 50) animated:NO]; |
|
|
|
|
|
//把路径点变成在 屏幕上的坐标点 |
|
self.pathPoints = [self pointsForCoordinates:coordinateArray count:pointCount]; |
|
|
|
free(coordinateArray); |
|
coordinateArray = NULL; |
|
} |
|
|
|
|
|
#pragma mark - 地图控件代理方法 |
|
|
|
#pragma mark 显示大头针时调用,注意方法中的annotation参数是即将显示的大头针对象 |
|
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{ |
|
//由于当前位置的标注也是一个大头针,所以此时需要判断,此代理方法返回nil使用默认大头针视图 |
|
static NSString *key1=@"AnnotationKey1"; |
|
CustomAnnotationView *annotationView = (CustomAnnotationView*)[_mapView dequeueReusableAnnotationViewWithIdentifier:key1]; |
|
MKPointAnnotation *pointAnnotation = (MKPointAnnotation*)annotation; |
|
//如果缓存池中不存在则新建 |
|
if (!annotationView) { |
|
annotationView=[[CustomAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:key1]; |
|
annotationView.canShowCallout=NO;//不允许交互点击 |
|
}else{ |
|
//重新设置此类大头针视图的大头针模型(因为有可能是从缓存池中取出来的,位置是放到缓存池时的位置) |
|
annotationView.annotation = annotation; |
|
} |
|
|
|
if([annotation isEqual:self.pointAnnotationArr.firstObject]&&self.pointAnnotationArr.count>1){ |
|
//修改大头针视图 设置终点 |
|
[annotationView setImageWithString:@"终点"]; |
|
//设置大小 |
|
annotationView.frame = CGRectMake(0, 0, 30, 30); |
|
//设置中心偏移量 |
|
annotationView.centerOffset = CGPointMake(0, -15); |
|
|
|
annotationView.calloutOffset=CGPointMake(0, 1);//定义详情视图偏移量 |
|
return annotationView; |
|
} |
|
if([annotation isEqual:self.pointAnnotationArr.lastObject]){ |
|
//修改大头针视图 设置起点 |
|
[annotationView setImageWithString:@"起点"]; |
|
|
|
//设置大小 |
|
annotationView.frame = CGRectMake(0, 0, 30, 30); |
|
//设置中心偏移量 |
|
annotationView.centerOffset = CGPointMake(0, -15); |
|
|
|
annotationView.calloutOffset=CGPointMake(0, 1);//定义详情视图偏移量 |
|
return annotationView; |
|
} |
|
[annotationView setImageWithString:@"icon_enter_the_footprint"]; |
|
double radian = [Util fixNewDirection:pointAnnotation.tracingPoint.course basedOnOldDirection:0]; |
|
// xLog(@"radian %f", radian*DegToRad); |
|
[annotationView setRadian:radian*DegToRad]; |
|
//设置大小 |
|
annotationView.frame = CGRectMake(0, 0, 12, 12); |
|
//设置中心偏移量 |
|
annotationView.centerOffset = CGPointMake(3, -3); |
|
|
|
annotationView.calloutOffset=CGPointMake(0, 0);//定义详情视图偏移量 |
|
|
|
return annotationView; |
|
|
|
} |
|
|
|
//MARK: 点击地图 中的 标点 触发的方法 |
|
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view{ |
|
if([view isKindOfClass:[CustomAnnotationView class]]){ |
|
if([view.annotation isKindOfClass:[MKPointAnnotation class]]){ |
|
MKPointAnnotation *pointAnnotation = (MKPointAnnotation*)view.annotation; |
|
curLocate = pointAnnotation.tempLocate; |
|
if(!curLocate){ |
|
return; |
|
} |
|
}else{ |
|
return; |
|
} |
|
//自定义动画 |
|
view.transform = CGAffineTransformScale(view.transform, 0.5, 0.5); |
|
// 弹簧动画,参数分别为:时长,延时,弹性(越小弹性越大),初始速度 |
|
[UIView animateWithDuration:1 delay:0 usingSpringWithDamping:0.3 initialSpringVelocity:0.3 options:UIViewAnimationOptionLayoutSubviews animations:^{ |
|
view.transform = CGAffineTransformScale(view.transform, 2, 2); |
|
} completion:^(BOOL finished) { |
|
|
|
}]; |
|
|
|
// 设备点 |
|
self.locationTypeLabel.text = curLocate.loc_type; |
|
if ([curLocate.loc_type isEqualToString:@"LBS"]) { |
|
self.locationTypeImageView.image = [UIImage imageNamed:@"icon_base_station"]; |
|
}else if([curLocate.loc_type isEqualToString:@"GPS"]){ |
|
self.locationTypeImageView.image = [UIImage imageNamed:@"icon_positioning_g"]; |
|
}else if([curLocate.loc_type isEqualToString:@"WIFI"]){ |
|
self.locationTypeImageView.image = [UIImage imageNamed:@"icon_wifi"]; |
|
}else{ |
|
//是空的 LBS |
|
self.locationTypeImageView.image = [UIImage imageNamed:@"icon_base_station"]; |
|
self.locationTypeLabel.text = @"LBS"; |
|
} |
|
if(curLocate.address.length > 0){ |
|
NSString *dateStr = [myHelper getDateFormatWithStr:@"HH:mm" |
|
date:[NSDate dateWithTimeIntervalSince1970:[curLocate.timestamp doubleValue]]]; |
|
NSMutableAttributedString *att = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@ %@",curLocate.address,dateStr]]; |
|
[att addAttribute:NSForegroundColorAttributeName |
|
value:mainColor |
|
range:NSMakeRange(att.length-dateStr.length,dateStr.length)]; |
|
self.locationAddressLabel.attributedText = att; |
|
}else{ |
|
AMapReGeocodeSearchRequest *regeo = [[AMapReGeocodeSearchRequest alloc] init]; |
|
|
|
regeo.location = [AMapGeoPoint locationWithLatitude:[curLocate.lat floatValue] longitude:[curLocate.lon floatValue]]; |
|
[self.searchAPI AMapReGoecodeSearch:regeo]; |
|
} |
|
|
|
|
|
|
|
} |
|
} |
|
|
|
|
|
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay |
|
{ |
|
if ([overlay isKindOfClass:[MKCircle class]]) |
|
{ |
|
MKCircleRenderer *circle = [[MKCircleRenderer alloc] initWithOverlay:overlay]; |
|
circle.strokeColor = [UIColor clearColor]; |
|
|
|
circle.fillColor = [[UIColor blueColor] colorWithAlphaComponent:0.1]; |
|
|
|
// circle.lineWidth = 1; |
|
return circle; |
|
} |
|
if (overlay == self.routeLine) { |
|
if(nil == self.routeLineView) { |
|
self.routeLineView = [[MKPolylineRenderer alloc] initWithPolyline:self.routeLine] ; |
|
//虚线=== |
|
//self.routeLineView.lineDashPhase = 10; |
|
//NSArray* array = [NSArray arrayWithObjects:[NSNumber numberWithInt:15] , [NSNumber numberWithInt:15],nil]; // 2 |
|
//self.routeLineView.lineDashPattern = array; // 3 |
|
//==== |
|
self.routeLineView.fillColor = mainColor; |
|
self.routeLineView.strokeColor = mainColor; |
|
self.routeLineView.lineWidth = 4; |
|
} |
|
return self.routeLineView; |
|
} |
|
|
|
return nil; |
|
} |
|
|
|
#pragma mark ====高德地图位置解析回调==== |
|
|
|
-(void)onReGeocodeSearchDone:(AMapReGeocodeSearchRequest *)request response:(AMapReGeocodeSearchResponse *)response{ |
|
if (response.regeocode != nil) |
|
{ |
|
NSString *name = response.regeocode.formattedAddress; |
|
//解析response获取地址描述,具体解析见 Demo |
|
|
|
// //获取市 |
|
// NSString *city = response.regeocode.addressComponent.city; |
|
// NSString *province = response.regeocode.addressComponent.province; |
|
// if(city && city.length>0){ |
|
// name = [name componentsSeparatedByString:city].lastObject; |
|
// if([city isEqualToString:province]){ |
|
// //直辖市 |
|
// name = [NSString stringWithFormat:@"%@ %@",name,city]; |
|
// }else{ |
|
// //普通市 |
|
// name = [NSString stringWithFormat:@"%@ %@ %@",name,city,province]; |
|
// } |
|
// } |
|
|
|
|
|
NSString *dateStr = [myHelper getDateFormatWithStr:@"HH:mm" |
|
date:[NSDate dateWithTimeIntervalSince1970:[curLocate.timestamp doubleValue]]]; |
|
NSMutableAttributedString *att = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@ %@",name,dateStr]]; |
|
[att addAttribute:NSForegroundColorAttributeName |
|
value:mainColor |
|
range:NSMakeRange(att.length-dateStr.length,dateStr.length)]; |
|
self.locationAddressLabel.attributedText = att; |
|
} |
|
} |
|
|
|
- (void)didReceiveMemoryWarning { |
|
[super didReceiveMemoryWarning]; |
|
// Dispose of any resources that can be recreated. |
|
switch (self.mapView.mapType) { |
|
case MKMapTypeHybrid: |
|
{ |
|
self.mapView.mapType = MKMapTypeStandard; |
|
} |
|
|
|
break; |
|
case MKMapTypeStandard: |
|
{ |
|
self.mapView.mapType = MKMapTypeHybrid; |
|
} |
|
|
|
break; |
|
default: |
|
break; |
|
} |
|
self.mapView.mapType = MKMapTypeStandard; |
|
} |
|
|
|
-(void)dealloc{ |
|
xLog(@"销毁 历史轨迹展示页面") |
|
|
|
} |
|
@end
|
|
|