iOS 開發(fā)地圖常見功能總結(jié)(定位、標(biāo)注大頭針、劃線、路徑規(guī)劃、導(dǎo)航、地理圍欄等)

前言

最近開發(fā)的項目使用了一些第三方地圖的功能,主要包括:

  • 定位
  • POI(附近,模糊)搜索
  • 地理編碼和逆編碼
  • 地圖展示
  • 標(biāo)記點(大頭針)
  • 線段,圖形,覆蓋物(折線,直線,圓形,不規(guī)則圖形)
  • 導(dǎo)航(內(nèi)部導(dǎo)航,跳轉(zhuǎn)其他地圖 App 導(dǎo)航)
  • 地理圍欄

主要包含了高德地圖、谷歌地圖等,其他的地圖如百度地圖,騰訊地圖等參考相關(guān)文檔其實差別也不大。

文章主要記錄一下相關(guān)代碼和一些集成調(diào)試踩過的一些坑,防止忘記,歸個檔。

廢話不多說,先看效果:

<img src="https://image-1254431338.cos.ap-guangzhou.myqcloud.com/2020-11-14-105932.png" alt="image-20201114185932086" style="zoom: 25%;" />

ezgif.com-gif-makereee

<img src="https://image-1254431338.cos.ap-guangzhou.myqcloud.com/2020-11-14-111311.gif" style="zoom: 50%;" />

<img src="https://image-1254431338.cos.ap-guangzhou.myqcloud.com/2020-11-14-110809.png" alt="image-20201114190809301" style="zoom:25%;" />

<img src="https://image-1254431338.cos.ap-guangzhou.myqcloud.com/2020-11-14-114426.png" alt="image-20201114194426235" style="zoom:33%;" />

定位

CoreLocation 框架, CLLocationManager 類。

定位 - 系統(tǒng)原生

// 1. 初始化 CLLocationManager 實例
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone

// 持續(xù)定位上報
locationManager.allowsBackgroundLocationUpdates = true
// 是否允許中斷定位功能
locationManager.pausesLocationUpdatesAutomatically = false

locationManager.startUpdatingLocation()

// 權(quán)限判斷
let status  = CLLocationManager.authorizationStatus()
        
if status == .notDetermined {
    locationManager.requestWhenInUseAuthorization()
}

if status == .denied || status == .restricted {
    let alert = UIAlertController(title: "定位權(quán)限未開啟", message: "請前往設(shè)置允許訪問您的定位權(quán)限", preferredStyle: .alert)
    let okAction = UIAlertAction(title: "去開啟", style: .default, handler: { _ in
        // 打開設(shè)置
        UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
    })

    alert.addAction(okAction)
    let cancelAction = UIAlertAction(title: "取消", style: .cancel, handler: nil)
    alert.addAction(cancelAction)

    UIApplication.shared.visibleVC().present(alert, animated: true, completion: nil)
    return
}

// 3. 代理回調(diào) CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    let currentLocation = locations.last
    
    // 得到經(jīng)緯度
    let currentCoordinate2D = currentLocation?.coordinate
    guard let latitude = currentCoordinate2D?.latitude, let longitude = currentCoordinate2D?.longitude else { return }

    // 定位成功后停止定位
    locationManager.stopUpdatingLocation()
}

// 發(fā)生錯誤
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
    
}

定位 - 高德定位

  1. 高德開放平臺注冊賬號
  2. 新建應(yīng)用
  3. 拿到 key,在 App 啟動的時候設(shè)置
// 設(shè)置 key
AMapServices.shared()?.apiKey = "\(appKey)"


// 1. 初始化 AMapLocationManager 實例
private lazy var locationManager : AMapLocationManager = {
    let managner = AMapLocationManager()
    managner.delegate = self
    managner.pausesLocationUpdatesAutomatically = false
    // 定位精度
    managner.desiredAccuracy = kCLLocationAccuracyBest
    // 是否允許后臺定位
    managner.allowsBackgroundLocationUpdates = false
    
    // 指定單次定位超時時間,默認(rèn)為10s。最小值是2s。注意單次定位請求前設(shè)置。注意: 單次定位超時時間從確定了定位權(quán)限(非kCLAuthorizationStatusNotDetermined狀態(tài))后開始計算。
    manager.locationTimeout = 2
    // 指定單次定位逆地理超時時間,默認(rèn)為5s。最小值是2s。注意單次定位請求前設(shè)置。
    manager.reGeocodeTimeout = 2
    
    return managner
}()

// 2. 開始連續(xù)定位。調(diào)用此方法會cancel掉所有的單次定位請求。
locationManager.startUpdatingLocation()

// 3. 停止連續(xù)定位。調(diào)用此方法會cancel掉所有的單次定位請求,可以用來取消單次定位。
locationManager.stopUpdatingLocation()

// 4. 單次定位。如果當(dāng)前正在連續(xù)定位,調(diào)用此方法將會失敗,返回NO。\n該方法將會根據(jù)設(shè)定的 desiredAccuracy 去獲取定位信息。如果獲取的定位信息精確度低于 desiredAccuracy ,將會持續(xù)的等待定位信息,直到超時后通過completionBlock返回精度最高的定位信息。\n可以通過 stopUpdatingLocation 方法去取消正在進行的單次定位請求。
// withReGeocode: 是否帶有逆地理信息(獲取逆地理信息需要聯(lián)網(wǎng))
locationManager.requestLocation(withReGeocode: true) { (location, regeocode, error) in
    guard error == nil else { return }
    guard let regeocode = regeocode else { return }
    print(regeocode)
}

// 5. 代理回調(diào)  AMapLocationManagerDelegate

// 定位成功
func amapLocationManager(_ manager: AMapLocationManager!, didUpdate location: CLLocation!, reGeocode: AMapLocationReGeocode!) {
        
}

// 定位失敗
func amapLocationManager(_ manager: AMapLocationManager!, didFailWithError error: Error!) {
    
}

// 請求定位授權(quán)
func amapLocationManager(_ manager: AMapLocationManager!, doRequireLocationAuth locationManager: CLLocationManager!) {
    locationManager.requestAlwaysAuthorization()
}

定位 - Google 定位

  1. https://cloud.google.com/maps-platform/ 平臺注冊開發(fā)者賬號
  2. 申請 API key
  3. image-20201114173121575
// 設(shè)置 API key
GMSServices.provideAPIKey("\(Google API key)")

POI(Point of Interest,興趣點)&& 地理編碼、逆編碼 && 路徑規(guī)劃

POI(Point of Interest,興趣點)&& 地理編碼、逆編碼 && 路徑規(guī)劃 - 高德地圖

高德地圖 SDK 封裝了 AMapSearchObject 基類對象,具體的搜搜請求對象和響應(yīng)對象都由子類去繼承實現(xiàn)。

// 1、初始化搜索對象,設(shè)置代理
private lazy var search: AMapSearchAPI = {
     let object = AMapSearchAPI()!
     object.delegate = self
     return object
}()


//---------  附近 :構(gòu)造請求對象 ----------- 
let request = AMapPOIAroundSearchRequest()
// 當(dāng)?shù)囟ㄎ唤?jīng)緯度
request.location = AMapGeoPoint.location(withLatitude: currentCoordinate!.latitude.cgFloat, longitude: currentCoordinate!.longitude.cgFloat)
request.sortrule = 0
// 類型,多個類型用“|”分割 可選值:文本分類、分類代碼 
// 高德地圖的POI類別共20個大類,分別為:汽車服務(wù)、汽車銷售、汽車維修、摩托車服務(wù)、餐飲服務(wù)、購物服務(wù)、生活服務(wù)、體育休閑服務(wù)、醫(yī)療保健服務(wù)、住宿服務(wù)、風(fēng)景名勝、商務(wù)住宅、政府機構(gòu)及社會團體、科教文化服務(wù)、交通設(shè)施服務(wù)、金融保險服務(wù)、公司企業(yè)、道路附屬設(shè)施、地名地址信息、公共設(shè)施,同時,每個大類別都還有二級以及三級的細(xì)小劃。
request.types = "風(fēng)景名勝|(zhì)商務(wù)住宅|政府機構(gòu)及社會團體|交通設(shè)施服務(wù)|公司企業(yè)|道路附屬設(shè)施|地名地址信息"
request.requireExtension = true
// 當(dāng)前頁數(shù)
request.page = page
request.requireSubPOIs = true

// 發(fā)起搜索
search.aMapPOIAroundSearch(request)


// ----------------- 關(guān)鍵詞搜索 構(gòu)造請求對象 ------------------------------
let request = AMapPOIKeywordsSearchRequest()
request.keywords = keywords
// 指定城市
request.city = city
request.sortrule = 0
request.requireExtension = true
request.page = page
request.cityLimit = false
request.requireSubPOIs = true
request.types = "風(fēng)景名勝|(zhì)商務(wù)住宅|政府機構(gòu)及社會團體|交通設(shè)施服務(wù)|公司企業(yè)|道路附屬設(shè)施|地名地址信息"

// 關(guān)鍵詞搜索 - 高德: 發(fā)起關(guān)鍵詞搜索
search.aMapPOIKeywordsSearch(request)


// ----------- 地理編碼:地理編碼,又稱為地址匹配,是從已知的地址描述到對應(yīng)的經(jīng)緯度坐標(biāo)的轉(zhuǎn)換過程。該功能適用于根據(jù)用戶輸入的地址確認(rèn)用戶具體位置的場景,常用于配送人員根據(jù)用戶輸入的具體地址找地點。-----------
let request = AMapGeocodeSearchRequest()
// 位置信息:地理編碼是依據(jù)當(dāng)前輸入,根據(jù)標(biāo)準(zhǔn)化的地址結(jié)構(gòu)(省/市/區(qū)或縣/鄉(xiāng)/村或社區(qū)/商圈/街道/門牌號/POI)進行各個地址級別的匹配,以確認(rèn)輸入地址對應(yīng)的地理坐標(biāo),只有返回的地理坐標(biāo)匹配的級別為POI,才會對應(yīng)一個具體的地物(POI)。
request.address = address

// 發(fā)起地理編碼搜索請求
search.aMapGeocodeSearch(request)


// -------------------------------- AMapSearchDelegate --------------------------------------
// POI 搜索回調(diào)
func onPOISearchDone(_ request: AMapPOISearchBaseRequest!, response: AMapPOISearchResponse!) {
    
    if response.count == 0 {
        return
    }
    
    //解析response獲取POI信息
}

// 地理編碼(地址轉(zhuǎn)坐標(biāo))回調(diào)
func onGeocodeSearchDone(_ request: AMapGeocodeSearchRequest!, response: AMapGeocodeSearchResponse!) {
    // 解析response獲取地理信息,
   
}

// 路線規(guī)劃
func onRouteSearchDone(_ request: AMapRouteSearchBaseRequest!, response: AMapRouteSearchResponse!) {
    if response.count > 0 {
        //解析response獲取路徑信息
    }
}

// 搜索出現(xiàn)錯誤    
func aMapSearchRequest(_ request: Any!, didFailWithError error: Error!) {

}

POI(Point of Interest,興趣點)&& 地理編碼、逆編碼 && 路徑規(guī)劃 - Google 地圖

Google 地圖這些 POI 搜索,導(dǎo)航數(shù)據(jù)這些,提供的是 HTTP API 請求調(diào)用的方式。

// 谷歌地圖 POI 關(guān)鍵詞模糊搜索
// https://maps.googleapis.com/maps/api/place/textsearch/json
var params: [String: Any] = [:]
params["key"] = "\(Google API key)"
// 語言 : 
params["language"] = "en"
params["query"] = "\(關(guān)鍵詞文本)"
params["inputType"] = "textQuery"
//  params["region"] =


// 谷歌地圖 POI 附近搜索
// https://maps.googleapis.com/maps/api/place/nearbysearch/json
var params: [String: Any] = [:]
params["key"] = "\(Google API key)"
// 語言 : 
params["language"] = "en"
// 當(dāng)前位置 字符串: "緯度,經(jīng)度"
params["location"] = "\(latitude,longitude)"
// 搜索半徑 (單位:米)
params["radius"] = "\(1500)"


// 谷歌地理編碼
// https://maps.googleapis.com/maps/api/geocode/json
var params: [String: Any] = [:]
params["key"] = "\(Google API key)"
// 地址文本
params["address"] = address


// 谷歌地址逆編碼
// https://maps.googleapis.com/maps/api/geocode/json
var params: [String: Any] = [:]
params["key"] = "\(Google API key)"
params["latlng"] = location.googleDisplay

return .requestParameters(parameters: params,
                          encoding: URLEncoding.default)


// 谷歌路線導(dǎo)航,默認(rèn)駕車
// https://maps.googleapis.com/maps/api/directions/json
var params: [String: Any] = [:]
params["key"] = "\(Google API key)"
// 出發(fā)點坐標(biāo)位置
params["origin"] = "\(latitude,longitude)"
// 目的地坐標(biāo)位置
params["destination"] = "\(latitude,longitude)"


// 解析相應(yīng)數(shù)據(jù),模型轉(zhuǎn)換框架使用的是 ObjectMapper 
/// 列表容器模型
struct ListContainerModel<T: Mappable>: Mappable {
    var list: [T]?
    var numberOfElements: Int?
    var total: Int?
    var pageNumber: Int?
    var size: Int?
    var hasNextPage: Bool?
    var hasPreviousPage: Bool?

    init?(map: Map) {
      
    }

    mutating func mapping(map: Map) {
        list <- map["list"]
        numberOfElements <- map["numberOfElements"]
        total <- map["total"]
        pageNumber <- map["pageNumber"]
        size <- map["size"]
        hasNextPage <- map["hasNextPage"]
        hasPreviousPage <- map["hasPreviousPage"]
    }
}

/// google POI 附近和關(guān)鍵詞地址搜索
struct GoogleSearchAddressContainerModel: Mappable {
    var html_attributions: [Any]?
    
    /// POI 列表
    var results: [SearchAddressModel]?
    
    /// 返回結(jié)果狀態(tài)
    var status: String?
    
    init?(map: Map) {
      
    }

    mutating func mapping(map: Map) {
        html_attributions <- map["html_attributions"]
        results <- map["results"]
        status <- map["status"]
    }
}

/// leg Model
struct GoogleDirectionLegModel: Mappable  {
    var distance: Any?
    var duration: Any?
    var end_address: String?
    var end_location: GeometryLocation?
    var start_address: String?
    var start_location: GeometryLocation?
    
    var steps: [GoogleDirectionAddressModel]?
    
    init?(map: Map) {
        
    }
    
    mutating func mapping(map: Map) {
        steps <- map["steps"]
    }
}

/// 谷歌地圖返回結(jié)果模型 
struct GoogleDirectionResultModel: Mappable {
    var steps: [GoogleDirectionAddressModel]? {
        get {
            var result = [GoogleDirectionAddressModel]()
            legs?.forEach({ (leg) in
                result.append(contentsOf: leg.steps ?? [])
            })
            
            return result
        }
    }
    
    var legs: [GoogleDirectionLegModel]?
    
    var points: String?
    
    init?(map: Map) {
        
    }
    
    mutating func mapping(map: Map) {
        legs <- map["routes.0.legs"]
        points <- map["routes.0.overview_polyline.points"]
    }
}

標(biāo)記點(大頭針 annotation || Marker)

標(biāo)記點(大頭針 annotation || Marker)- 高德

// 1. 創(chuàng)建地圖對象
let gaodeMap = MAMapView()
gaodeMap.delegate = self
// 縮放級別
zoomLevel = 17
// 是否顯示用戶位置
isShowsUserLocation = true

// 自定義地圖樣式(1. 在高德地圖開發(fā)者應(yīng)用后臺新建地圖樣式,下載樣式的資源包拖到工程里面去)
guard let url = R.file.styleData(),
    let data = try? Data(contentsOf: url) else { return }

let options = MAMapCustomStyleOptions()
options.styleData = data
setCustomMapStyleOptions(options)
customMapStyleEnabled = true

// 設(shè)定當(dāng)前地圖的經(jīng)緯度范圍,該范圍可能會被調(diào)整為適合地圖窗口顯示的范圍
setRegion(MACoordinateRegion(center:  centerCoordinate, span: MACoordinateSpan(latitudeDelta: 0.03, longitudeDelta: 0.03)), animated: false)


// 2. 新建標(biāo)注
let pointAnnotation = MAPointAnnotation()
// 標(biāo)注中心點經(jīng)緯度坐標(biāo)
pointAnnotation.coordinate = center
// 標(biāo)注標(biāo)題
pointAnnotation.title = title
// 標(biāo)注副標(biāo)題
pointAnnotation.subtitle = subTitle

// 添加標(biāo)注 
gaodeMap.addAnnotation(pointAnnotation
// 選中標(biāo)注數(shù)據(jù)對應(yīng)的view。注意:如果annotation對應(yīng)的annotationView因不在屏幕范圍內(nèi)而被移入復(fù)用池,為了完成選中操作,會將對應(yīng)的annotationView添加到地圖上,并將地圖中心點移至annotation.coordinate的位置。
gaodeMap.selectAnnotation(pointAnnotation, animated: false)
                       
                       
// 設(shè)置標(biāo)注 的外觀樣式
// MAMapViewDelegate       
 func mapView(_ mapView: MAMapView!, viewFor annotation: MAAnnotation!) -> MAAnnotationView! {
    if annotation.isKind(of: MAPointAnnotation.self) {
        let pointReuseIndetifier = "pointReuseIndetifier"
        var annotationView: MAPinAnnotationView? = mapView.dequeueReusableAnnotationView(withIdentifier: pointReuseIndetifier) as! MAPinAnnotationView?

        if annotationView == nil {
            annotationView = MAPinAnnotationView(annotation: annotation, reuseIdentifier: pointReuseIndetifier)
        }
        
        // 是否可以點擊標(biāo)注view
        annotationView!.canShowCallout = true
        // 是否有標(biāo)注添加到地圖上的動畫效果(從上而下)
        annotationView!.animatesDrop = true
        // 是否可以拖拽
        annotationView!.isDraggable = true
        // 是否有右邊的視圖
        annotationView!.rightCalloutAccessoryView = nil

        return annotationView!
    }

    return nil
}
                       
// 移除標(biāo)注
gaodeMap.removeAnnotation(pointAnnotation)             

標(biāo)記點(大頭針 annotation || Marker)- Google

// 創(chuàng)建 Google map
let googleMap = GMSMapView()


// 添加標(biāo)注 Marker
// 創(chuàng)建 GMSMarker 對象
let marker = GMSMarker()
// 標(biāo)注位置
marker.position = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
// 標(biāo)注標(biāo)題
marker.title = title
// 標(biāo)注副標(biāo)題
marker.snippet = subTitle

// 設(shè)置標(biāo)注的圖片
marker.icon = UIImage()

// 設(shè)置
let camera = GMSCameraPosition.camera(withLatitude: marker.position.latitude, longitude: marker.position.longitude, zoom: 16.0)
marker.map = googleMap

// 自動顯示標(biāo)題內(nèi)容
selectedMarker = marker


// 移除標(biāo)注
marker.map = nil

Overlay && Polyline

Overlay && Polyline - 高德

// 多線段的坐標(biāo)點數(shù)組
var lineCoordinates = [CLLocationCoordinate2D]()
// 多邊形
let pol = MAPolygon(coordinates: &lineCoordinates, count: UInt(lineCoordinates.count))
gaodeMap.add(pol)

// 不是多邊形
let polyline: MAPolyline = MAPolyline(coordinates: &lineCoordinates, count: UInt(lineCoordinates.count))
gaodeMap.addOverlays([polyline])
// 展示路線
showOverlays([polyline], animated: false)

// 移除
removeOverlays(polyline)

Overlay && Polyline - Google

// 多線段的坐標(biāo)點數(shù)組
var lineCoordinates = [CLLocationCoordinate2D]()

// 多邊形路徑
let gmsPath = GMSMutablePath()
lineCoordinates.forEach { (location) in
    if let latitude = location.latitude, let longitude = location.longitude {
        gmsPath.addLatitude(latitude, longitude: longitude)
    }
}

let polyline = GMSPolyline(path: gmsPath)
// 設(shè)置線段顏色
polyline.strokeColor = UIColor._themeColor()
// 設(shè)置線段寬度
polyline.strokeWidth = 8.0
// 顯示出來
polyline.map = googleMap


// 移除
polyline.map = nil

地理圍欄

  • POI 圍欄

  • 行政區(qū)劃 圍欄

  • 自定義圓形圍欄

  • 自定義多邊圍欄

// 創(chuàng)建圍欄對象
lazy var geoFenceManager: AMapGeoFenceManager = {
    let manager = AMapGeoFenceManager()
    //進入,離開,停留都要進行通知
    manager.activeAction = [AMapGeoFenceActiveAction.inside , AMapGeoFenceActiveAction.outside , AMapGeoFenceActiveAction.stayed]
    manager.allowsBackgroundLocationUpdates = false  // 允許后臺定位
    manager.delegate = self

    return manager
}()
    
// 創(chuàng)建行政區(qū)域地理圍欄
geoFenceManager.addDistrictRegionForMonitoring(withDistrictName: "深圳市", customID: "city")

// 添加圓形圍欄
// withCenter: 圍欄中心點
//  radius: 圍欄半徑
geoFenceManager.addCircleRegionForMonitoring(withCenter: CLLocationCoordinate2D(latitude: latitude, longitude: longitude), radius: 500, customID: "123456")

// 移除圍欄


// 設(shè)置圓形圍欄樣式
// 渲染多邊型 --- MAMapViewDelegate
func mapView(_ mapView: MAMapView!, rendererFor overlay: MAOverlay!) -> MAOverlayRenderer! {
    if overlay.isKind(of: MACircle.self) {
        let renderer: MACircleRenderer = MACircleRenderer(overlay: overlay)
     
        renderer.lineWidth = 1.0
        renderer.strokeColor = UIColor.gxd_themeColor()
        renderer.fillColor = UIColor.gxd_themeColor().withAlphaComponent(0.4)

        return renderer
    }

    return nil
}


// MARK: - AMapGeoFenceManagerDelegate 
// 圍欄創(chuàng)建完成
func amapGeoFenceManager(_ manager: AMapGeoFenceManager!, didAddRegionForMonitoringFinished regions: [AMapGeoFenceRegion]!, customID: String!, error: Error!) {
    if let error = error {
        let error = error as NSError
        NSLog("創(chuàng)建失敗 %@",error);
    }
    else {
        NSLog("創(chuàng)建成功")
        let circleRegion = regions.first as? AMapGeoFenceCircleRegion

        // 在地圖上畫一個區(qū)域
        guard let center = circleRegion?.center,
            let radius = circleRegion?.radius else { return }
        
        let circle: MACircle = MACircle(center: CLLocationCoordinate2D(latitude: center.latitude, longitude: center.longitude), radius: CLLocationDistance(radius))
        gaodeMap.add(circle)
    }
}

// 圍欄狀態(tài)改變時 用戶未知、進入圍欄、退出圍欄、在圍欄內(nèi)停留
func amapGeoFenceManager(_ manager: AMapGeoFenceManager!, didGeoFencesStatusChangedFor region: AMapGeoFenceRegion!, customID: String!, error: Error!) {
    if error == nil {
        print("status changed \(region.description)")
    } else {
        print("status changed error \(error)")
    }
}

導(dǎo)航

跳轉(zhuǎn)到第三方地圖 App 發(fā)起導(dǎo)航,先要在 InfoPlist 文件里面配置 QueriesSchemes 字段

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>comgooglemaps</string>
    <string>iosamap</string>
    <string>baidumap</string>
</array>

發(fā)起導(dǎo)航:

/// 地圖導(dǎo)航模式
enum MapDirectionMode {
    // 駕車
    case driving
    // 步行
    case walking
    // 騎行
    case bicycling
    // 公共交通
    case transit
}

struct DirectionParams {
    /// 源 App 名稱
    var sourceApplication: String = U""
    /// 源 App scheme url
    var backScheme: String = "\(appUrlScheme)"

    /// 起始地點
    var origin: Location?
    /// 目的地點
    var destination: Location
    /// 導(dǎo)航模式
    var mode: MapDirectionMode = .driving
}



/// 打開蘋果地圖導(dǎo)航
/// - Parameter params: 參數(shù)
static func openAppleMapDirection(withParams params: DirectionParams) {
    guard let latitude = params.destination.latitude,
        let longitude = params.destination.longitude else { return }

    let currentLocation = MKMapItem.forCurrentLocation()
    let toPlacemark = MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: latitude, longitude: longitude))
    let toLocation = MKMapItem(placemark: toPlacemark)
    toLocation.name = params.destination.name
    MKMapItem.openMaps(with: [currentLocation, toLocation], launchOptions: [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving])
}


/// 打開 google 地圖導(dǎo)航
/// - Parameter params: 參數(shù)
static func openGoogleMapDirection(withParams params: DirectionParams) {
    if !Keys.MapSchemeUrl.google.isInstalled { return }

    guard var daddr = params.destination.name else {
        return
    }
    daddr = daddr.replacingOccurrences(of: " ", with: "+", options: .literal, range: nil)

    let urlString = Keys.MapSchemeUrl.google.rawValue + "?" +  "x-source=\(params.sourceApplication)&x-success=\(params.backScheme)&saddr=&daddr=\(daddr)&directionsmode=driving"

    guard let url = URL(string: urlString) else { return }
    UIApplication.shared.open(url, options: [:], completionHandler: nil)

}


/// 打開 百度 地圖導(dǎo)航
/// - Parameter params: 參數(shù)
static func openBaiduMapDirection(withParams params: DirectionParams) {
    if !Keys.MapSchemeUrl.baidu.isInstalled { return }

    guard let destinationLatitude = params.destination.latitude,
                let destinationLongitude = params.destination.longitude else { return }
    let destinationName = params.destination.name ?? ""

    let urlString = Keys.MapSchemeUrl.baidu.rawValue + "map/direction?origin=name:{{我的位置}}&destination=name:\(destinationName)|latlng:\(destinationLatitude),\(destinationLongitude)&mode=driving&coord_type=bd09ll&src=ios.Express.\(params.sourceApplication)"

    guard let url = URL(string: urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "") else { return }
    UIApplication.shared.open(url, options: [:], completionHandler: nil)
}


/// 打開 高德 地圖導(dǎo)航
/// - Parameter params: 參數(shù)
static func openGaodeMapDirection(withParams params: DirectionParams) {
    if !Keys.MapSchemeUrl.gaode.isInstalled { return }

    guard let latitude = params.destination.latitude,
        let longitude = params.destination.longitude else { return }
    let destinationName = params.destination.name ?? ""
    let urlString = Keys.MapSchemeUrl.gaode.rawValue +  "path?sourceApplication=\(params.sourceApplication)&backScheme=\(params.backScheme)&dlat=\(latitude)&dlon=\(longitude)&dname=\(destinationName)&dev=0&t=0"

    guard let url = URL(string: urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "") else { return }
    UIApplication.shared.open(url, options: [:], completionHandler: nil)
}

參考鏈接

  1. https://lbs.amap.com/api
  2. https://developers.google.com/places/ios-sdk/overview
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容