折线类为 MAPolyline,由一组经纬度坐标组成,并以有序序列形式建立一系列的线段。iOS SDK支持在3D矢量地图上绘制带箭头或有纹理等样式的折线,同时可设置折线端点和连接点的类型,以满足各种绘制线的场景。
思路:我们创建两个数组,其中一个存放用户位置的经度,一个存放用户位置的纬度,且数组长度均为2,在初始化的时候,我们将数组中的两个元素均存为初始时的经纬度,然后创建一个定时器,定时器每一秒执行一次事件函数,时间函数中先更新经纬度数组中第二个元素的内容为当前用户位置的经纬度,然后进行一次折线的绘制,循环往复即可绘制出用户一直运动时的折线轨迹。
代码如下:
viewController.h中:
#import <UIKit/UIKit.h>
#import <MAMapKit/MAMapKit.h>
#import <AMapFoundationKit/AMapFoundationKit.h>
@interface ViewController : UIViewController
//地图的属性
@property (nonatomic, strong) MAMapView *mapView;
//显示用户当前位置的属性
@property (nonatomic, strong) MAUserLocationRepresentation *r;
//下方为自己编写的绘制轨迹图的相关属性
//包含近两次用户当前纬度位置的数组
@property (nonatomic, strong) NSMutableArray *contentArray;
//包含近两次用户当前经度位置的数组
@property (nonatomic, strong) NSMutableArray *contentArraySecond;
//刷新绘制路线的计时器
@property (nonatomic, retain) NSTimer *timerTest;
@property (nonatomic, assign) NSInteger flag;
@end
viewController.m中:
#import "ViewController.h"
//#import
//#import
#define width [UIScreen mainScreen].bounds.size.width
#define height [UIScreen mainScreen].bounds.size.height
/*
//以下为绘制运动轨迹的设定
//绘制间隔
#define huizhiTimes 0.02
//居中点的个数
#define IntheMiddlePoint 2
//每次画线跳跃几个点
#define jumpPoints 3
*/
@interface ViewController () <MAMapViewDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//初始化flag
_flag = 0;
//检查隐私合规
[MAMapView updatePrivacyShow:AMapPrivacyShowStatusDidShow privacyInfo:AMapPrivacyInfoStatusDidContain];
[MAMapView updatePrivacyAgree:AMapPrivacyAgreeStatusDidAgree];
///地图需要v4.5.0及以上版本才必须要打开此选项(v4.5.0以下版本,需要手动配置info.plist)
[AMapServices sharedServices].enableHTTPS = YES;
//初始化地图
_mapView = [[MAMapView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
//地图跟着位置移动
[_mapView setUserTrackingMode:MAUserTrackingModeFollow animated:YES];
_mapView.delegate = self;
_mapView.showsIndoorMap = YES; //YES:显示室内地图;NO:不显示;
//把地图添加至view
[self.view addSubview:_mapView];
///如果您需要进入地图就显示定位小蓝点,则需要下面两行代码
_mapView.showsUserLocation = YES;
_mapView.userTrackingMode = MAUserTrackingModeFollow;
//初始化MAUserLocationRepresentation 对象
_r = [[MAUserLocationRepresentation alloc] init];
_r.showsAccuracyRing = NO;///精度圈是否显示,默认YES
///执行
[_mapView updateUserLocationRepresentation:_r];
//初始化包含当前用户近两次位置的数组
_contentArray = [[NSMutableArray alloc] init];
_contentArraySecond = [[NSMutableArray alloc] init];
NSNumber *latitudeTest = [NSNumber numberWithDouble:_mapView.userLocation.coordinate.latitude];
NSNumber *longitudeTest = [NSNumber numberWithDouble:_mapView.userLocation.coordinate.longitude];
for (int i = 0; i < 2; ++i) {
[_contentArray addObject:latitudeTest];
[_contentArraySecond addObject:longitudeTest];
}
//初始化定时器
_timerTest = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(createLine) userInfo:nil repeats:YES];
//将定时器添加到runloop
[[NSRunLoop currentRunLoop] addTimer:_timerTest forMode:NSDefaultRunLoopMode];
}
//设置折线样式(此为所签订地图协议的回调函数)(该函数中可以设定折线的相关属性,如添加图片等)
- (MAOverlayRenderer *)mapView:(MAMapView *)mapView rendererForOverlay:(id <MAOverlay>)overlay
{
if ([overlay isKindOfClass:[MAPolyline class]])
{
MAPolylineRenderer *polylineRenderer = [[MAPolylineRenderer alloc] initWithPolyline:overlay];
polylineRenderer.lineWidth = 8.f;
polylineRenderer.strokeColor = [UIColor colorWithRed:0 green:1 blue:0 alpha:0.6];
polylineRenderer.lineJoinType = kMALineJoinRound;
polylineRenderer.lineCapType = kMALineCapRound;
return polylineRenderer;
}
return nil;
}
//以下为自己编写的绘制折线轨迹图
- (void) createLine {
//运动秒数加一
++_timeLongTest;
//刷新经纬度数组的数据
[self.contentArray removeObjectAtIndex:0];
[self.contentArraySecond removeObjectAtIndex:0];
NSNumber *latitudeTest = [NSNumber numberWithDouble:_mapView.userLocation.coordinate.latitude];
NSNumber *longitudeTest = [NSNumber numberWithDouble:_mapView.userLocation.coordinate.longitude];
//将新的经纬度添加到数组中
[self.contentArray addObject:latitudeTest];
[self.contentArraySecond addObject:longitudeTest];
//下方为绘制折线的代码(添加两个点,然后在两个点间绘制折线(也可以添加多个点,在多个点中绘制折线))
CLLocationCoordinate2D commonPolylineCoords[2];
//添加的第一个经纬度点
commonPolylineCoords[0].latitude = [self.contentArray[0] doubleValue];
commonPolylineCoords[0].longitude = [self.contentArraySecond[0] doubleValue];
//添加的第二个经纬度点
commonPolylineCoords[1].latitude = [self.contentArray[1] doubleValue];
commonPolylineCoords[1].longitude = [self.contentArraySecond[1] doubleValue];
//构造折线对象
MAPolyline *commonPolyline = [MAPolyline polylineWithCoordinates:commonPolylineCoords count:2];
//在地图上添加折线对象
[_mapView addOverlay:commonPolyline];
//由于第一次的经纬度可能会由于服务器等原因导致误差较大,所以将第一次绘制的折线remove掉
if (_flag == 0) {
[_mapView removeOverlay:commonPolyline];
++_flag;
}
}
@end
以上就是绘制折线的全部操作了
实际效果如下图:
iOS 地图 SDK中的 MAGeometry.h 中提供了很多计算方法,包括:计算两点距离、矩形面积、坐标转换、判断点是否圆或者多边形内等等,下面做简单介绍,具体的接口可参考:MAGeometry.h。
1、两点间的直线距离计算
根据用户指定的两个经纬度坐标点,计算这两个点间的直线距离,单位为米。代码如下:
//1.将两个经纬度点转成投影点
MAMapPoint point1 = MAMapPointForCoordinate(CLLocationCoordinate2DMake(39.989612,116.480972));
MAMapPoint point2 = MAMapPointForCoordinate(CLLocationCoordinate2DMake(39.990347,116.480441));
//2.计算距离
CLLocationDistance distance = MAMetersBetweenMapPoints(point1,point2);
其中,我们利用刚才绘制折线的定时器,每一秒计算数组中两个经纬度点之间的距离,然后添加到总距离的属性中即可得到实时变化的总距离。
我们利用定时器一秒调用一次事件函数的特性,初始化一个总时间属性,初始化其为0,每调用一次定时器事件函数就将其值加一,即可得到实时变化的总运动时长了。
在定时器的时间函数中,我们利用刚才获得的总运动距离和总运动时长,用总时长除以总运动公里数,即可得到实时运动时的每公里的配速了。
代码如下:
viewController.h中:
#import <UIKit/UIKit.h>
#import <MAMapKit/MAMapKit.h>
#import <AMapFoundationKit/AMapFoundationKit.h>
@interface ViewController : UIViewController
//地图的属性
@property (nonatomic, strong) MAMapView *mapView;
//显示运动里程时间等信息的视图
@property (nonatomic, strong) UIView *underView;
//显示配速的label
@property (nonatomic, strong) UILabel *labelSpeed;
//显示时间的label
@property (nonatomic, strong) UILabel *labelTime;
//显示距离的label
@property (nonatomic, strong) UILabel *labelKm;
//显示时间内容的label
@property (nonatomic, strong) UILabel *labelTimeContent;
//显示距离内容的label
@property (nonatomic, strong) UILabel *labelKmContent;
//显示配速内容的label
@property (nonatomic, strong) UILabel *labelSpeedContent;
//显示用户当前位置的属性
@property (nonatomic, strong) MAUserLocationRepresentation *r;
//下方为自己编写的绘制轨迹图的相关属性
//包含近两次用户当前纬度位置的数组
@property (nonatomic, strong) NSMutableArray *contentArray;
//包含近两次用户当前经度位置的数组
@property (nonatomic, strong) NSMutableArray *contentArraySecond;
//刷新绘制路线的计时器
@property (nonatomic, retain) NSTimer *timerTest;
@property (nonatomic, assign) NSInteger flag;
//记录跑步米数
@property (nonatomic, assign) NSInteger distanceTest;
//记录运动秒数
@property (nonatomic, assign) NSInteger timeLongTest;
@end
viewController.m中:
#import "ViewController.h"
#define width [UIScreen mainScreen].bounds.size.width
#define height [UIScreen mainScreen].bounds.size.height
@interface ViewController () <MAMapViewDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//初始化flag
_flag = 0;
//初始化运动距离(米)
_distanceTest = 0;
//初始化运动时间(秒)
_timeLongTest = 0;
//检查隐私合规
[MAMapView updatePrivacyShow:AMapPrivacyShowStatusDidShow privacyInfo:AMapPrivacyInfoStatusDidContain];
[MAMapView updatePrivacyAgree:AMapPrivacyAgreeStatusDidAgree];
///地图需要v4.5.0及以上版本才必须要打开此选项(v4.5.0以下版本,需要手动配置info.plist)
[AMapServices sharedServices].enableHTTPS = YES;
//初始化地图
_mapView = [[MAMapView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
//地图跟着位置移动
[_mapView setUserTrackingMode:MAUserTrackingModeFollow animated:YES];
_mapView.delegate = self;
_mapView.showsIndoorMap = YES; //YES:显示室内地图;NO:不显示;
//把地图添加至view
[self.view addSubview:_mapView];
///如果您需要进入地图就显示定位小蓝点,则需要下面两行代码
_mapView.showsUserLocation = YES;
_mapView.userTrackingMode = MAUserTrackingModeFollow;
//初始化MAUserLocationRepresentation 对象
_r = [[MAUserLocationRepresentation alloc] init];
_r.showsAccuracyRing = NO;///精度圈是否显示,默认YES
///执行
[_mapView updateUserLocationRepresentation:_r];
//添加显示运动里程的视图
_underView = [[UIView alloc] init];
_underView.backgroundColor = [UIColor whiteColor];
_underView.frame = CGRectMake(width * 0.09345, height * 0.8139, width * 0.8131, height * 0.1620);
_underView.layer.cornerRadius = 10.0;
_underView.layer.masksToBounds = YES;
[_mapView addSubview:_underView];
_labelSpeed = [[UILabel alloc] init];
_labelTime = [[UILabel alloc] init];
_labelKm = [[UILabel alloc] init];
_labelSpeed.text = @"配速";
_labelTime.text = @"时间";
_labelKm.text = @"距离(km)";
_labelSpeed.textColor = [UIColor grayColor];
_labelTime.textColor = [UIColor grayColor];
_labelKm.textColor = [UIColor grayColor];
_labelSpeed.textAlignment = NSTextAlignmentCenter;
_labelTime.textAlignment = NSTextAlignmentCenter;
_labelKm.textAlignment = NSTextAlignmentCenter;
_labelSpeed.frame = CGRectMake(width * 0, height * 0.0216, width * 0.1724, height * 0.0324);
_labelTime.frame = CGRectMake(width * 0.315, height * 0.0216, width * 0.1539, height * 0.0324);
_labelKm.frame = CGRectMake(width * 0.62, height * 0.0216, width * 0.1724, height * 0.0324);
[_underView addSubview:_labelSpeed];
[_underView addSubview:_labelTime];
[_underView addSubview:_labelKm];
//设置显示内容的label
_labelTimeContent = [[UILabel alloc] init];
_labelKmContent = [[UILabel alloc] init];
_labelSpeedContent = [[UILabel alloc] init];
//将米转化为公里
double kmTest = _distanceTest / 1000;
NSString *kmTestString = [NSString stringWithFormat:@"%.2f公里", kmTest];
_labelKmContent.text = kmTestString;
_labelTimeContent.text = @"00:00:00";
_labelSpeedContent.text = @"00‘00’‘";
_labelSpeedContent.textColor = [UIColor greenColor];
_labelTimeContent.textColor = [UIColor greenColor];
_labelKmContent.textColor = [UIColor greenColor];
_labelSpeedContent.textAlignment = NSTextAlignmentCenter;
_labelTimeContent.textAlignment = NSTextAlignmentCenter;
_labelKmContent.textAlignment = NSTextAlignmentCenter;
_labelSpeedContent.frame = CGRectMake(width * 0, height * 0.0816, width * 0.1724, height * 0.0324);
_labelTimeContent.frame = CGRectMake(width * 0.315, height * 0.0816, width * 0.1539, height * 0.0324);
_labelKmContent.frame = CGRectMake(width * 0.62, height * 0.0816, width * 0.1724, height * 0.0324);
[_underView addSubview:_labelSpeedContent];
[_underView addSubview:_labelTimeContent];
[_underView addSubview:_labelKmContent];
//初始化包含当前用户近两次位置的数组
_contentArray = [[NSMutableArray alloc] init];
_contentArraySecond = [[NSMutableArray alloc] init];
NSNumber *latitudeTest = [NSNumber numberWithDouble:_mapView.userLocation.coordinate.latitude];
NSNumber *longitudeTest = [NSNumber numberWithDouble:_mapView.userLocation.coordinate.longitude];
for (int i = 0; i < 2; ++i) {
[_contentArray addObject:latitudeTest];
[_contentArraySecond addObject:longitudeTest];
}
_timerTest = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(createLine) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timerTest forMode:NSDefaultRunLoopMode];
}
//设置折线样式
- (MAOverlayRenderer *)mapView:(MAMapView *)mapView rendererForOverlay:(id <MAOverlay>)overlay
{
if ([overlay isKindOfClass:[MAPolyline class]])
{
MAPolylineRenderer *polylineRenderer = [[MAPolylineRenderer alloc] initWithPolyline:overlay];
polylineRenderer.lineWidth = 8.f;
polylineRenderer.strokeColor = [UIColor colorWithRed:0 green:1 blue:0 alpha:0.6];
polylineRenderer.lineJoinType = kMALineJoinRound;
polylineRenderer.lineCapType = kMALineCapRound;
return polylineRenderer;
}
return nil;
}
//以下为自己编写的绘制折线轨迹图
- (void) createLine {
//运动秒数加一
++_timeLongTest;
//刷新经纬度数组的数据
[self.contentArray removeObjectAtIndex:0];
[self.contentArraySecond removeObjectAtIndex:0];
NSNumber *latitudeTest = [NSNumber numberWithDouble:_mapView.userLocation.coordinate.latitude];
NSNumber *longitudeTest = [NSNumber numberWithDouble:_mapView.userLocation.coordinate.longitude];
[self.contentArray addObject:latitudeTest];
[self.contentArraySecond addObject:longitudeTest];
if (_flag != 0) {
//将两个经纬度点转换成投影点
MAMapPoint pointFirst = MAMapPointForCoordinate(CLLocationCoordinate2DMake([self.contentArray[0] doubleValue], [self.contentArraySecond[0] doubleValue]));
MAMapPoint pointSecond = MAMapPointForCoordinate(CLLocationCoordinate2DMake([self.contentArray[1] doubleValue], [self.contentArraySecond[1] doubleValue]));
//计算距离
CLLocationDistance distance = MAMetersBetweenMapPoints(pointFirst, pointSecond);
//更新总运动距离(米)
_distanceTest += distance;
NSLog(@"%ld", _distanceTest);
}
//将米转化为公里
double kmTest = _distanceTest / 1000.0;
NSLog(@"$$$ %lf", kmTest);
NSString *kmTestString = [NSString stringWithFormat:@"%.2f公里", kmTest];
_labelKmContent.text = kmTestString;
if (kmTest >= 0.01) {
//计算配速
NSInteger speedTest = _timeLongTest / kmTest;
NSInteger speedTestMin = speedTest / 60;
NSInteger speedTestSec = speedTest % 60;
NSString *speedTestText;
if (speedTestMin < 10 && speedTestSec < 10) {
speedTestText = [NSString stringWithFormat:@"0%ld‘0%ld‘’", speedTestMin, speedTestSec];
}
if (speedTestMin < 10 && speedTestSec > 10) {
speedTestText = [NSString stringWithFormat:@"0%ld‘%ld‘’", speedTestMin, speedTestSec];
}
if (speedTestMin >= 10 && speedTestSec < 10) {
speedTestText = [NSString stringWithFormat:@"%ld‘0%ld‘’", speedTestMin, speedTestSec];
}
if (speedTestMin >= 10 && speedTestSec >= 10) {
speedTestText = [NSString stringWithFormat:@"%ld‘%ld‘’", speedTestMin, speedTestSec];
}
_labelSpeedContent.text = speedTestText;
}
//计算时间
NSInteger timeHour = _timeLongTest / 3600;
NSInteger timeMin = (_timeLongTest % 3600) / 60;
NSInteger timeSec = _timeLongTest % 60;
NSString *timeTestText;
if (timeHour < 10 && timeMin < 10 && timeSec < 10) {
timeTestText = [NSString stringWithFormat:@"0%ld:0%ld:0%ld", timeHour, timeMin, timeSec];
}
if (timeHour < 10 && timeMin < 10 && timeSec >= 10) {
timeTestText = [NSString stringWithFormat:@"0%ld:0%ld:%ld", timeHour, timeMin, timeSec];
}
if (timeHour < 10 && timeMin >= 10 && timeSec < 10) {
timeTestText = [NSString stringWithFormat:@"0%ld:%ld:0%ld", timeHour, timeMin, timeSec];
}
if (timeHour >= 10 && timeMin < 10 && timeSec < 10) {
timeTestText = [NSString stringWithFormat:@"%ld:%ld:0%ld", timeHour, timeMin, timeSec];
}
if (timeHour < 10 && timeMin >= 10 && timeSec >= 10) {
timeTestText = [NSString stringWithFormat:@"0%ld:%ld:%ld", timeHour, timeMin, timeSec];
}
if (timeHour >= 10 && timeMin >= 10 && timeSec < 10) {
timeTestText = [NSString stringWithFormat:@"%ld:%ld:0%ld", timeHour, timeMin, timeSec];
}
if (timeHour >= 10 && timeMin < 10 && timeSec >= 10) {
timeTestText = [NSString stringWithFormat:@"%ld:0%ld:%ld", timeHour, timeMin, timeSec];
}
if (timeHour >= 10 && timeMin >= 10 && timeSec >= 10) {
timeTestText = [NSString stringWithFormat:@"%ld:%ld:%ld", timeHour, timeMin, timeSec];
}
_labelTimeContent.text = timeTestText;
CLLocationCoordinate2D commonPolylineCoords[2];
commonPolylineCoords[0].latitude = [self.contentArray[0] doubleValue];
commonPolylineCoords[0].longitude = [self.contentArraySecond[0] doubleValue];
commonPolylineCoords[1].latitude = [self.contentArray[1] doubleValue];
commonPolylineCoords[1].longitude = [self.contentArraySecond[1] doubleValue];
//构造折线对象
MAPolyline *commonPolyline = [MAPolyline polylineWithCoordinates:commonPolylineCoords count:2];
//在地图上添加折线对象
[_mapView addOverlay:commonPolyline];
if (_flag == 0) {
[_mapView removeOverlay:commonPolyline];
++_flag;
}
}
@end
以上代码就是我们实现计算并显示运动总时长、距离、配速的全部代码
运行结果如下:
如图所示,我们已经成功计算并显示了运动的总时长、距离、配速。