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.
840 lines
33 KiB
840 lines
33 KiB
1 year ago
|
//
|
||
|
// 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
|