Swift 5解析多层GeoJSON及地图多边形交互实现咨询
实现点击多边形展示属性的方案如下:
核心思路
你当前的问题是MKPolygon原生类没有存储对应的GeoJSON属性字段,点击时无法关联到对应属性,我们通过自定义多边形子类绑定属性+手势检测点击命中区域的方案实现需求。
具体实现步骤
- 自定义多边形子类,新增属性存储字段
继承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? }
- 修改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) } } } }
- 给地图添加点击手势,检测命中的多边形
在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 }
- 实现点击处理逻辑,展示属性信息
在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
相关产品推荐
相关产品推荐

