Fit extension for map annotations

This commit is contained in:
Garth Vander Houwen 2023-01-15 10:25:24 -08:00
parent 5fd3215921
commit 547d250ec2
5 changed files with 46 additions and 51 deletions

View file

@ -69,6 +69,7 @@
DD964FBD296E6B01007C176F /* EmojiOnlyTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FBC296E6B01007C176F /* EmojiOnlyTextField.swift */; };
DD964FBF296E76EF007C176F /* WaypointFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FBE296E76EF007C176F /* WaypointFormView.swift */; };
DD964FC2297272AE007C176F /* WaypointEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FC1297272AE007C176F /* WaypointEntityExtension.swift */; };
DD964FC42974767D007C176F /* MapViewFitExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FC32974767D007C176F /* MapViewFitExtension.swift */; };
DD97E96628EFD9820056DDA4 /* MeshtasticLogo.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD97E96528EFD9820056DDA4 /* MeshtasticLogo.swift */; };
DD97E96828EFE9A00056DDA4 /* About.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD97E96728EFE9A00056DDA4 /* About.swift */; };
DD994B69295F88B60013760A /* IntervalEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD994B68295F88B60013760A /* IntervalEnums.swift */; };
@ -195,6 +196,7 @@
DD964FBE296E76EF007C176F /* WaypointFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaypointFormView.swift; sourceTree = "<group>"; };
DD964FC029724F6D007C176F /* MeshtasticDataModelV6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV6.xcdatamodel; sourceTree = "<group>"; };
DD964FC1297272AE007C176F /* WaypointEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaypointEntityExtension.swift; sourceTree = "<group>"; };
DD964FC32974767D007C176F /* MapViewFitExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewFitExtension.swift; sourceTree = "<group>"; };
DD97E96528EFD9820056DDA4 /* MeshtasticLogo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticLogo.swift; sourceTree = "<group>"; };
DD97E96728EFE9A00056DDA4 /* About.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = About.swift; sourceTree = "<group>"; };
DD994B68295F88B60013760A /* IntervalEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntervalEnums.swift; sourceTree = "<group>"; };
@ -282,6 +284,7 @@
C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */,
DD2AD8A7296D2DF9001FF0E7 /* MapViewSwiftUI.swift */,
DD964FBE296E76EF007C176F /* WaypointFormView.swift */,
DD964FC32974767D007C176F /* MapViewFitExtension.swift */,
);
path = Map;
sourceTree = "<group>";
@ -784,6 +787,7 @@
DD964FC2297272AE007C176F /* WaypointEntityExtension.swift in Sources */,
DD47E3CE26F103C600029299 /* NodeList.swift in Sources */,
DD8EBF43285058FA00426DCA /* DisplayConfig.swift in Sources */,
DD964FC42974767D007C176F /* MapViewFitExtension.swift in Sources */,
DD47E3D626F17ED900029299 /* CircleText.swift in Sources */,
DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */,
DD17E5DE277D49D400010EC2 /* storeforward.pb.swift in Sources */,

View file

@ -0,0 +1,37 @@
//
// MapViewFitExtension.swift
// Meshtastic
//
// Created by Garth Vander Houwen on 1/15/23.
//
import MapKit
extension MKMapView {
func fitAllAnnotations(with padding: UIEdgeInsets = UIEdgeInsets(top: 100, left: 100, bottom: 100, right: 100)) {
var zoomRect: MKMapRect = .null
annotations.forEach({
let annotationPoint = MKMapPoint($0.coordinate)
let pointRect = MKMapRect(x: annotationPoint.x, y: annotationPoint.y, width: 0.01, height: 0.01)
zoomRect = zoomRect.union(pointRect)
})
setVisibleMapRect(zoomRect, edgePadding: padding, animated: true)
}
func fit(annotations: [MKAnnotation], andShow show: Bool, with padding: UIEdgeInsets = UIEdgeInsets(top: 100, left: 100, bottom: 100, right: 100)) {
var zoomRect: MKMapRect = .null
annotations.forEach({
let aPoint = MKMapPoint($0.coordinate)
let rect = MKMapRect(x: aPoint.x, y: aPoint.y, width: 0.1, height: 0.1)
zoomRect = zoomRect.isNull ? rect : zoomRect.union(rect)
})
if show {
addAnnotations(annotations)
}
setVisibleMapRect(zoomRect, edgePadding: padding, animated: true)
}
}

View file

@ -14,7 +14,6 @@ struct MapViewSwiftUI: UIViewRepresentable {
let mapView = MKMapView()
let positions: [PositionEntity]
let waypoints: [WaypointEntity]
let region: MKCoordinateRegion
let mapViewType: MKMapType
// Offline Maps
@ -28,11 +27,10 @@ struct MapViewSwiftUI: UIViewRepresentable {
func makeUIView(context: Context) -> MKMapView {
// Parameters
mapView.addAnnotations(positions)
mapView.fit(annotations: positions, andShow: true)
mapView.addAnnotations(waypoints)
mapView.mapType = mapViewType
mapView.setRegion(region, animated: true)
mapView.setUserTrackingMode(.none, animated: false)
mapView.setUserTrackingMode(.none, animated: true)
// Other MKMapView Settings
mapView.isPitchEnabled = true
mapView.isRotateEnabled = true
@ -61,12 +59,8 @@ struct MapViewSwiftUI: UIViewRepresentable {
let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
let tilePath = documentsDirectory.appendingPathComponent("offline_map.mbtiles", isDirectory: false).path
if fileManager.fileExists(atPath: tilePath) {
//if let tilePath = Bundle.main.path(forResource: "offline_map", ofType: "mbtiles") {
print("Loading local map file")
if let overlay = LocalMBTileOverlay(mbTilePath: tilePath) {
overlay.canReplaceMapContent = false//customMapOverlay.canReplaceMapContent
mapView.addOverlay(overlay)
}
@ -79,9 +73,6 @@ struct MapViewSwiftUI: UIViewRepresentable {
self.loadedLastUpdatedLocalMapFile = self.lastUpdatedLocalMapFile
}
}
if dynamicRegion {
self.moveToMeshRegion(mapView)
}
mapView.removeAnnotations(mapView.annotations)
mapView.addAnnotations(positions)
mapView.addAnnotations(waypoints)
@ -91,53 +82,17 @@ struct MapViewSwiftUI: UIViewRepresentable {
return Coordinator(self)
}
func moveToMeshRegion(_ mapView: MKMapView) {
//go through the annotations and create a bounding box that encloses them
var minLat: CLLocationDegrees = 90.0
var maxLat: CLLocationDegrees = -90.0
var minLon: CLLocationDegrees = 180.0
var maxLon: CLLocationDegrees = -180.0
for annotation in mapView.annotations {
if annotation.isKind(of: MKAnnotation.self) {
minLat = min(minLat, annotation.coordinate.latitude)
maxLat = max(maxLat, annotation.coordinate.latitude)
minLon = min(minLon, annotation.coordinate.longitude)
maxLon = max(maxLon, annotation.coordinate.longitude)
}
}
//check if the mesh region looks sensible before we move to it. Otherwise we won't move the map (leave it at the current location)
if maxLat < minLat || (maxLat-minLat) > 5 || maxLon < minLon || (maxLon-minLon) > 5 {
return
} else if minLat == maxLat && minLon == maxLon {
//then we are focussed on a single point (probably because there is only one node with a position)
//widen that out a little (don't zoom way in to that point)
//0.001 degrees latitude is about 100m
//the mapView.regionThatFits call below will expand this out to a rectangle
minLat = minLat - 0.001
maxLat = maxLat + 0.001
}
let centerCoord = CLLocationCoordinate2D(latitude: (minLat+maxLat)/2, longitude: (minLon+maxLon)/2)
let span = MKCoordinateSpan(latitudeDelta: (maxLat-minLat)*1.5, longitudeDelta: (maxLon-minLon)*1.5)
let region = mapView.regionThatFits(MKCoordinateRegion(center: centerCoord, span: span))
mapView.setRegion(region, animated: true)
}
final class MapCoordinator: NSObject, MKMapViewDelegate, UIGestureRecognizerDelegate {
var parent: MapViewSwiftUI
var longPressRecognizer = UILongPressGestureRecognizer()
var overlays: [Overlay] = []
init(_ parent: MapViewSwiftUI) {
self.parent = parent
super.init()
self.longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressHandler))
self.longPressRecognizer.minimumPressDuration = 0.2
self.longPressRecognizer.minimumPressDuration = 0.3
self.longPressRecognizer.delegate = self
self.parent.mapView.addGestureRecognizer(longPressRecognizer)
self.overlays = []
@ -163,7 +118,6 @@ struct MapViewSwiftUI: UIViewRepresentable {
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "waypoint") as? MKMarkerAnnotationView ?? MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: "Waypoint")
annotationView.canShowCallout = true
if waypointAnnotation.icon == 0 {
print(waypointAnnotation.icon)
annotationView.glyphText = "📍"
} else {
annotationView.glyphText = String(UnicodeScalar(Int(waypointAnnotation.icon)) ?? "📍")

View file

@ -51,7 +51,7 @@ struct NodeDetail: View {
} else {
presentingWaypointForm = true
}
}, positions: annotations, waypoints: Array(waypoints), region: MKCoordinateRegion(center: nodeCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005)), mapViewType: mapType,
}, positions: annotations, waypoints: Array(waypoints), mapViewType: mapType,
customMapOverlay: self.customMapOverlay,
overlays: self.overlays
)

View file

@ -59,7 +59,7 @@ struct NodeMap: View {
presentingWaypointForm = true
}
}, positions: Array(positions), waypoints: Array(waypoints), region: MKCoordinateRegion(center: LocationHelper.currentLocation, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)), mapViewType: mapType,
}, positions: Array(positions), waypoints: Array(waypoints), mapViewType: mapType,
customMapOverlay: self.customMapOverlay,
overlays: self.overlays
)