1. 程式人生 > >iOS 【地圖繪製行政區域邊界及填充】

iOS 【地圖繪製行政區域邊界及填充】

很久沒有寫東西了,因為最近的專案要趕在國慶中秋前夕上架,忙碌了2個多月的時間。對地圖繪製這塊整理了一下,希望能讓大家少走彎路。

想法

之前看到在網頁端有地理區域的繪製和填充,覺得挺不錯。在網上扒了扒,沒找到合適的方案。實現效果如下:

環境

  • iOS 開發環境
  • 高德地圖 SDK
  • swift 4.1 語言環境

準備

  • 山東省邊界點資料集合
  • 合理使用高德 API

實現

(1)查詢出山東省行政區域邊界點集合

/// 區域規劃邊界點集合拼接字串 回撥閉包
typealias SearchPolylinesBlock = ([String]) -> Void

/// AMap 檢索物件
lazy var mapSearch = {
    return AMapSearchAPI()
}()
/// 根據關鍵字查詢行政區域邊界
///
/// - Parameters:
///   - keywords: 關鍵字
///   - polylinesCallBack: <#polylinesCallBack description#>
func searchPostionLine(keywords: String, polylinesCallBack: @escaping SearchPolylinesBlock) {
    self.searchPolylinesBlock = polylinesCallBack
        
    let request = AMapDistrictSearchRequest()
    request.keywords = keywords
    request.requireExtension = true
    self.mapSearch?.aMapDistrictSearch(request)
}
/// 行政區域查詢回撥函式
///
/// - Parameters:
///   - request: <#request description#>
///   - response: <#response description#>
func onDistrictSearchDone(_ request: AMapDistrictSearchRequest!, response: AMapDistrictSearchResponse!) {
    if response.count == 0 {
        print("規劃失敗,區域規劃結果不存在!")
        return
    }
    
    if response.districts.count == 0 {
        print("規劃失敗,區域規劃結果不存在!")
        return
    }
    
    /// 行政區劃 邊界點
    var pointLinesArr = [String]()
    for (_, district) in response.districts.enumerated() {
        if district.polylines.count == 0 {
            continue
        }
        for (_, polylineStr) in district.polylines.enumerated() {
            pointLinesArr.append(polylineStr)
        }
    }
    
    if let block = self.searchPolylinesBlock {
        block(pointLinesArr)
    }
}

(2)處理邊界點集合資料

先根據具體情況去選擇解析字串的的格式,目前 amap api 返回的則是內外分隔符不同的情況。具體情況具體分析。

/// 解析經緯度字串(適用於 內外分隔符 不同的情況)
///
/// - Parameters:
///   - pathStr: 經緯度字串(示例:114.036217,22.524128;114.036079,22.52401;114.036011,22.523979)
///   - separatorIn: 一組經緯度 經度和緯度 之間的分隔符
///   - separatorOut: 每 兩組經緯度 之間的間隔符
/// - Returns: [CLLocationCoordinate2D]
func coordinatesForString(_ pathStr: String?, separatorIn: Character = ",", separatorOut: Character = ";") -> [CLLocationCoordinate2D] {
    guard let pathStr = pathStr else { return [] }
    let coorStrs = pathStr.split(separator: separatorOut)
    var results = [CLLocationCoordinate2D]()
    for coorStr in coorStrs {
        let coordinate = coorStr.split(separator: separatorIn)
        guard coordinate.count == 2, let lng = Double(coordinate[0]), let lat = Double(coordinate[1]) else { continue }
        let point = CLLocationCoordinate2D(latitude: lat, longitude: lng)
        results.append(point)
    }
    return results
}

/// 解析經緯度字串(適用於 內外分隔符 相同的情況)
/// warning:
///
/// - Parameters:
///   - pathStr: 經緯度字串(示例:114.036217,22.524128,114.036079,22.52401,114.036011,22.523979)
///   - separator: 每 兩組經緯度 之間的間隔符 & 一組經緯度 經度和緯度 之間的分隔符
/// - Returns: [CLLocationCoordinate2D]
func coordinatesForString(_ pathStr: String?, separator: Character = ",") -> [CLLocationCoordinate2D] {
    guard let pathStr = pathStr else { return [] }
    let coorStrs = pathStr.split(separator: separator)
    var results = [CLLocationCoordinate2D]()
    
    for index in stride(from: 0, to: coorStrs.count, by: 2) {
        guard (index + 1 >= coorStrs.count), let lng = Double(coorStrs[index]), let lat = Double(coorStrs[index + 1]) else { continue }
        let point = CLLocationCoordinate2D(latitude: lat, longitude: lng)
        results.append(point)
    }
    return results
}

(3)描邊填充

// 山東省 區域邊界
AMapUtils.share.searchPostionLine(keywords: "山東省") { [weak self] (pointsStrArr) in
    guard self != nil else { return }
    // 將規範化的字串 —> [CLLocationCoordinate2D]
    var lineArr = [SearchPolyline]()
    for (_, pointsStr) in pointsStrArr.enumerated() {
        var coors: [CLLocationCoordinate2D] = BLLModuleFactory().returnSearchBll().coordinatesForString(pointsStr, separatorIn: ",", separatorOut: ";")
        
        // 填充內部區域(一部分一部分的填充)
        let gonlay = MAPolygon.init(coordinates: &coors, count: UInt(coors.count))
        self?.myMap.add(gonlay)
        
        let polyline = SearchPolyline(coordinates: &coors, count: UInt(coors.count))
        guard let line = polyline else { return } // 沒有規劃出線路來
        line.polylineType = PolylineType.ShandongProvince
        lineArr.append(line)
    }
    self?.myMap.addOverlays(lineArr)
}

幾點注意

(1)AMapUtils 為自定義工具類,方便呼叫,讀者可自行定義;

(2)SearchPolyline 為自定義描線,繼承於 MAPolyline,為了處理業務的複雜邏輯,讀者可自行定義;

(3)描邊和填充內部區域用的是 amap 的不同填充物件(MAPolygon、MAPolyline);

(4)區域規劃一般來說不是封閉的線,是由很多段線條組成的,所以新增線時要一段一段的新增,不然就會頭尾相連亂掉。同理,填充內部區域時也需分段,不然會出現重疊的區域