diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 464ac36c..e078d344 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -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 = ""; }; DD964FC029724F6D007C176F /* MeshtasticDataModelV6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV6.xcdatamodel; sourceTree = ""; }; DD964FC1297272AE007C176F /* WaypointEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaypointEntityExtension.swift; sourceTree = ""; }; + DD964FC32974767D007C176F /* MapViewFitExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewFitExtension.swift; sourceTree = ""; }; DD97E96528EFD9820056DDA4 /* MeshtasticLogo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticLogo.swift; sourceTree = ""; }; DD97E96728EFE9A00056DDA4 /* About.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = About.swift; sourceTree = ""; }; DD994B68295F88B60013760A /* IntervalEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntervalEnums.swift; sourceTree = ""; }; @@ -282,6 +284,7 @@ C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */, DD2AD8A7296D2DF9001FF0E7 /* MapViewSwiftUI.swift */, DD964FBE296E76EF007C176F /* WaypointFormView.swift */, + DD964FC32974767D007C176F /* MapViewFitExtension.swift */, ); path = Map; sourceTree = ""; @@ -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 */, diff --git a/Meshtastic/Views/Map/MapViewFitExtension.swift b/Meshtastic/Views/Map/MapViewFitExtension.swift new file mode 100644 index 00000000..52c13eaa --- /dev/null +++ b/Meshtastic/Views/Map/MapViewFitExtension.swift @@ -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) + } +} diff --git a/Meshtastic/Views/Map/MapViewSwiftUI.swift b/Meshtastic/Views/Map/MapViewSwiftUI.swift index 171c9c58..a8e37692 100644 --- a/Meshtastic/Views/Map/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/MapViewSwiftUI.swift @@ -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)) ?? "📍") diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index 71117497..f1d8a03b 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -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 ) diff --git a/Meshtastic/Views/Nodes/NodeMap.swift b/Meshtastic/Views/Nodes/NodeMap.swift index 06593a6f..30aede82 100644 --- a/Meshtastic/Views/Nodes/NodeMap.swift +++ b/Meshtastic/Views/Nodes/NodeMap.swift @@ -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 )