iOS MapKit - 点击了哪个叠加层
iOS MapKit - Which overlay was tapped
我正在尝试构建一个在地图上显示某些区域 (polygons/overlays) 的应用程序,当点击多边形时,我想在控制台上打印 print("Polygon \(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"
}
好的,如果其他人需要的话,我找到了解决方案。
- 我向我的地图视图实例添加了一个 UITapGestureRecognizer。
- 我使用了 MKMapView 的 convert(_:toCoordinateFrom:) 将触摸点转换为地图坐标
- 我从该坐标创建了一个 MKMapPoint 并检查了 MKPolygon 渲染器路径是否包含该点
- 对于 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()
}
}
我正在尝试构建一个在地图上显示某些区域 (polygons/overlays) 的应用程序,当点击多边形时,我想在控制台上打印 print("Polygon \(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"
}
好的,如果其他人需要的话,我找到了解决方案。
- 我向我的地图视图实例添加了一个 UITapGestureRecognizer。
- 我使用了 MKMapView 的 convert(_:toCoordinateFrom:) 将触摸点转换为地图坐标
- 我从该坐标创建了一个 MKMapPoint 并检查了 MKPolygon 渲染器路径是否包含该点
- 对于 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()
}
}