Swift Swift编程swift

Swift_系统定位CLLocationManager

2019-01-29  本文已影响32人  Mccc_

前言

坐标系

高德地图,谷歌地图和苹果地图在国外都是使用的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 (标准坐标系 -> 百度坐标系)
     *
     *  @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 (百度坐标系 -> 标准坐标系)
     *
     *  @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()
    }
}
上一篇下一篇

猜你喜欢

热点阅读