老规矩先上效果图(北京的朝阳区):
此图是从https://lbs.amap.com/api/javascript-api/example/district-search/draw-district-boundaries这个网站截得,手机真机测试的效果和颜色和这个一样,另外就是再加上点击蓝色区域内和蓝色区域外会有toast弹窗提示。
先描述一下情况: 这几天正好做实验室的一个小项目app(也就是仅供学习那种。。。)需要的一个功能是在地图上进行填充需要的行政区域,也就是行政区划填充,并且要实现能够区分点击填充区域内外的动作。但是从网上大多是画轮廓线的,少有的还是oc写的,折腾了好久好久终于出效果了,特此来记录一下,看看能不能帮助需要的朋友少走弯路。
一开始看到这个博主的代码和思路:
https://blog.csdn.net/Felicity294250051/article/details/82801137
无奈本人太菜,给的程序不全,并且好多函数功能没讲清楚(反正我是没看懂),但是对理解代码还是帮助蛮大的(此处表示感谢博主)
于是还是得又从官方给的demo下手,倒是有画轮廓线的,也有画多边形面的,记得网上就有一些根据现成的几个经纬度点来画多边形面,给几个链接:(反正当时搜了很多到处看,现在能找到可能有帮助的就这俩了。。。)
https://www.jianshu.com/p/fb1177cac1ec
https://blog.csdn.net/sinat_30162391/article/details/52765414
提示一下:完整的swift代码官方demo里面有,可以在那工程里搜相关的关键代码应该就能找到了,就是MAMapKit_2D_Demo这个文件夹里面InsideTestViewController.swift是填充区域的主要就是搜画轮廓线的polyline和多边形的polygon这俩参数(变量)。
其他关于怎么配置高德地图api啥的就不在这里讲了。
下面给出我的全部代码,注释都比较详细,还有包括点击填充区域内外屏幕给出的toast效果。有部分的标注掉的是用来只画轮廓线的:
注意的是,我用的是朝阳区的,但是搜索出来除了北京还有吉林也有一个朝阳。。。。所以在pointLinesArr变量这里做了说明,北京的朝阳有两个多边形构成,所以这样实际上前面的response.districts这里面应该是有两个行政区域(两个朝阳区),这里我走了不少弯路。高德给出的行政划分先是北京朝阳的再是吉林朝阳,所以我在循环让他执行完北京的后就break了。
//
// pollution_sourceController.swift
// 污染源地图界面
//
// Created by zhy on 2020/1/19.
// Copyright © 2020年 zhy. All rights reserved.
//
import UIKit
import MapKit
import CoreLocation
import Toast
class pollution_sourceController: UIViewController, MAMapViewDelegate, AMapSearchDelegate {
var search: AMapSearchAPI!
var mapView: MAMapView!
var currentLocation:CLLocation?//定位
var polygon:MAPolygon!
//var polygon_struct: UnsafeMutablePointer<CLLocationCoordinate2D>
//var polygonview:MAPolygonView
var overlays: Array<MAOverlay>!
var select_longtitude : String = ""
var select_latitude : String = ""
override func viewDidLoad() {
super.viewDidLoad()
AMapServices.shared().enableHTTPS = true
mapView = MAMapView(frame: self.view.bounds)
mapView.delegate = self
mapView.isShowsUserLocation = true
mapView.userTrackingMode = .followWithHeading// 设置跟随定位模式,将定位点设置成地图中心点
self.view.addSubview(mapView)
let r = MAUserLocationRepresentation()//初始化 MAUserLocationRepresentation 对象
r.showsHeadingIndicator = true
mapView.showsCompass = true
mapView.showsScale = true
mapView.update(r)
mapView.distanceFilter = 1000
mapView.zoomLevel = 14
initSearch()
overlays = Array()//地图覆盖物数组初始化
// 发起一次请求
searchDistrict(withKeyword: "朝阳区")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
func initSearch() {
search = AMapSearchAPI()
search.delegate = self
}
//MARK: - Action
func searchDistrict(withKeyword keyword: String?) {
if keyword == nil || keyword! == "" {
return
}
let request = AMapDistrictSearchRequest()
request.keywords = keyword
// 显示商圈,而不是街道
request.requireExtension = true
search.aMapDistrictSearch(request)
}
func addAnnotation(_ coordinate: CLLocationCoordinate2D, title: String, subTitle: String) -> TGAnnotation {
let annotation: TGAnnotation = TGAnnotation()
annotation.coordinate = coordinate
annotation.title = title
annotation.subtitle = subTitle
mapView.addAnnotation(annotation as? MAAnnotation)
return annotation
}
//MARK: - 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)
}
annotationView!.canShowCallout = true
annotationView!.isDraggable = false
annotationView!.rightCalloutAccessoryView = UIButton(type: UIButtonType.detailDisclosure)
return annotationView!
}
return nil
}
//只画轮廓
/*func mapView(_ mapView: MAMapView!, rendererFor overlay: MAOverlay!) -> MAOverlayRenderer! {
if overlay.isKind(of: MAPolyline.self) {
let renderer: MAPolylineRenderer = MAPolylineRenderer(overlay: overlay)
renderer.lineWidth = 8.0
renderer.strokeColor = UIColor.cyan//青色
return renderer
}
return nil
}*/
//填充行政区域
func mapView(_ mapView: MAMapView!, rendererFor overlay: MAOverlay!) -> MAOverlayRenderer! {
if overlay.isKind(of: SelectableOverlay.self) {
let selectableOverlay: SelectableOverlay = overlay as! SelectableOverlay;
let actualOverlay: MAOverlay! = selectableOverlay.overlay
if actualOverlay.isKind(of: MAPolygon.self) {
let renderer: MAPolygonRenderer = MAPolygonRenderer(overlay: actualOverlay)
renderer.strokeColor = UIColor.cyan//轮廓颜色
renderer.lineWidth = 6.0//轮廓粗细
renderer.fillColor = #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 0.3)
return renderer
}
}
return nil;
}
//点击地图操作(判断是否在填充区域内)
func mapView(_ mapView: MAMapView!, didSingleTappedAt coordinate: CLLocationCoordinate2D) {
print("centerCoordinate:\(self.mapView.centerCoordinate.latitude)")
var judge = false
for (index, overlay) in mapView.overlays.enumerated().reversed() {
if (overlay as AnyObject).isKind(of: SelectableOverlay.self) {
let selectableOverlay: SelectableOverlay = overlay as! SelectableOverlay
let mapPoint = MAMapPointForCoordinate(coordinate)
//print("mapPoint:\(mapPoint)")
let distance = self.mapPointsPerPointInViewAtCurrentZoomLevel()
// 判断是否选中了overlay
if isOverlayWithLineWidthContainsPoint(selectableOverlay.overlay, distance, mapPoint){
judge = true
}
}
}
if judge{//选中了填充区域
self.view.makeToast(String.init(format: "coordinate = {%f, %f}", coordinate.latitude,coordinate.longitude))
}else{//没选中填充区域
self.view.makeToast("您无权限访问")
}
}
//MARK: - Utility
func mapPointsPerPointInViewAtCurrentZoomLevel() -> Double {
return Double(self.mapView.metersPerPointForCurrentZoomLevel) * MAMapPointsPerMeterAtLatitude(self.mapView.centerCoordinate.latitude)
}
//MARK: - AMapSearchDelegate
func aMapSearchRequest(_ request: Any!, didFailWithError error: Error!) {
print("Error:\(String(describing: error))")
}
func onDistrictSearchDone(_ request: AMapDistrictSearchRequest!, response: AMapDistrictSearchResponse!) {
mapView.removeAnnotations(mapView.annotations)
mapView.removeOverlays(mapView.overlays)
if response.count == 0 {
print(print("规划失败,区域规划结果不存在!"))
return
}
for aDistrict in response.districts {//aDistrict:在搜索到的每个行政区域循环
var subAnnotations = Array<MAPointAnnotation>()//标注点
for subDistrict in aDistrict.districts {//返回该行政区划下级的区划对象
let coordinate = CLLocationCoordinate2D(latitude: CLLocationDegrees(subDistrict.center.latitude), longitude: CLLocationDegrees(subDistrict.center.longitude))
let anno = MAPointAnnotation()
anno.coordinate = coordinate//经纬度
anno.title = subDistrict.name
subAnnotations.append(anno)//边界点
//print("coordinate:\(coordinate)")
}
print("aDistrict:\(aDistrict)")
var pointLinesArr = [String]()//所有属于该区域的经纬度坐标集合(数组中每个""代表一个小区域),如朝阳区有2个
//mapView.addAnnotations(subAnnotations)
if aDistrict.polylines != nil {//所有坐标集合
//var polylines = Array<MAPolyline>()//将坐标整合后的区域(0x开头)
for polylineString in aDistrict.polylines {//从每个小区域的经纬度坐标字符串数组遍历
var polygon_array = coordinatesForString(polylineString,separatorIn: ",", separatorOut: ";")//将字符串中“;”转换为”,“后的小区域数组
//let polyline = CommonUtility.polyline(forCoordinateString: polylineString)
//polylines.append(polyline!)
polygon = MAPolygon(coordinates:&polygon_array, count: UInt(polygon_array.count))//将每个小区域进行填充,构造多边形
//print("polygon_array:\(polygon_array)")
let selectablePolygon: SelectableOverlay = SelectableOverlay(overlay: polygon)
pointLinesArr.append(polylineString)
overlays.append(selectablePolygon)
mapView.add(polygon)
}
print("pointLinesArr.count:\(pointLinesArr.count)")//小区域个数
//print(pointLinesArr)
//mapView.addOverlays(polylines)
mapView.addOverlays(overlays)
}
break
}
if mapView.overlays.count > 0 {
mapView.showOverlays(mapView.overlays, animated: true)//立马展示所画区域
print("mapView.overlays:\(String(describing: mapView.overlays))")
}
else {
mapView.showAnnotations(mapView.annotations, edgePadding: UIEdgeInsetsMake(100, 40, 40, 40), animated: true)
print("else")
}
}
/// - 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
}
// 点击定位小蓝点进行逆地理编码查询
func reverseGeocoding(){
let coordinate = currentLocation?.coordinate
// 构造 AMapReGeocodeSearchRequest 对象,配置查询参数(中心点坐标)
let regeo: AMapReGeocodeSearchRequest = AMapReGeocodeSearchRequest()
regeo.location = AMapGeoPoint.location(withLatitude: CGFloat(coordinate!.latitude), longitude: CGFloat(coordinate!.longitude))
// 进行逆地理编码查询
self.search!.aMapReGoecodeSearch(regeo)
}
// 定位回调
func mapView(_ mapView: MAMapView!, didUpdate userLocation: MAUserLocation!, updatingLocation: Bool) {
if updatingLocation {
currentLocation = userLocation.location
let coordinate = currentLocation?.coordinate
let circle_radius :MACircle = MACircle(center:CLLocationCoordinate2D(latitude: coordinate!.latitude, longitude: coordinate!.longitude), radius: 500)
mapView.add(circle_radius)
}
}
// 点击Annoation回调
func mapView(_ mapView: MAMapView!, didSelect view: MAAnnotationView!) {
// 若点击的是定位标注,则执行逆地理编码
if view.annotation.isKind(of: MAUserLocation.self){
reverseGeocoding()
}
}
// 逆地理编码回调
func onReGeocodeSearchDone(_ request: AMapReGeocodeSearchRequest!, response: AMapReGeocodeSearchResponse!) {
if (response.regeocode != nil) {
var title = response.regeocode.addressComponent.city
title = response.regeocode.addressComponent.province
//给定位标注的title和subtitle赋值,在气泡中显示定位点的地址信息
mapView?.userLocation.title = title
mapView?.userLocation.subtitle = response.regeocode.formattedAddress
}
}
deinit {
print("释放")
}
}