iOS MapKit-点击了哪个覆盖



我正在尝试构建一个应用程序,显示地图上的一些区域(多边形/覆盖层(,当点击多边形时,我想在控制台打印上打印("多边形\(zone_id(已被点击"(。多边形是从GeoJSON文件中渲染的,我们也可以在属性功能中找到zone_id。到目前为止,我在地图上绘制了覆盖图,但我被卡住了,我很感激一些指导。

我将粘贴到目前为止的代码,以及GeoJSON文件的一个片段。

import SwiftUI
import CoreLocation
struct Home: View {
   
  @StateObject var mapData = MapViewModel()
   
  var body: some View {
    ZStack{
       
      MapView()
        .environmentObject(mapData)
        .ignoresSafeArea(.all, edges: .all)
         
    }
  }
}
struct Home_Previews: PreviewProvider {
  static var previews: some View {
    Home()
  }
}
import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {
   
  @EnvironmentObject var mapData: MapViewModel
   
  @State var restrictions: [MKOverlay] = []
   
  func makeCoordinator() -> Coordinator {
    return MapView.Coordinator()
  }
   
  func makeUIView(context: Context) -> MKMapView {
     
    let view = mapData.mapView
     
    view.showsUserLocation = true
    view.delegate = context.coordinator
     
    mapData.showRestrictedZones { (restrictions) in
      self.restrictions = restrictions
      view.addOverlays(self.restrictions)
    }
    return view
  }
   
  func updateUIView(_ uiView: MKMapView, context: Context) {
     
  }
   
  class Coordinator: NSObject, MKMapViewDelegate {
     
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
      if let polygon = overlay as? MKPolygon {
        let renderer = MKPolygonRenderer(polygon: polygon)
        renderer.fillColor = UIColor.purple.withAlphaComponent(0.2)
        renderer.strokeColor = .purple.withAlphaComponent(0.7)
         
        return renderer
      }
      return MKOverlayRenderer(overlay: overlay)
    }
  }
}
import SwiftUI
import MapKit
// All Map Data Goes here...
class MapViewModel: NSObject, ObservableObject {
   
  @Published var mapView = MKMapView()
   
   
  
  // Decode GeoJSON from the server
  func showRestrictedZones(completion: @escaping ([MKOverlay]) -> ()) {
    guard let url = URL(string: "https://flightplan.romatsa.ro/init/static/zone_restrictionate_uav.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]()
        for item in geoJson {
          if let feature = item as? MKGeoJSONFeature {
            for geo in feature.geometry {
              if let polygon = geo as? MKPolygon {
                overlays.append(polygon)
                 
              }
            }
          }
        }
        DispatchQueue.main.async {
          completion(overlays)
        }
      }
    }
  }
   
  func downloadData( fromURL url: URL, completion: @escaping (_ data: Data?) -> ()) {
    URLSession.shared.dataTask(with: url) { (data, response, error) in
      guard
        let data = data,
        error == nil,
        let response = response as? HTTPURLResponse,
        response.statusCode >= 200 && response.statusCode < 300 else {
        print("Error downloading data.")
        completion(nil)
        return
      }
      completion(data)
    }
    .resume()
  }
}

这里是GeoJSON结构的一个片段

{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": "zone_restrictionate_uav.2120",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
24.98812963,
44.10877275
],
[
24.98806588,
44.1070722
],
[
24.98537796,
44.10717296
],
[
24.9854417,
44.10887351
],
[
24.98812963,
44.10877275
]
]
]
},
"geometry_name": "the_geom",
"properties": {
"zone_id": "RZ 2120",
"human_readable_definition": "POLYGON: 440631.5819N 0245917.2667E- 440625.4599N 0245917.0372E- 440625.8227N 0245907.3606E- 440631.9446N 0245907.5901E- 440631.5819N 0245917.2667E",
"wkt": "POLYGON((24.9881296281 44.1087727458,24.9880658794 44.1070721972,24.9853779561 44.1071729596,24.9854417047 44.1088735083,24.9881296281 44.1087727458))",
"lower_lim": "GND",
"upper_lim": "120m AGL",
"contact": "mailto: aerofoto@mapn.ro",
"status": "RESTRICTED"
}
},
{
"type": "Feature",
"id": "zone_restrictionate_uav.2121",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
26.44760265,
44.13453494
],
[
26.43440247,
44.11089351
],
[
26.38955244,
44.1359355
],
[
26.40275262,
44.15957693
],
[
26.44760265,
44.13453494
]
]
]
},
"geometry_name": "the_geom",
"properties": {
"zone_id": "RZ 2121",
"human_readable_definition": "POLYGON: 440804.3258N 0262651.3695E- 440639.2166N 0262603.8489E- 440809.3678N 0262322.3888E- 440934.4769N 0262409.9094E- 440804.3258N 0262651.3695E",
"wkt": "POLYGON((26.4476026463 44.1345349397,26.4344024672 44.1108935127,26.3895524427 44.1359355018,26.4027526218 44.1595769288,26.4476026463 44.1345349397))",
"lower_lim": "GND",
"upper_lim": "120m AGL",
"contact": "mailto: aerofoto@mapn.ro",
"status": "RESTRICTED"
}
}
],
"totalFeatures": 339,
"numberMatched": 339,
"numberReturned": 339,
"timeStamp": "2021-08-15T11:39:26.055Z"
}

好的,如果其他人需要的话,我找到了一个解决方案。

  1. 我在地图视图实例中添加了一个UIMapGestureRecognizer
  2. 我使用MKMapView的convert(_:toCoordinateFrom:(将触摸点转换为地图坐标
  3. 我从该坐标创建了一个MKMapPoint,并检查MKPolygon渲染器路径是否包含该点
  4. 对于MKPolygon,作为一个MKShape,我使用.title属性来分配从GeoJSON解析的zone_id值

所以我能够识别出哪个多边形被点击了。

import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {

var tapCoordinates: Binding<CLLocationCoordinate2D>
var polygonID: Binding<String>
@EnvironmentObject var mapData: MapViewModel
@State var restrictions: [MKOverlay] = []
@State var restrictionsData: [RestrictionInfo] = []

func makeCoordinator() -> Coordinator {
return MapView.Coordinator(self, tapCoordinatesBinding: tapCoordinates, polygonTitle: polygonID)
}

func makeUIView(context: Context) -> MKMapView {

mapData.mapView.delegate = context.coordinator

let view = mapData.mapView


view.showsUserLocation = true
view.delegate = context.coordinator

mapData.showRestrictedZones { (restriction) in
self.restrictions = restriction
view.addOverlays(self.restrictions)
}
return view
}

func updateUIView(_ uiView: MKMapView, context: Context) {

}

class Coordinator: NSObject, MKMapViewDelegate, UIGestureRecognizerDelegate {
var parent: MapView
var polygonTitle: Binding<String>
var gRecognizer = UITapGestureRecognizer()

var tapCoordinatesBinding: Binding<CLLocationCoordinate2D>
var coordinate = CLLocationCoordinate2D()

init(_ parent: MapView, tapCoordinatesBinding: Binding<CLLocationCoordinate2D>, polygonTitle: Binding<String>) {
self.parent = parent
self.tapCoordinatesBinding = tapCoordinatesBinding
self.polygonTitle = polygonTitle
super.init()
self.gRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapHandler))
self.gRecognizer.delegate = self
self.parent.mapData.mapView.addGestureRecognizer(gRecognizer)
}


@objc func tapHandler(_ gesture: UITapGestureRecognizer) {
// position on the screen, CGPoint
let location = gRecognizer.location(in: self.parent.mapData.mapView)
// position on the map, CLLocationCoordinate2D
coordinate = self.parent.mapData.mapView.convert(location, toCoordinateFrom: self.parent.mapData.mapView)
tapCoordinatesBinding.wrappedValue = coordinate

for overlay: MKOverlay in self.parent.mapData.mapView.overlays {
if let polygon = overlay as? MKPolygon {
let renderer = MKPolygonRenderer(polygon: polygon)
let mapPoint = MKMapPoint(coordinate)
let rendererPoint = renderer.point(for: mapPoint)
if renderer.path.contains(rendererPoint) {
print("Tap inside polygon")
print("Polygon (polygon.title ?? "no value") has been tapped")
polygonTitle.wrappedValue = polygon.title!
}
}
}
}


func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let polygon = overlay as? MKPolygon {
let renderer = MKPolygonRenderer(polygon: polygon)
renderer.fillColor = UIColor.purple.withAlphaComponent(0.2)
renderer.strokeColor = .purple.withAlphaComponent(0.7)
renderer.lineWidth = 2

return renderer
}
return MKOverlayRenderer(overlay: overlay)
}
}
}
import SwiftUI
import MapKit
// All Map Data Goes here...
class MapViewModel: NSObject, ObservableObject {

@Published var mapView = MKMapView()



func restrictionsInfo(completion: @escaping ([RestrictionInfo]) -> ()) {
guard let url = URL(string: "https://flightplan.romatsa.ro/init/static/zone_restrictionate_uav.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 restrictionsInfo = [RestrictionInfo]()
for item in geoJson {
if let feature = item as? MKGeoJSONFeature {
let propData = feature.properties!
for geo in feature.geometry {
if geo is MKPolygon {
let polygonInfo = try? JSONDecoder.init().decode(RestrictionInfo.self, from: propData)
restrictionsInfo.append(polygonInfo!)
}
}
}
}
DispatchQueue.main.async {
completion(restrictionsInfo)
}
}
}
}


// Decode GeoJSON from the server
func showRestrictedZones(completion: @escaping ([MKOverlay]) -> ()) {
guard let url = URL(string: "https://flightplan.romatsa.ro/init/static/zone_restrictionate_uav.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]()
for item in geoJson {
if let feature = item as? MKGeoJSONFeature {
let propData = feature.properties!
for geo in feature.geometry {
if let polygon = geo as? MKPolygon {
let polygonInfo = try? JSONDecoder.init().decode(RestrictionInfo.self, from: propData)
polygon.title = polygonInfo?.zone_id
overlays.append(polygon)
}
}
}
}
DispatchQueue.main.async {
completion(overlays)
}
}
}
}

func downloadData( fromURL url: URL, completion: @escaping (_ data: Data?) -> ()) {
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard
let data = data,
error == nil,
let response = response as? HTTPURLResponse,
response.statusCode >= 200 && response.statusCode < 300 else {
print("Error downloading data.")
completion(nil)
return
}
completion(data)
}
.resume()
}
}

最新更新