美文网首页iOS 开发 iOS Developer
高德地图SDK实时绘制跑步路径-Swift实现

高德地图SDK实时绘制跑步路径-Swift实现

作者: msq3 | 来源:发表于2016-06-01 12:33 被阅读4091次
    -_-

    跑步运动软件最基本的功能之一,就是对运动中的用户进行实时定位,并绘制出运动路径。本文主要内容,就是用高德的SDK实现简单的路径绘制。(苹果内置的地图用的也是高德,当然,你也可以用百度的。)

    首先,xcode创建一个新的工程,选择Single View Application,语言选择Swift

    1.jpg

    工程创建完毕,下一步是导入高德地图的framework。可以选择用CocoaPods,手动安装方式如下:

    下载高德地图SDK,申请API Key。相关网页
    (撰写本文时高德SDK的最新版本为3.3.0,需要加-Objc,否则相关的接口调用会直接崩溃,Demo使用的版本为3.2.0,不需要加标记,版本之间一些接口和变量会存在差异。)

    <h3>导入SDK</h3>

    左侧目录中选中工程名,在 TARGETS->Build Phases-> Link Binary With Libaries 中点击“+”按钮,在弹出的窗口中点击“Add Other”按钮,选择下载好的 MAMapKit.framework 文件添加到工程中。

    2.jpg

    <h3>引入AMap.bundle资源文件</h3>

    AMap.bundle资源文件中存储了定位、默认大头针标注视图等图片,可利用这些资源图片进行开发。

    左侧目录中选中工程名,在右键菜单中选择Add Files to “工程名”…,从 MAMapKit.framework->Resources 文件夹中选择 AMap.bundle文件,并勾选“Copy items if needed”复选框,单击“Add”按钮,将资源文件添加到工程中。

    (MAMapKit.framework最好放在工程文件下,否则有可能导致编译不过。)

    <h3>引入依赖库</h3>

    左侧目录中选中工程名,在TARGETS->Build Settings-> Link Binary With Libaries中点击“+”按钮,在弹出的窗口中查找并选择所需的库(见下),单击“Add”按钮,将库文件添加到工程中。

    libz.tbd

    libstdc++.6.0.9.tbd

    Security.framework

    SystemConfiguration.framework

    CoreTelephony.framework

    OpenGLES.framework

    CoreLocation.framework

    CoreGraphics.framework

    Foundation.framework

    UIKit.framework

    引入完成后如下图所示:

    3.jpg

    <h3>Info.plist添加属性</h3>

    添加NSLocationAlwaysUsageDescription。(不添加这货的话会导致无法进行定位,被这个问题坑了下。。)

    添加Required background modes,并给它添加App registers for location updates属性,使得应用在进入后台时也能继续接受定位信息,MAMapView有一个属性的设置和这个相关,后续提到。

    App Transport Security Settings添加上Allow Arbitrary Loads,并设为YES,iOS9之后网络请求默认改为https,不修改会报警告

    修改后info.plist如下所示

    4.jpg

    <h2>接下来,开始代码的实现</h2>

    <h3>新建桥接头文件</h3>

    SDK为第三方OC项目,在Swift中调用需要创建桥接头文件。新建头文件,命名为MapPath-Bridging-Header.h,然后设置好对应的路径,如下图:

    5.jpg

    Ps.先设置麻烦此时可以新建一个OC类,这时xcode会自动弹出一个是否创建桥接头文件提示框,选择Create Bridging Header即可自动生成并完成工程的对应设置。

    6.jpg

    文件代码实现如下

    // Create-Bridging-Header.h
    #import <MAMapKit/MAMapKit.h>
    

    <h3>设置ApiKey</h3>

    使用地图需要先设置ApiKey,否则会报ApiKey为空的警告。在项目启动的时候设置,所以在AppDelegatedidFinishLaunchingWithOptions进行处理

    //  AppDelegate.swift
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.
        
        // 设置apiKey
        MAMapServices.sharedServices().apiKey = "e8be3a280e6fc3c2651ebef77e7a0fa5"
        return true
    }
    

    新建一个类RunningViewController,继承自UIViewController,遵守MAMapViewDelegate,该类用来专门展示跑步过程中的地图界面,同时创建RunningViewController.xib文件

    class RunningViewController: UIViewController, MAMapViewDelegate
    

    声明属性

    @IBOutlet weak var mapView: MAMapView!
    var coordinateArray: [CLLocationCoordinate2D] = []
    

    将一个UIView拖到RunningViewController.xib的视图上,设置四边具体父视图的间距为0,并链接上mapView。(懒得写代码添加视图了。)

    coordinateArray数组用来存储每次获取的定位更新数据,绘制路径时需要用到。

    viewDidLoad的时候进行地图初始化的设置

    override func viewDidLoad() {
        super.viewDidLoad()
    
        initMapView()
    }
    
    func initMapView()
    {
        mapView.delegate = self
        mapView.zoomLevel = 15.5
        mapView.distanceFilter = 3.0
        mapView.desiredAccuracy = kCLLocationAccuracyBestForNavigation
    }
    

    将视图控制器设置为mapView的代理,当位置变化更新时通过代理进行回调,绘制路径时也需要通过代理进行属性的设置

    zoomLevel为当前地图缩放的比例

    distanceFilter为定位的最小更新距离,当移动距离超过设定的值时便会有位置的更新回调

    desiredAccuracy为定位精度,默认为最高精度,一般直接用这个就好

    当视图显示后,开始进行定位

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        startLocation()
    }
    
    func startLocation()
    {
        mapView.showsUserLocation = true
        mapView.userTrackingMode = MAUserTrackingMode.Follow
        mapView.pausesLocationUpdatesAutomatically = false
        mapView.allowsBackgroundLocationUpdates = true
    }
    

    showsUserLocation设为true后便开始进行定位,设为false则定位停止。定位本身会比较耗点,应用在不需要定位时记得将定位功能关闭,否则你的手机电量会消耗很快滴。

    userTrackingMode为定位的方式,查看接口可知有三种形式,这里采用跟随用户位置移动的定位方式

    allowsBackgroundLocationUpdates设置为true表示允许进行后台定位,保证之前有设置过App registers for location updates属性,否则会崩溃。

    <h3>代理的实现</h3>

    每次有位置更新变会调用mapView(mapView: MAMapView, didUpdateUserLocation userLocation: MAUserLocation, updatingLocation: Bool)函数

    // MARK: MAMapViewDelegate
    
    func mapView(mapView: MAMapView, didUpdateUserLocation userLocation: MAUserLocation, updatingLocation: Bool)
    {
        // 地图每次有位置更新时的回调
        
        if updatingLocation {
            // 获取新的定位数据
            let coordinate = userLocation.coordinate
            
            // 添加到保存定位点的数组
            self.coordinateArray.append(coordinate)
           
            updatePath()
        }
    }
    

    通过updatePath函数进行路径的绘制

     func updatePath () {
        
        // 每次获取到新的定位点重新绘制路径
        
        // 移除掉除之前的overlay
        let overlays = self.mapView.overlays
        self.mapView.removeOverlays(overlays)
        
        let polyline = MAPolyline(coordinates: &self.coordinateArray, count: UInt(self.coordinateArray.count))
        self.mapView.addOverlay(polyline)
        
        // 将最新的点定位到界面正中间显示
        let lastCoord = self.coordinateArray[self.coordinateArray.count - 1]
        self.mapView.setCenterCoordinate(lastCoord, animated: true)
    }
    

    之前最开始的实现方式是用最新的点和上一次点进行连接,但这种方式会有一个问题,在将地图放大到最大缩放比后会发现,通过两点两点连接的线段并不能构成一条完整的线,而是一段一段不连贯的线段。所以现在改为直接拿所有的点重新绘制整条路径。绘制之前记得将已经存在的路径移除掉,否则每次绘制的路径会堆积在一起,导致路径线条变粗长生毛刺。

    removeOverlays用来移除掉之前的路径。

    addOverlay添加重新绘制的路径。

    setCenterCoordinate将指定的坐标点显示在地图中间,保证在用户跑了很长的距离之后也不会超出地图的显示范围。

    通过addOverlay绘制路径时,会有一个函数回调:

    func mapView(mapView: MAMapView!, viewForOverlay overlay: MAOverlay!) -> MAOverlayView! {
        if overlay.isKindOfClass(MAPolyline) {
            let polylineView = MAPolylineView(overlay: overlay)
            polylineView.lineWidth = 6
            polylineView.strokeColor = UIColor(red: 4 / 255.0, green:  181 / 255.0, blue:  108 / 255.0, alpha: 1.0)
            
            return polylineView
        }
        
        return nil
    }
    

    判断是否为MAPolyline类型,然后设置路径宽度和颜色。(除了线条以外,地图上贴图片等一些操作也在这个回调中进行处理)

    大体实现如上。接下来可以把Demo跑起来了。

    程序大体样子如下,打开时,界面中间有个按钮,点击之后便进入地图界面

    7.jpg 8.jpg

    此时,你可能迫不及待地想编好真机然后到外面跑跑测试下效果,其实模拟器提供了一个功能,能够直接模拟位置的变化。选中模拟器的Debug,点Location,里面有几个选项,有几种改变位置的方式,也可以通过自己设置经纬度进行定位,其中Freeway Drive运动的速度比较快~:)

    绘制运动路径的效果如下图:

    9.jpg

    (模拟器模拟的效果地图上不会显示其他信息,真机上则会跟你实际位置相对应,地图内容是完整显示周围信息的)

    具体实现见源码

    <h3>最后再有个后记</h3>

    iOS系统的定位采用的是混合定位的方式,通过GPS、Wifi、手机基站信号共同定位的方式来提高定位精度,虽说如此,但偶尔出现某个点的定位误差依然是难以避免的,当出现较大偏差时,会导致路径上有某个明显凸出的点,或整条路径毛刺现象严重,即使长时间在一个位置不动,也会出现定位点在附近导出乱飘的情况。这些问题只能通过算法分析来修正。

    常用的处理算法为卡尔曼滤波,相关的内容对数学功底要求极高,有兴趣可自行Google研究。

    之前无意在github上找到了份相关的源码实现,用的是java,改成iOS后直接拿来用发现对结果的修正确实起到一定的帮助,能将绘制的路径进行平滑处理,滤掉了明显的毛刺。

    下面为源码对测试数据处理前后的结果显示

    10.png 11.png

    有兴趣的自己看源码修改来用的,地址见下方:

    KalmanFilter的Java实现

    <完> :)

    相关文章

      网友评论

        本文标题:高德地图SDK实时绘制跑步路径-Swift实现

        本文链接:https://www.haomeiwen.com/subject/pyshdttx.html