You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift 5解析多层GeoJSON及地图多边形交互实现咨询

实现点击多边形展示属性的方案如下:

核心思路

你当前的问题是MKPolygon原生类没有存储对应的GeoJSON属性字段,点击时无法关联到对应属性,我们通过自定义多边形子类绑定属性+手势检测点击命中区域的方案实现需求。


具体实现步骤

  1. 自定义多边形子类,新增属性存储字段
    继承MKPolygon新增属性用于存储对应限制区域的属性信息:
// 先完善Properties结构体,包含你需要展示的所有字段
struct Properties: Decodable {
    let zone_id: String
    let wkt: String
    let contact: String
    let lower_lim: String
    let upper_lim: String
    let status: String
}

// 自定义多边形类
class RestrictedZonePolygon: MKPolygon {
    var zoneProperties: Properties?
}
  1. 修改GeoJSON解析逻辑,绑定属性到多边形
    修改MapViewModel中showRestrictedZones方法的解析部分,将解析出的属性绑定到自定义多边形实例:
func showRestrictedZones(completion: @escaping ([MKOverlay]) -> ()) {
    guard let url = URL(string: "https://www.yeltsy.com/wp-content/uploads/2021/08/restrictions.json") else {
        fatalError("Unable to get geoJSON") }
    
    downloadData(fromURL: url) { (returnedData) in
        if let data = returnedData {
            var geoJson = [MKGeoJSONObject]()
            do {
                geoJson = try MKGeoJSONDecoder().decode(data)
            } catch {
                fatalError("Unable to decode GeoJSON")
            }
            var overlays = [MKOverlay]()
            let decoder = JSONDecoder()
            for item in geoJson {
                if let feature = item as? MKGeoJSONFeature {
                    // 解码当前Feature的属性
                    var properties: Properties?
                    if let propData = feature.properties {
                        properties = try? decoder.decode(Properties.self, from: propData)
                    }
                    for geo in feature.geometry {
                        if let polygon = geo as? MKPolygon {
                            // 生成自定义多边形实例,绑定属性
                            let restrictedPolygon = RestrictedZonePolygon(
                                points: polygon.points(), 
                                count: polygon.pointCount
                            )
                            restrictedPolygon.zoneProperties = properties
                            overlays.append(restrictedPolygon)
                        }
                    }
                }
            }
            DispatchQueue.main.async {
                completion(overlays)
            }
        }
    }
}
  1. 给地图添加点击手势,检测命中的多边形
    在MapView的makeUIView方法中给地图添加点击手势,同时在Coordinator中实现点击处理逻辑:
func makeUIView(context: Context) -> MKMapView {
    let view = mapData.mapView
    view.showsUserLocation = true
    view.delegate = context.coordinator
    // 添加点击手势
    let tapGesture = UITapGestureRecognizer(
        target: context.coordinator, 
        action: #selector(context.coordinator.handleMapTap(_:))
    )
    view.addGestureRecognizer(tapGesture)
    
    mapData.showRestrictedZones { (restrictions) in
        self.restrictions = restrictions
        view.addOverlays(self.restrictions)
    }
    return view
}
  1. 实现点击处理逻辑,展示属性信息
    在Coordinator类中实现点击检测方法,判断点击位置是否落在多边形内,命中后展示对应属性:
class Coordinator: NSObject, MKMapViewDelegate {
    // 原有rendererFor方法保留
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        if let polygon = overlay as? RestrictedZonePolygon {
            let renderer = MKPolygonRenderer(polygon: polygon)
            renderer.fillColor = UIColor.purple.withAlphaComponent(0.2)
            renderer.strokeColor = .purple.withAlphaComponent(0.7)
            return renderer
        }
        return MKOverlayRenderer(overlay: overlay)
    }
    
    // 新增点击处理方法
    @objc func handleMapTap(_ gesture: UITapGestureRecognizer) {
        guard let mapView = gesture.view as? MKMapView else { return }
        let tapPoint = gesture.location(in: mapView)
        let tapCoordinate = mapView.convert(tapPoint, toCoordinateFrom: mapView)
        let tapMapPoint = MKMapPoint(tapCoordinate)
        
        // 先清除之前添加的临时标注
        mapView.removeAnnotations(mapView.annotations.filter { $0 is MKPointAnnotation })
        
        // 遍历所有限制区域多边形,检测点击命中
        for overlay in mapView.overlays {
            guard let polygon = overlay as? RestrictedZonePolygon,
                  let properties = polygon.zoneProperties else { continue }
            let renderer = MKPolygonRenderer(polygon: polygon)
            let rendererPoint = renderer.point(for: tapMapPoint)
            // 判断点击点是否在多边形路径内
            if renderer.path.contains(rendererPoint) {
                // 命中后添加临时标注弹出信息,也可以自定义弹窗或者弹出SwiftUI Sheet展示完整信息
                let annotation = MKPointAnnotation()
                annotation.coordinate = tapCoordinate
                annotation.title = properties.zone_id
                annotation.subtitle = "高度范围:\(properties.lower_lim) ~ \(properties.upper_lim) | 状态:\(properties.status)"
                mapView.addAnnotation(annotation)
                mapView.selectAnnotation(annotation, animated: true)
                return
            }
        }
    }
}

可选优化

如果需要自定义弹窗样式,可以实现MKMapViewDelegate的mapView(_:viewFor:)方法自定义标注视图,也可以在命中多边形后通过ObservableObject把属性传递到SwiftUI层,弹出sheet展示完整的限制区域信息。

内容的提问来源于stack exchange,提问作者Alin Fusaru

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 23:42:03