天天看點

iOS LBS地圖服務之高德地圖根據背景傳回的經緯度點繪制規劃線路圖

需求

項目中有這樣的需求,開啟app背景定位和連續定位,将定位的資料傳遞給背景,然後相關主管人員可以檢視公司人員的日常活動軌迹,展示在地圖上

高德地圖的內建和實作

高德地圖的內建和實作:github的連結

效果圖

iOS LBS地圖服務之高德地圖根據背景傳回的經緯度點繪制規劃線路圖

主要代碼

iOS LBS地圖服務之高德地圖根據背景傳回的經緯度點繪制規劃線路圖
#Boundle Id 
com.star.star           
開啟背景定位
iOS LBS地圖服務之高德地圖根據背景傳回的經緯度點繪制規劃線路圖
//
//  AppDelegate.m
//  GDMap
//
//  Created by zhouyu on 2018/1/22.
//  Copyright © 2018年 zhouyu. All rights reserved.
//

#import "AppDelegate.h"
#import "ViewController.h"
#import "ViewController2.h"
#import <MAMapKit/MAMapKit.h>
#import <AMapFoundationKit/AMapFoundationKit.h>

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // 高德地圖注冊
    [AMapServices sharedServices].apiKey = @"95eea26d4b4e6be311730672da6d49b4";

    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[ViewController2 alloc] init]];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

@end           
//
//  ViewController.m
//  GDMap
//
//  Created by zhouyu on 2018/1/22.
//  Copyright © 2018年 zhouyu. All rights reserved.
//

#import "ViewController.h"
#import <MAMapKit/MAMapKit.h>
#import "CustomAnnotationView.h"
#import <AMapLocationKit/AMapLocationKit.h>

#define kCalloutViewMargin -8

@interface ViewController ()<MAMapViewDelegate,AMapLocationManagerDelegate>
/*
 *
 */
@property (nonatomic, strong) MAMapView *mapView;

@property (nonatomic, strong) NSArray *distanceArray;
@property (nonatomic, assign) double sumDistance;

///全軌迹overlay
@property (nonatomic, strong) MAPolyline *fullTraceLine;
///走過軌迹的overlay
@property (nonatomic, strong) MAPolyline *passedTraceLine;
@property (nonatomic, assign) int passedTraceCoordIndex;

//定位的點經緯度和自定義視圖routeAnno
@property (nonatomic, copy) NSMutableArray *coordsArrM;
@property (nonatomic, copy) NSMutableArray *routeAnnoArrM;

//定位資料管理
@property (nonatomic, strong) AMapLocationManager *locationManager;
/**
 *  背景定位是否傳回逆地理資訊,預設NO。
 */
@property (nonatomic, assign) BOOL locatingWithReGeocode;

// 定位
@property (nonatomic, strong) UIButton             *locationBtn;
// 使用者自定義大頭針
@property (nonatomic, strong) UIImage              *imageLocated;
@property (nonatomic, strong) UIImage              *imageNotLocate;

///車頭方向跟随轉動
@property (nonatomic, strong) MAAnimatedAnnotation *car1;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"地圖定位";

    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(cameraDegreeZeroAction)];

    ///地圖需要v4.5.0及以上版本才必須要打開此選項(v4.5.0以下版本,需要手動配置info.plist)
    [AMapServices sharedServices].enableHTTPS = YES;

    ///把地圖添加至view
    [self.view addSubview:self.mapView];
    self.mapView.delegate = self;

    [self initCoords];

    [self initToolBar];

    [self initZoom];

    [self initLocation];

    [self initCurrentPoint];
}

//旋轉成3D立體地圖是複位成3D平面模式
- (void)cameraDegreeZeroAction{
    self.mapView.cameraDegree = 0;
}

//彈出層自适應螢幕位置
- (void)mapView:(MAMapView *)mapView didSelectAnnotationView:(MAAnnotationView *)view{
    /* Adjust the map center in order to show the callout view completely. */
    if ([view isKindOfClass:[CustomAnnotationView class]]) {
        CustomAnnotationView *cusView = (CustomAnnotationView *)view;
        CGRect frame = [cusView convertRect:cusView.calloutView.frame toView:self.mapView];
        frame = UIEdgeInsetsInsetRect(frame, UIEdgeInsetsMake(kCalloutViewMargin, kCalloutViewMargin, kCalloutViewMargin, kCalloutViewMargin));

        if (!CGRectContainsRect(self.mapView.frame, frame)){
            /* Calculate the offset to make the callout view show up. */
            CGSize offset = [self offsetToContainRect:frame inRect:self.mapView.frame];
            CGPoint theCenter = self.mapView.center;
            theCenter = CGPointMake(theCenter.x - offset.width, theCenter.y - offset.height);
            CLLocationCoordinate2D coordinate = [self.mapView convertPoint:theCenter toCoordinateFromView:self.mapView];
            [self.mapView setCenterCoordinate:coordinate animated:YES];
        }
    }
}
- (CGSize)offsetToContainRect:(CGRect)innerRect inRect:(CGRect)outerRect{
    CGFloat nudgeRight = fmaxf(0, CGRectGetMinX(outerRect) - (CGRectGetMinX(innerRect)));
    CGFloat nudgeLeft = fminf(0, CGRectGetMaxX(outerRect) - (CGRectGetMaxX(innerRect)));
    CGFloat nudgeTop = fmaxf(0, CGRectGetMinY(outerRect) - (CGRectGetMinY(innerRect)));
    CGFloat nudgeBottom = fminf(0, CGRectGetMaxY(outerRect) - (CGRectGetMaxY(innerRect)));
    return CGSizeMake(nudgeLeft ?: nudgeRight, nudgeTop ?: nudgeBottom);
}

//初始化經緯度資料--需要重背景擷取
- (void)initCoords{

    int count = (int)self.coordsArrM.count;
    double sum = 0;
    NSMutableArray *arr = [NSMutableArray arrayWithCapacity:count];

    for (int i = 0;  i < count - 1; ++i) {
        NSArray *beginArr = self.coordsArrM[i];
        NSArray *endArr = self.coordsArrM[i+1];
        CLLocation *begin = [[CLLocation alloc] initWithLatitude:[beginArr[0] doubleValue]  longitude:[beginArr[1] doubleValue]];
        CLLocation *end = [[CLLocation alloc] initWithLatitude:[endArr[0] doubleValue]  longitude:[endArr[1] doubleValue]];
        CLLocationDistance distance = [end distanceFromLocation:begin];
        [arr addObject:[NSNumber numberWithDouble:distance]];
        sum += distance;
    }

    self.distanceArray = arr;
    self.sumDistance = sum;
}

#pragma mark - Map Delegate
- (MAAnnotationView *)mapView:(MAMapView *)mapView viewForAnnotation:(id<MAAnnotation>)annotation{
   if([annotation isKindOfClass:[MAPointAnnotation class]]) {
        NSString *pointReuseIndetifier = @"pointReuseIndetifier3";
        CustomAnnotationView *annotationView = (CustomAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:pointReuseIndetifier];
        if (annotationView == nil) {
            annotationView = [[CustomAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pointReuseIndetifier];
            // 設定為NO,用以調用自定義的calloutView
            annotationView.canShowCallout = NO;
            // 設定中心點偏移,使得标注底部中間點成為經緯度對應點
//            annotationView.centerOffset = CGPointMake(0, -18);
        }
        //更換展示的圖示
        if ([annotation.title isEqualToString:@"route"]) {
            annotationView.enabled = YES;
            annotationView.image = [UIImage imageNamed:@"trackingPoints"];
        } else if ([annotation.title isEqualToString:@"begin"]) {
           annotationView.image = [UIImage imageNamed:@"startPoint"];
       } else if ([annotation.title isEqualToString:@"end"]) {
           annotationView.image = [UIImage imageNamed:@"endPoint"];
       } else {
//           NSLog(@"%@",annotation);//目前定位點
           annotationView.image = [UIImage imageNamed:@"locate"];
       }
        return annotationView;
   }
    return nil;
}

//修改軌迹線寬顔色等
- (MAPolylineRenderer *)mapView:(MAMapView *)mapView rendererForOverlay:(id<MAOverlay>)overlay {
    if(overlay == self.fullTraceLine) {
        MAPolylineRenderer *polylineView = [[MAPolylineRenderer alloc] initWithPolyline:overlay];
        polylineView.lineWidth   = 2.f;
        polylineView.strokeColor = [UIColor colorWithRed:0 green:0.47 blue:1.0 alpha:0.9];
        return polylineView;
    }
    return nil;
}

//初始化軌迹線路
- (void)mapInitComplete:(MAMapView *)mapView {
    int count = (int)self.coordsArrM.count;

    CLLocationCoordinate2D *pCoords = malloc(sizeof(CLLocationCoordinate2D) * self.coordsArrM.count);
    if(!pCoords) {
        return;
    }

    for(int i = 0; i < self.coordsArrM.count; ++i) {
        NSArray *arr = [self.coordsArrM objectAtIndex:i];
        CLLocationCoordinate2D *pCur = pCoords + i;
        pCur->latitude = [arr[0] doubleValue];
        pCur->longitude = [arr[1] doubleValue];
    }

    self.fullTraceLine = [MAPolyline polylineWithCoordinates:pCoords count:count];
    [self.mapView addOverlay:self.fullTraceLine];

    NSMutableArray * routeAnno = [NSMutableArray array];
    for (int i = 0 ; i < count; i++) {
        NSArray *arr = self.coordsArrM[i];
        MAPointAnnotation * a = [[MAPointAnnotation alloc] init];
        a.coordinate = CLLocationCoordinate2DMake([arr[0] doubleValue], [arr[1] doubleValue]);
        if (i == 0) {
            a.title = @"begin";
        } else if(i == self.coordsArrM.count - 1) {
            a.title = @"end";
        } else {
            a.title = @"route";
        }

        a.subtitle = [NSString stringWithFormat:@"%d+%@", i, [self getCurrentTimes]];
        [routeAnno addObject:a];
    }
    [self.routeAnnoArrM addObjectsFromArray:routeAnno];
    [self.mapView addAnnotations:self.routeAnnoArrM];
    [self.mapView showAnnotations:self.routeAnnoArrM animated:YES];
}

//擷取目前的時間
- (NSString*)getCurrentTimes{
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"];
    NSDate *datenow = [NSDate date];
    NSString *currentTimeString = [formatter stringFromDate:datenow];
    return currentTimeString;
}

// MARK: 定位--到目前位置
- (void)actionLocation{
    if (self.mapView.userTrackingMode == MAUserTrackingModeFollow){
        [self.mapView setUserTrackingMode:MAUserTrackingModeNone animated:YES];
    } else {
        [self.mapView setCenterCoordinate:self.mapView.userLocation.coordinate animated:YES];

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
            // 因為下面這句的動畫有bug,是以要延遲0.5s執行,動畫由上一句産生
            [self.mapView setUserTrackingMode:MAUserTrackingModeFollow animated:YES];
        });
    }
}

#pragma mark - 背景定位和持續定位。
- (void)initCurrentPoint{
    self.imageLocated = [UIImage imageNamed:@"gpssearchbutton"];
    self.imageNotLocate = [UIImage imageNamed:@"gpsnormal"];
    self.locationBtn = [[UIButton alloc] initWithFrame:CGRectMake(10, CGRectGetHeight(self.mapView.bounds) - 20, 32, 32)];
    self.locationBtn.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
    self.locationBtn.backgroundColor = [UIColor whiteColor];

    self.locationBtn.layer.cornerRadius = 3;
    [self.locationBtn addTarget:self action:@selector(actionLocation) forControlEvents:UIControlEventTouchUpInside];
    [self.locationBtn setImage:self.imageNotLocate forState:UIControlStateNormal];

    [self.view addSubview:self.locationBtn];
}

#pragma mark - 背景定位和持續定位。
- (void)initLocation{
    self.locationManager = [[AMapLocationManager alloc] init];
    self.locationManager.delegate = self;//遵守代理,實作協定
    //設定定位最小更新距離方法如下,機關米。當兩次定位距離滿足設定的最小更新距離時,SDK會傳回符合要求的定位結果。
    self.locationManager.distanceFilter = 0.1;

    //開啟持續定位
    //iOS 9(不包含iOS 9) 之前設定允許背景定位參數,保持不會被系統挂起
    [self.locationManager setPausesLocationUpdatesAutomatically:NO];

    //iOS 9(包含iOS 9)之後新特性:将允許出現這種場景,同一app中多個locationmanager:一些隻能在前台定位,另一些可在背景定位,并可随時禁止其背景定位。
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9) {
        self.locationManager.allowsBackgroundLocationUpdates = YES;
    }

    //    如果需要持續定位傳回逆地理編碼資訊
    [self.locationManager setLocatingWithReGeocode:YES];
    //開始持續定位
    [self.locationManager startUpdatingLocation];
}
#pragma mark - 在回調函數中,擷取定位坐标,進行業務處理--傳遞給背景。
- (void)amapLocationManager:(AMapLocationManager *)manager didUpdateLocation:(CLLocation *)location reGeocode:(AMapLocationReGeocode *)reGeocode{
    NSLog(@"location:{緯度lat:%f; 經度lon:%f; accuracy:%f}", location.coordinate.latitude, location.coordinate.longitude, location.horizontalAccuracy);
    if (reGeocode){
        NSLog(@"reGeocode:%@", reGeocode);

        //實時定位,實時繪制定位點連線
//        [self.mapView removeAnnotations:self.routeAnnoArrM];
//        [self.mapView removeOverlay:self.fullTraceLine];
//        [self.coordsArrM addObject:@[@(location.coordinate.latitude),@(location.coordinate.longitude)]];
//
//        int count = (int)self.coordsArrM.count;
//
//        CLLocationCoordinate2D *pCoords = malloc(sizeof(CLLocationCoordinate2D) * self.coordsArrM.count);
//        if(!pCoords) {
//            return;
//        }
//
//        for(int i = 0; i < self.coordsArrM.count; ++i) {
//            NSArray *arr = [self.coordsArrM objectAtIndex:i];
//            CLLocationCoordinate2D *pCur = pCoords + i;
//            pCur->latitude = [arr[0] doubleValue];
//            pCur->longitude = [arr[1] doubleValue];
//        }
//
//        self.fullTraceLine = [MAPolyline polylineWithCoordinates:pCoords count:count];
//        [self.mapView addOverlay:self.fullTraceLine];
//
//        MAPointAnnotation * a = [[MAPointAnnotation alloc] init];
//        a.coordinate = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude);
//        a.title = @"route";
//        a.subtitle = [NSString stringWithFormat:@"+%@", [self getCurrentTimes]];
//
//        [self.routeAnnoArrM addObject:a];
//        [self.mapView addAnnotations:self.routeAnnoArrM];
//        [self.mapView showAnnotations:self.routeAnnoArrM animated:YES];
    }
}

//切換地圖顯示級别
- (void)initZoom{
    UIView *zoomPannelView = [self makeZoomPannelView];
    zoomPannelView.center = CGPointMake(self.view.bounds.size.width -  CGRectGetMidX(zoomPannelView.bounds) - 10,
                                        self.view.bounds.size.height -  CGRectGetMidY(zoomPannelView.bounds) - 50);

    [self.view addSubview:zoomPannelView];
}
- (UIView *)makeZoomPannelView{
    UIView *ret = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 53, 98)];

    UIButton *incBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 53, 49)];
    [incBtn setImage:[UIImage imageNamed:@"increase"] forState:UIControlStateNormal];
    [incBtn sizeToFit];
    [incBtn addTarget:self action:@selector(zoomPlusAction) forControlEvents:UIControlEventTouchUpInside];

    UIButton *decBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 49, 53, 49)];
    [decBtn setImage:[UIImage imageNamed:@"decrease"] forState:UIControlStateNormal];
    [decBtn sizeToFit];
    [decBtn addTarget:self action:@selector(zoomMinusAction) forControlEvents:UIControlEventTouchUpInside];

    [ret addSubview:incBtn];
    [ret addSubview:decBtn];
    return ret;
}
//放大
- (void)zoomPlusAction{
    CGFloat oldZoom = self.mapView.zoomLevel;
    [self.mapView setZoomLevel:(oldZoom + 1) animated:YES];
}
//縮小
- (void)zoomMinusAction{
    CGFloat oldZoom = self.mapView.zoomLevel;
    [self.mapView setZoomLevel:(oldZoom - 1) animated:YES];
}

//切換地圖類型
- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    self.navigationController.toolbar.barStyle      = UIBarStyleBlack;
    self.navigationController.toolbar.translucent   = YES;
    [self.navigationController setToolbarHidden:NO animated:animated];
}

//切換地圖類型
- (void)mapTypeAction:(UISegmentedControl *)segmentedControl{
    self.mapView.mapType = segmentedControl.selectedSegmentIndex;
}
#pragma mark - 切換地圖類型
- (void)initToolBar{
    UIBarButtonItem *flexbleItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
    UISegmentedControl *mapTypeSegmentedControl = [[UISegmentedControl alloc] initWithItems: [NSArray arrayWithObjects:@"标準(Standard)",@"衛星(Satellite)",nil]];
    mapTypeSegmentedControl.selectedSegmentIndex  = self.mapView.mapType;
    [mapTypeSegmentedControl addTarget:self action:@selector(mapTypeAction:) forControlEvents:UIControlEventValueChanged];
    UIBarButtonItem *mayTypeItem = [[UIBarButtonItem alloc] initWithCustomView:mapTypeSegmentedControl];
    self.toolbarItems = [NSArray arrayWithObjects:flexbleItem, mayTypeItem, flexbleItem, nil];
}

#pragma mark 來加載
- (MAMapView *)mapView{
    if (_mapView == nil) {
        ///初始化地圖
        MAMapView *mapView = [[MAMapView alloc] initWithFrame:CGRectMake(0, 44 + [UIApplication sharedApplication].statusBarFrame.size.height, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - 44 - [UIApplication sharedApplication].statusBarFrame.size.height)];
        _mapView = mapView;

        ///如果您需要進入地圖就顯示定位小藍點,則需要下面兩行代碼
        mapView.showsUserLocation = YES;
        mapView.userTrackingMode = MAUserTrackingModeFollow;

        mapView.showsCompass= YES; // 設定成NO表示關閉指南針;YES表示顯示指南針
        mapView.compassOrigin= CGPointMake(mapView.compassOrigin.x, 10); //設定指南針位置

        mapView.showsScale= YES;  //設定成NO表示不顯示比例尺;YES表示顯示比例尺
        mapView.scaleOrigin= CGPointMake(mapView.scaleOrigin.x - 60, 5);  //設定比例尺位置

        [mapView setZoomLevel:15.5 animated:YES];
        mapView.maxZoomLevel = 19;

        //手勢事件
        mapView.scrollEnabled = YES;
        mapView.zoomEnabled = YES;
        mapView.rotateEnabled = YES;
        mapView.skyModelEnable = YES;

        // 背景持續定位 iOS9以上系統必須配置
        // 左側目錄中選中工程名,開啟 TARGETS->Capabilities->Background Modes 在 Background Modes中勾選 Location updates
        mapView.allowsBackgroundLocationUpdates = YES;
    }
    return _mapView;
}

- (NSMutableArray *)routeAnnoArrM{
    if (_routeAnnoArrM == nil) {
        _routeAnnoArrM = [[NSMutableArray alloc] init];
    }
    return _routeAnnoArrM;
}

- (NSMutableArray *)coordsArrM{
    if (_coordsArrM == nil) {
        _coordsArrM = [@[
            @[@(39.97617053371078), @(116.3499049793749)],
            @[@(39.97619854213431), @( 116.34978804908442)],
            @[@(39.97623045687959), @( 116.349674596623)],
            @[@(39.976260803938594), @(116.34918981582413)],
            @[@(39.97623535890678), @(116.34906721558868)],
            @[@(39.976214717128855), @(116.34895185151584)],
            @[@(39.976280148755315), @(116.34886935936889)],
            @[@(39.97628182112874), @(116.34873954611332)],
            @[@(39.97626038855863), @(116.34860763527448)],
            @[@(39.97655231226543), @(116.34827643560175)],
            @[@(39.976658372925556), @(116.34824186261169)],
            @[@(39.9767570732376), @(116.34825080406188)],
            @[@(39.976869087779995), @(116.34825631960626)],
            @[@(39.97698451764595), @(116.34822111635201)],
            @[@(39.977079745909876), @(116.34822901510276)],
            @[@(39.97786190186833), @(116.3482045955917)],
            @[@(39.977958856930286), @(116.34822159449203)],
            @[@(39.97807288885813), @(116.3482256370537)],
            @[@(39.978170063673524), @(116.3482098441266)],
            @[@(39.978266951404066), @(116.34819564465377)],
            @[@(39.978380693859116), @(116.34820541974412)],
            @[@(39.97848741209275), @(116.34819672351216)],
            @[@(39.978593409607825), @(116.34816588867105)],
            @[@(39.97870216883567), @(116.34818489339459)],
            @[@(39.979308267469264), @(116.34809495907906)],
            @[@(39.97939658036473), @(116.34805113358091)],
            @[@(39.979491697188685), @(116.3480310509613)],
            @[@(39.979588529006875), @(116.3480082124968)],
            @[@(39.979685789111635), @(116.34799530586834)],
            @[@(39.979801430587926), @(116.34798818413954)],
            @[@(39.97990758587515), @(116.3479996420353)],
            @[@(39.980000796262615), @(116.34798697544538)],
            @[@(39.980116318796085), @(116.3479912988137)],
            @[@(39.98021407403913), @(116.34799204219203)],
            @[@(39.980325006125696), @(116.34798535084123)],
            @[@(39.98098214824056), @(116.3478962642899)],
            @[@(39.98108306010269), @(116.34782449883967)],
            @[@(39.98115277119176), @(116.34774758827285)],
            @[@(39.98115430642997), @(116.34761476652932)],
            @[@(39.98114590845294), @(116.34749135408349)],
            @[@(39.98114337322547), @(116.34734772765582)],
            @[@(39.98115066909245), @(116.34722082902628)],
            @[@(39.98112495260716), @(116.34658043260109)],
            @[@(39.9811107163792), @(116.34643721418927)],
            @[@(39.981085081075676), @(116.34631638374302)],
            @[@(39.981052294975264), @(116.34537348820508)],
            @[@(39.980956549928244), @(116.3453513775533)],
        ] mutableCopy];
    }
    return _coordsArrM;
}
@end