美文网首页Swift Swift编程swift
Swift_系统定位CLLocationManager

Swift_系统定位CLLocationManager

作者: Mccc_ | 来源:发表于2019-01-29 18:07 被阅读32次

前言

  • 苹果产品只允许接入了苹果定位CoreLocation,第三方地图(百度/高德/腾讯)只是在苹果定位服务基础上二次封装。

  • 定位的方式有三种,基站定位,WiFi定位,GPS定位。其实无论哪种定位,都是根据已知点位置信息来定位当前位置,原理都是一样的,只是精度、定位速度和耗电的差别。目前苹果使用的是GPS定位。

  • 执行定位的代理方法的时候,会发现定位代理方法一般会执行3~4次。先给返回一个大致的位置,之后再进行校正,所以后面返回的坐标精度比前面的高。

  • 地图定位的原理
    地图定位是三角测量定位,已知三个点的坐标,和未知点到这三个点的距离,求未知点的坐标。
    Trilateration三边测量定位算法的原理:已知(x1,y1),(x2,y2),(x3,y3),和三个圆的半径,求(x0,y0)。但实际情况是三个圆往往不能相交于一个点,因为三个圆的半径有误差,所以最后得到的是一个圆,而不是点,那个圆的半径其实就是精度。为了增加精度,也就是缩小圆的半径,我们需要更多的已知点和未知点到已知点的距离,来相互校验。

    三角测量定位

坐标系

  • WGS-84坐标系 (原始坐标系)
    用国际GPS纪录仪记录下来的经纬度,通过GPS定位拿到的原始经纬度。
  • GCJ-02坐标系 (火星坐标系/中国坐标系)
    是我国独创的坐标体系。由WGS-84加密而成,在国内,必须至少使用GCJ-02坐标系或使用基于GCJ-02加密的坐标系。
  • bd-09坐标系(百度坐标系)
    百度坐标系是在GCJ-02坐标系的基础上再次加密偏移后形成的坐标系,只适用于百度地图。

高德地图,谷歌地图和苹果地图在国外都是使用的WGS-84坐标系坐标系,在国内使用的是GCJ-02坐标系。百度地图使用的是bd-09坐标系

听闻是为了国家安全搞的加密措施,使用的是非线性的偏移值,想得到真实的数据,得向GCJ申请,才能得到解密方法。

那么就可以解释以下问题了。

  1. CLLocationManager定位坐标不准确问题?
    因为CLLocationManager定位出来的结果是基于WGS-84坐标系的,却拿来用在GCJ-02坐标系上使用。肯定是有一定误差的。
  2. 为什么在国内的地图还是定位挺准确的?
    虽然这些地图的定位都是使用的CLLocationManager来定位的,但是地图根据定位的信息根据算法进行了转化。
    1.China Map Deviation as a Regression Problem
    2.The Deviation of China Map as a Regression Problem

不同坐标系下的转换

  • 判断是否在中国范围
    /**
     *  判断是否在中国范围
     *
     *  @param lat 纬度
     *  @param lon 经度
     *
     *  @return bool
     */
    private static func inTheRangeChina(latitude: Double, longitude: Double) -> Bool {
        
        if (longitude < 72.004 || longitude > 137.8347) {
        return false
        }
        if (latitude < 0.8293 || latitude > 55.8271) {
        return false
        }
        return true
    }
  • 火星坐标系 -> 原始坐标系
    /**
     *  中国坐标 -> 标准坐标
     *
     *  @param latitude 标准坐标的维度
     *
     *  @param longitude 标准坐标的经度
     *
     *  @return 中国坐标
     */
    public static func transformFromGCJToWGS(latitude: Double, longitude: Double) -> (latitude:Double,longitude:Double) {
        
        let gcLoc     : (latitude: Double, longitude: Double) = (latitude, longitude)
        var wgLoc     : (latitude: Double, longitude: Double) = (latitude, longitude)
        var currGcLoc : (latitude: Double, longitude: Double) = (0,0)
        var dLoc      : (latitude: Double, longitude: Double) = (0,0)

        
        
        while true {
            currGcLoc = transformFromWGSToGCJ(latitude: latitude, longitude: longitude)
            
            dLoc.latitude = gcLoc.latitude - currGcLoc.latitude;
            dLoc.longitude = gcLoc.longitude - currGcLoc.longitude;
            if (fabs(dLoc.latitude) < 1e-7 && fabs(dLoc.longitude) < 1e-7) {  // 1e-7 ~ centimeter level accuracy
                // Result of experiment:
                //   Most of the time 2 iterations would be enough for an 1e-8 accuracy (milimeter level).
                //
                return wgLoc;
            }
            wgLoc.latitude += dLoc.latitude;
            wgLoc.longitude += dLoc.longitude
        }
        
        return wgLoc
    }

    private static func transformLatitude(x:Double, y:Double) -> Double {
        let pi : Double = Double.pi

        var lat: Double = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(x > 0 ? x:-x);
        lat += (20.0 * sin(6.0 * x * pi) + 20.0 * sin(2.0 * x * pi)) * 2.0 / 3.0;
        lat += (20.0 * sin(y * pi) + 40.0 * sin(y / 3.0 * pi)) * 2.0 / 3.0;
        lat += (160.0 * sin(y / 12.0 * pi) + 320 * sin(y * pi / 30.0)) * 2.0 / 3.0;
        return lat
    }
    
    private static func transformLongitude(x:Double, y:Double) -> Double {
        let pi : Double = Double.pi

        var lon : Double = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(x > 0 ? x:-x);
        lon += (20.0 * sin(6.0 * x * pi) + 20.0 * sin(2.0 * x * pi)) * 2.0 / 3.0;
        lon += (20.0 * sin(x * pi) + 40.0 * sin(x / 3.0 * pi)) * 2.0 / 3.0;
        lon += (150.0 * sin(x / 12.0 * pi) + 300.0 * sin(x / 30.0 * pi)) * 2.0 / 3.0;
        return lon;
    }

  • 中国坐标 -> 标准坐标
    /**
     *  中国坐标 -> 标准坐标
     *
     *  @param latitude 标准坐标的维度
     *
     *  @param longitude 标准坐标的经度
     *
     *  @return 中国坐标
     */
    public static func transformFromGCJToWGS(latitude: Double, longitude: Double) -> (latitude:Double,longitude:Double) {
        
        let gcLoc     : (latitude: Double, longitude: Double) = (latitude, longitude)
        var wgLoc     : (latitude: Double, longitude: Double) = (latitude, longitude)
        var currGcLoc : (latitude: Double, longitude: Double) = (0,0)
        var dLoc      : (latitude: Double, longitude: Double) = (0,0)

        
        
        while true {
            currGcLoc = transformFromWGSToGCJ(latitude: latitude, longitude: longitude)
            
            dLoc.latitude = gcLoc.latitude - currGcLoc.latitude;
            dLoc.longitude = gcLoc.longitude - currGcLoc.longitude;
            if (fabs(dLoc.latitude) < 1e-7 && fabs(dLoc.longitude) < 1e-7) {  // 1e-7 ~ centimeter level accuracy
                // Result of experiment:
                //   Most of the time 2 iterations would be enough for an 1e-8 accuracy (milimeter level).
                //
                return wgLoc;
            }
            wgLoc.latitude += dLoc.latitude;
            wgLoc.longitude += dLoc.longitude
        }
        
        return wgLoc
    }
  • GCJ-02 to BD-09 (标准坐标系 -> 百度坐标系)
    /**
     * GCJ-02 to BD-09 (标准坐标系 -> 百度坐标系)
     *
     *  @param latitude 标准坐标的维度
     *
     *  @param longitude 标准坐标的经度
     *
     *  @return 百度坐标
     */
    public static func transformFromGCJToBD(latitude: Double, longitude: Double) -> (latitude:Double,longitude:Double) {
        return (latitude + 0.006, longitude + 0.0065)
    }

  • BD-09 to GCJ-02 (百度坐标系 -> 标准坐标系)
    /**
     * BD-09 to GCJ-02 (百度坐标系 -> 标准坐标系)
     *
     *  @param latitude 百度坐标的维度
     *
     *  @param longitude 百度坐标的经度
     *
     *  @return 标准坐标
     */
    public static func transformFromBDToGCJ(latitude: Double, longitude: Double) -> (latitude:Double,longitude:Double) {
        return (latitude - 0.006, longitude - 0.0065)
    }

封装的定位功能

代码地址

如何使用 ?
import UIKit
import MCComponentFunction

class ViewController: UIViewController {

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        MCLocation.shared.didUpdateLocation(self)
    }
}


extension ViewController: MCLocationProtocol {
    func mc_locationManager(latitude: Double, longitude: Double) {
        // latitude 经度
        // longitude 维度
    }
}
实现逻辑
import Foundation

import CoreLocation


@objc public protocol MCLocationProtocol {
    func mc_locationManager(latitude: Double, longitude: Double)
}


public class MCLocation: NSObject {
    
    public weak var delegate : MCLocationProtocol?
    
    public static let shared = MCLocation.init()
    
    private var locationManager : CLLocationManager?
    private var viewController : UIViewController?      // 承接外部传过来的视图控制器,做弹框处理
    
    
    // 外部初始化的对象调用,执行定位处理。
    public func didUpdateLocation(_ vc:UIViewController) {
        
        self.viewController = vc
        self.delegate = vc as? MCLocationProtocol
        if (self.locationManager != nil) && (CLLocationManager.authorizationStatus() == .denied) {
            // 定位提示
            self.alter(viewController: viewController!)
        } else {
            self.requestLocationServicesAuthorization()
        }
    }
    
    
    // 初始化定位
    private func requestLocationServicesAuthorization() {
        
        if (self.locationManager == nil) {
            self.locationManager = CLLocationManager()
            self.locationManager?.delegate = self
        }
        
        self.locationManager?.requestWhenInUseAuthorization()
        self.locationManager?.startUpdatingLocation()
        
        if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.notDetermined) {
            locationManager?.requestWhenInUseAuthorization()
        }
        
        if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse) {
            locationManager?.desiredAccuracy = kCLLocationAccuracyBest
            let distance : CLLocationDistance = 100.0
            locationManager?.distanceFilter = distance
            locationManager?.startUpdatingLocation()
        }
    }
    
    
    // 获取定位代理返回状态进行处理
    private func reportLocationServicesAuthorizationStatus(status:CLAuthorizationStatus) {
        
        if status == .notDetermined {
            // 未决定,继续请求授权
            requestLocationServicesAuthorization()
        } else if (status == .restricted) {
            // 受限制,尝试提示然后进入设置页面进行处理
            alter(viewController: viewController!)
        } else if (status == .denied) {
            // 受限制,尝试提示然后进入设置页面进行处理
            alter(viewController: viewController!)
        }
    }
    
    
    private func alter(viewController:UIViewController) {
        let alter = UIAlertController.init(title: "定位服务未开启,是否前往开启?", message: "", preferredStyle: UIAlertController.Style.alert)
        let cancle = UIAlertAction.init(title: "暂不开启", style: UIAlertAction.Style.cancel) { (a) in
        }
        let confirm = UIAlertAction.init(title: "前往开启", style: UIAlertAction.Style.default) { (b) in
            // 跳转到开启定位服务页面
            let url = NSURL.init(string: UIApplication.openSettingsURLString)
            if(UIApplication.shared.canOpenURL(url! as URL)) {
                UIApplication.shared.openURL(url! as URL)
            }
        }
        alter.addAction(cancle)
        alter.addAction(confirm)
        viewController.present(alter, animated: true, completion: nil)
    }
}


extension MCLocation:  CLLocationManagerDelegate {
    
    public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        self.locationManager?.stopUpdatingLocation()
        
        let location = locations.last ?? CLLocation.init()
        let coordinate = location.coordinate
        
        let latitude : Double = coordinate.latitude
        let longitude : Double = coordinate.longitude
        
        
        let transformLocation = MCLocationHelper.transformFromWGSToGCJ(latitude: latitude, longitude: longitude)
        
        
        delegate?.mc_locationManager(latitude: transformLocation.latitude, longitude: transformLocation.longitude)
    }
    
    public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        reportLocationServicesAuthorizationStatus(status: status)
    }
    
    public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        self.locationManager?.stopUpdatingLocation()
    }
}

相关文章

  • Swift_系统定位CLLocationManager

    前言 苹果产品只允许接入了苹果定位CoreLocation,第三方地图(百度/高德/腾讯)只是在苹果定位服务基础上...

  • IOS-CLLocationManager用法心得

    CLLocationManager定位权限问题。 我们在使用CLLocationManager进行定位的时候,肯定...

  • CLLocationManager获取地理位置信息

    要使用CLLocationManager获取定位的话,首先要引入系统框架CoreLocation.framewor...

  • IOS Swift3 使用 CLLocationManager

    CLLocationManager 是iOS系统提供的定位对象,通过该对象可以获取定位信息,包括:经纬度、海拔、方...

  • 定位 CLLocationManager

    项目当中经常使用到需要获取用户的地理位置,经纬度,现在使用CLLocationManager 苹果自带的获取地理位...

  • ios 定位当前位置

    开始定位代码展示: CLLocationManager 定位类 CLLocationManagerDelegate...

  • MapKit资料

    MKMapView 地图 MKMapViewDelegate CLLocationManager 定位 CLLo...

  • iOS CLLocationManager定位

    声明 因为苹果在不允许使用第三方库进行定位,即使是百度地图的定位也是在苹果API的基础上进行了封装,所以学习下iO...

  • 定位知识的回顾

    1.定位CoreLocation框架CLLocationManager设置代理开始定位代理方法CLLocation...

  • iOS定位(LBS)

    CoreLocation 地理定位 1.创建定位管理者 CLLocationManager *mgr = [[CL...

网友评论

    本文标题:Swift_系统定位CLLocationManager

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