From 50943ea5fc269b6065f44cb27258ed2045aec0f2 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 13 Jan 2023 22:30:10 -0800 Subject: [PATCH] More waypoints --- Meshtastic.xcodeproj/project.pbxproj | 8 +- Meshtastic/Helpers/BLEManager.swift | 4 +- Meshtastic/Helpers/MeshPackets.swift | 65 ++++ .../Meshtastic.xcdatamodeld/.xccurrentversion | 2 +- .../contents | 3 +- .../contents | 280 ++++++++++++++++++ .../Persistence/WaypointEntityExtension.swift | 54 ++++ Meshtastic/Protobufs/mesh.pb.swift | 12 +- Meshtastic/Views/Map/LocalMBTileOverlay.swift | 21 +- Meshtastic/Views/Map/MapViewModule.swift | 2 +- Meshtastic/Views/Map/MapViewSwiftUI.swift | 36 ++- Meshtastic/Views/Map/WaypointFormView.swift | 1 + Meshtastic/Views/Nodes/NodeDetail.swift | 14 +- Meshtastic/Views/Nodes/NodeMap.swift | 44 +-- 14 files changed, 461 insertions(+), 85 deletions(-) create mode 100644 Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV6.xcdatamodel/contents create mode 100644 Meshtastic/Persistence/WaypointEntityExtension.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index b145995c..464ac36c 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -68,6 +68,7 @@ DD913639270DFF4C00D7ACF3 /* LocalNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */; }; 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 */; }; 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 */; }; @@ -192,6 +193,8 @@ DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotificationManager.swift; sourceTree = ""; }; DD964FBC296E6B01007C176F /* EmojiOnlyTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiOnlyTextField.swift; sourceTree = ""; }; 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 = ""; }; 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 = ""; }; @@ -565,6 +568,7 @@ DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */, DD58C5F12919AD3C00D5BEFB /* ChannelEntityExtension.swift */, DD3CC6C128EB9D4900FA9159 /* UpdateCoreData.swift */, + DD964FC1297272AE007C176F /* WaypointEntityExtension.swift */, ); path = Persistence; sourceTree = ""; @@ -777,6 +781,7 @@ DDB6ABDB28B0AC6000384BA1 /* DistanceText.swift in Sources */, C9A7BC1027759A9600760B50 /* PositionAnnotationView.swift in Sources */, DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */, + DD964FC2297272AE007C176F /* WaypointEntityExtension.swift in Sources */, DD47E3CE26F103C600029299 /* NodeList.swift in Sources */, DD8EBF43285058FA00426DCA /* DisplayConfig.swift in Sources */, DD47E3D626F17ED900029299 /* CircleText.swift in Sources */, @@ -1226,13 +1231,14 @@ DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + DD964FC029724F6D007C176F /* MeshtasticDataModelV6.xcdatamodel */, DD457BC4295D5E35004BCE4D /* MeshtasticDataModelV5.xcdatamodel */, DDEE03EC29544A1000FCAD57 /* MeshtasticDataModelV4.xcdatamodel */, DDCDC69A29467643004C1DDA /* MeshtasticDataModelV3.xcdatamodel */, DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */, DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */, ); - currentVersion = DD457BC4295D5E35004BCE4D /* MeshtasticDataModelV5.xcdatamodel */; + currentVersion = DD964FC029724F6D007C176F /* MeshtasticDataModelV6.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 5dbd0f8b..65cfc18c 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -506,7 +506,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { case .positionApp: positionPacket(packet: decodedInfo.packet, context: context!) case .waypointApp: - MeshLogger.log("πŸ•ΈοΈ MESH PACKET received for Waypoint App UNHANDLED \(try! decodedInfo.packet.jsonString())") + waypointPacket(packet: decodedInfo.packet, context: context!) case .nodeinfoApp: if !invalidVersion { nodeInfoAppPacket(packet: decodedInfo.packet, context: context!) } case .routingApp: @@ -627,7 +627,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { if preferredPeripheral != nil && preferredPeripheral?.peripheral != nil { connectTo(peripheral: preferredPeripheral!.peripheral) } - let nodeName = connectedPeripheral!.peripheral.name ?? NSLocalizedString("unknown", comment: NSLocalizedString("unknown", comment: "Unknown")) + let nodeName = connectedPeripheral?.peripheral.name ?? NSLocalizedString("unknown", comment: NSLocalizedString("unknown", comment: "Unknown")) let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.textmessage.send.failed %@", comment: "Message Send Failed, not properly connected to %@"), nodeName) MeshLogger.log("🚫 \(logString)") diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index d07bb2ba..10406180 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -1311,3 +1311,68 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM } } } + +func waypointPacket (packet: MeshPacket, context: NSManagedObjectContext) { + + let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.waypoint.received %@", comment: "Waypoint Packet received from node: %@"), String(packet.from)) + MeshLogger.log("πŸ“ \(logString)") + + let fetchWaypointRequest: NSFetchRequest = NSFetchRequest.init(entityName: "WaypointEntity") + fetchWaypointRequest.predicate = NSPredicate(format: "id == %lld", Int64(packet.id)) + + do { + + if let waypointMessage = try? Waypoint(serializedData: packet.decoded.payload) { + // Don't save empty waypoint packets + if waypointMessage.longitudeI > 0 || waypointMessage.latitudeI > 0 { + let fetchedWaypoint = try context.fetch(fetchWaypointRequest) as! [WaypointEntity] + if fetchedWaypoint.isEmpty { + let waypoint = WaypointEntity(context: context) + + waypoint.id = Int64(packet.id) + waypoint.name = waypointMessage.name + waypoint.longDescription = waypointMessage.description_p + waypoint.latitudeI = waypointMessage.latitudeI + waypoint.longitudeI = waypointMessage.longitudeI + //waypoint.icon = Int32(waypointMessage.icon) + waypoint.locked = waypointMessage.locked + if waypointMessage.expire != 0 { + waypoint.expire = Date(timeIntervalSince1970: TimeInterval(Int64(waypointMessage.expire))) + } + do { + try context.save() + print("πŸ’Ύ Updated Node Waypoint App Packet For: \(waypoint.id)") + } catch { + context.rollback() + let nsError = error as NSError + print("πŸ’₯ Error Saving WaypointEntity from WAYPOINT_APP \(nsError)") + } + } else { + fetchedWaypoint[0].id = Int64(packet.id) + fetchedWaypoint[0].name = waypointMessage.name + fetchedWaypoint[0].longDescription = waypointMessage.description_p + fetchedWaypoint[0].latitudeI = waypointMessage.latitudeI + fetchedWaypoint[0].longitudeI = waypointMessage.longitudeI + //fetchedWaypoint[0].icon = Int32(waypointMessage.icon) + fetchedWaypoint[0].locked = waypointMessage.locked + if waypointMessage.expire != 0 { + fetchedWaypoint[0].expire = Date(timeIntervalSince1970: TimeInterval(Int64(waypointMessage.expire))) + } + do { + try context.save() + print("πŸ’Ύ Updated Node Waypoint App Packet For: \(fetchedWaypoint[0].id)") + } catch { + context.rollback() + let nsError = error as NSError + print("πŸ’₯ Error Saving WaypointEntity from WAYPOINT_APP \(nsError)") + } + } + } else { + print("πŸ’₯ Empty WAYPOINT_APP Packet") + print(try! packet.jsonString()) + } + } + } catch { + print("πŸ’₯ Error Deserializing WAYPOINT_APP packet.") + } +} diff --git a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion index 1b3c637e..a13f6b2e 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion +++ b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - MeshtasticDataModelV5.xcdatamodel + MeshtasticDataModelV6.xcdatamodel diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV5.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV5.xcdatamodel/contents index 1f463d49..4bb57e13 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV5.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV5.xcdatamodel/contents @@ -1,5 +1,5 @@ - + @@ -269,6 +269,7 @@ + diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV6.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV6.xcdatamodel/contents new file mode 100644 index 00000000..792586e0 --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV6.xcdatamodel/contents @@ -0,0 +1,280 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meshtastic/Persistence/WaypointEntityExtension.swift b/Meshtastic/Persistence/WaypointEntityExtension.swift new file mode 100644 index 00000000..1ee65fa4 --- /dev/null +++ b/Meshtastic/Persistence/WaypointEntityExtension.swift @@ -0,0 +1,54 @@ +// +// WaypointEntityExtension.swift +// Meshtastic +// +// Copyright (c) Garth Vander Houwen 1/13/23. +// +import CoreData +import CoreLocation +import MapKit +import SwiftUI + +extension WaypointEntity { + + var latitude: Double? { + + let d = Double(latitudeI) + if d == 0 { + return 0 + } + return d / 1e7 + } + + var longitude: Double? { + + let d = Double(longitudeI) + if d == 0 { + return 0 + } + return d / 1e7 + } + + var waypointCoordinate: CLLocationCoordinate2D? { + if latitudeI != 0 && longitudeI != 0 { + let coord = CLLocationCoordinate2D(latitude: latitude!, longitude: longitude!) + return coord + } else { + return nil + } + } + + var annotaton: MKPointAnnotation { + let pointAnn = MKPointAnnotation() + if waypointCoordinate != nil { + pointAnn.coordinate = waypointCoordinate! + } + return pointAnn + } +} + +extension WaypointEntity: MKAnnotation { + public var coordinate: CLLocationCoordinate2D { waypointCoordinate ?? LocationHelper.DefaultLocation } + public var title: String? { self.title ?? "" } + public var subtitle: String? { self.description } +} diff --git a/Meshtastic/Protobufs/mesh.pb.swift b/Meshtastic/Protobufs/mesh.pb.swift index d168c267..9a7101aa 100644 --- a/Meshtastic/Protobufs/mesh.pb.swift +++ b/Meshtastic/Protobufs/mesh.pb.swift @@ -1156,7 +1156,7 @@ struct Waypoint { /// /// Designator icon for the waypoint in the form of a unicode emoji - var emoji: UInt32 = 0 + var icon: UInt32 = 0 var unknownFields = SwiftProtobuf.UnknownStorage() @@ -2782,7 +2782,7 @@ extension Waypoint: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB 5: .same(proto: "locked"), 6: .same(proto: "name"), 7: .same(proto: "description"), - 8: .same(proto: "emoji"), + 8: .same(proto: "icon"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -2798,7 +2798,7 @@ extension Waypoint: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB case 5: try { try decoder.decodeSingularBoolField(value: &self.locked) }() case 6: try { try decoder.decodeSingularStringField(value: &self.name) }() case 7: try { try decoder.decodeSingularStringField(value: &self.description_p) }() - case 8: try { try decoder.decodeSingularFixed32Field(value: &self.emoji) }() + case 8: try { try decoder.decodeSingularFixed32Field(value: &self.icon) }() default: break } } @@ -2826,8 +2826,8 @@ extension Waypoint: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB if !self.description_p.isEmpty { try visitor.visitSingularStringField(value: self.description_p, fieldNumber: 7) } - if self.emoji != 0 { - try visitor.visitSingularFixed32Field(value: self.emoji, fieldNumber: 8) + if self.icon != 0 { + try visitor.visitSingularFixed32Field(value: self.icon, fieldNumber: 8) } try unknownFields.traverse(visitor: &visitor) } @@ -2840,7 +2840,7 @@ extension Waypoint: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB if lhs.locked != rhs.locked {return false} if lhs.name != rhs.name {return false} if lhs.description_p != rhs.description_p {return false} - if lhs.emoji != rhs.emoji {return false} + if lhs.icon != rhs.icon {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/Meshtastic/Views/Map/LocalMBTileOverlay.swift b/Meshtastic/Views/Map/LocalMBTileOverlay.swift index 83409f4b..11c5af3e 100644 --- a/Meshtastic/Views/Map/LocalMBTileOverlay.swift +++ b/Meshtastic/Views/Map/LocalMBTileOverlay.swift @@ -2,7 +2,7 @@ // LocalMBTileOverlay.swift // MeshtasticApple // -// Created by Joshua Pirihi on 16/01/22. +// Copyright(c) Joshua Pirihi 16/01/22. // import UIKit @@ -41,9 +41,7 @@ enum MapTileError: Error { class LocalMBTileOverlay: MKTileOverlay { var path: String! - var mb: Connection! - private var _boundingMapRect: MKMapRect! override var boundingMapRect: MKMapRect { get { @@ -55,7 +53,6 @@ class LocalMBTileOverlay: MKTileOverlay { super.init(urlTemplate: nil) self.path = path - do { self.mb = try Connection(self.path, readonly: true) let metadata = Table("metadata") @@ -87,43 +84,29 @@ class LocalMBTileOverlay: MKTileOverlay { ] self._boundingMapRect = MKMapRect(coordinates: coords) - } catch { print("πŸ’₯ Map tile error: \(error)") return nil } - - } override func loadTile(at path: MKTileOverlayPath, result: @escaping (Data?, Error?) -> Void) { - //} - - //override func loadTile(at path: MKTileOverlayPath) async throws -> Data { - let tileX = Int64(path.x) let tileY = Int64(path.y) - let tileZ = Int64(path.z) - + let tileZ = Int64(path.z) let tileData = Expression("tile_data") let zoomLevel = Expression("zoom_level") let tileColumn = Expression("tile_column") let tileRow = Expression("tile_row") if let dataQuery = try? self.mb.pluck(Table("tiles").select(tileData).filter(zoomLevel == tileZ).filter(tileColumn == tileX).filter(tileRow == tileY)) { - let data = Data(bytes: dataQuery[tileData].bytes, count: dataQuery[tileData].bytes.count)//dataQuery![tileData].bytes - - //return data result(data, nil) - } else { print("πŸ’₯ No tile here: x:\(tileX) y:\(tileY) z:\(tileZ)") - //return Data() let error = NSError(domain: "LocalMBTileOverlay", code: 1, userInfo: ["reason": "no_tile"]) result(nil, error) } } - } diff --git a/Meshtastic/Views/Map/MapViewModule.swift b/Meshtastic/Views/Map/MapViewModule.swift index 4f782173..cba18980 100644 --- a/Meshtastic/Views/Map/MapViewModule.swift +++ b/Meshtastic/Views/Map/MapViewModule.swift @@ -153,7 +153,7 @@ // // mapView.isScrollEnabled = self.userTrackingMode == MKUserTrackingMode.none ? self.scrollEnabled : false // -// if let scrollBoundary = self.scrollBoundaries, (mapView.cameraBoundary?.region.center.latitude != scrollBoundary.center.latitude || mapView.cameraBoundary?.region.center.longitude != scrollBoundary.center.longitude || mapView.cameraBoundary?.region.span.latitudeDelta != scrollBoundary.span.latitudeDelta || mapView.cameraBoundary?.region.span.longitudeDelta != scrollBoundary.span.longitudeDelta) { +// if let scrollBoundary = self.scrollBoundaries, (mapView.cameraBoundary?.region.center.latitude != scrollBoundary.center.latitude || mapView.cameraBoundary?.region.center.longitude != scrollBoundary.center.longitude || mapView.camera Boundary?.region.span.latitudeDelta != scrollBoundary.span.latitudeDelta || mapView.cameraBoundary?.region.span.longitudeDelta != scrollBoundary.span.longitudeDelta) { // mapView.cameraBoundary = MKMapView.CameraBoundary(coordinateRegion: scrollBoundary) // } else if self.scrollBoundaries == nil && mapView.cameraBoundary != nil { // mapView.cameraBoundary = nil diff --git a/Meshtastic/Views/Map/MapViewSwiftUI.swift b/Meshtastic/Views/Map/MapViewSwiftUI.swift index 84256020..cfcbd5b3 100644 --- a/Meshtastic/Views/Map/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/MapViewSwiftUI.swift @@ -2,7 +2,7 @@ // MapViewSwitUI.swift // Meshtastic // -// Copyright(c) Garth Vander Houwen 1/9/23. +// Copyright(c) Josh Pirihi & Garth Vander Houwen 1/16/22. // import SwiftUI @@ -13,6 +13,7 @@ struct MapViewSwiftUI: UIViewRepresentable { var onMarkerTap: (_ waypointCoordinate: CLLocationCoordinate2D? ) -> Void let mapView = MKMapView() let positions: [PositionEntity] + let waypoints: [WaypointEntity] let region: MKCoordinateRegion let mapViewType: MKMapType @@ -26,18 +27,24 @@ struct MapViewSwiftUI: UIViewRepresentable { let dynamicRegion: Bool = true func makeUIView(context: Context) -> MKMapView { + // Parameters + mapView.addAnnotations(positions) mapView.mapType = mapViewType mapView.setRegion(region, animated: true) - mapView.isRotateEnabled = true - mapView.isPitchEnabled = true - mapView.showsBuildings = true; - mapView.addAnnotations(positions) - mapView.showsUserLocation = true mapView.setUserTrackingMode(.none, animated: false) + // Other MKMapView Settings + mapView.isPitchEnabled = true + mapView.isRotateEnabled = true + mapView.isScrollEnabled = true + mapView.isZoomEnabled = true + mapView.showsBuildings = true mapView.showsCompass = true mapView.showsScale = true - mapView.isZoomEnabled = true - mapView.isScrollEnabled = true + mapView.showsTraffic = true + mapView.showsUserLocation = true + #if targetEnvironment(macCatalyst) + mapView.showsZoomControls = true + #endif mapView.delegate = context.coordinator return mapView } @@ -149,9 +156,14 @@ struct MapViewSwiftUI: UIViewRepresentable { annotationView.markerTintColor = UIColor(.accentColor) annotationView.titleVisibility = .visible return annotationView - case _ as WaypointEntity: - return nil - + case let waypointAnnotation as WaypointEntity: + let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "waypoint") as? MKMarkerAnnotationView ?? MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: "Waypoint") + annotationView.canShowCallout = true + annotationView.glyphText = "πŸͺ§" + annotationView.clusteringIdentifier = "waypointGroup" + annotationView.markerTintColor = UIColor(.green) + annotationView.titleVisibility = .visible + return annotationView default: return nil } } @@ -161,7 +173,6 @@ struct MapViewSwiftUI: UIViewRepresentable { let location = longPressRecognizer.location(in: self.parent.mapView) // Map Coordinate - CLLocationCoordinate2D let coordinate = self.parent.mapView.convert(location, toCoordinateFrom: self.parent.mapView) - // Add annotation: let annotation = MKPointAnnotation() annotation.title = "Dropped Pin" @@ -244,7 +255,6 @@ struct MapViewSwiftUI: UIViewRepresentable { maximumZoomLevel: Int? = nil, defaultTile: DefaultTile? = nil ) { - self.mapName = mapName self.tileType = tileType self.canReplaceMapContent = canReplaceMapContent diff --git a/Meshtastic/Views/Map/WaypointFormView.swift b/Meshtastic/Views/Map/WaypointFormView.swift index 7af0dca7..6433ed85 100644 --- a/Meshtastic/Views/Map/WaypointFormView.swift +++ b/Meshtastic/Views/Map/WaypointFormView.swift @@ -27,6 +27,7 @@ struct WaypointFormView: View { Section(header: Text("Waypoint")) { HStack { Text("Location: \(String(format: "%.5f", coordinate.latitude ) + "," + String(format: "%.5f", coordinate.longitude ))") + .textSelection(.enabled) .foregroundColor(Color.gray) .font(.caption2) if coordinate.latitude != LocationHelper.DefaultLocation.latitude && coordinate.longitude != LocationHelper.DefaultLocation.longitude { diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index 8278b6c6..4aad6646 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -28,6 +28,9 @@ struct NodeDetail: View { var node: NodeInfoEntity + @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)], animation: .default) + private var waypoints: FetchedResults + var body: some View { let hwModelString = node.user?.hwModel ?? "UNSET" @@ -44,16 +47,16 @@ struct NodeDetail: View { MapViewSwiftUI(onMarkerTap: { coord in presentingWaypointForm = true waypointCoordinate = coord - }, positions: annotations, region: MKCoordinateRegion(center: nodeCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005)), mapViewType: mapType, + }, positions: annotations, waypoints: Array(waypoints), region: MKCoordinateRegion(center: nodeCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005)), mapViewType: mapType, customMapOverlay: self.customMapOverlay, overlays: self.overlays ) VStack { Spacer() Text(mostRecent.satsInView > 0 ? "Sats: \(mostRecent.satsInView)" : " ") - .font(.caption2) - - Picker("", selection: $mapType) { + .font(.caption) + .padding() + Picker("Map Type", selection: $mapType) { Text("Standard").tag(MKMapType.standard) Text("Muted").tag(MKMapType.mutedStandard) Text("Hybrid").tag(MKMapType.hybrid) @@ -61,8 +64,7 @@ struct NodeDetail: View { Text("Satellite").tag(MKMapType.satellite) Text("Sat Flyover").tag(MKMapType.satelliteFlyover) } - .pickerStyle(SegmentedPickerStyle()) - .padding(.bottom, 30) + .pickerStyle(.menu) } } .ignoresSafeArea(.all, edges: [.top, .leading, .trailing]) diff --git a/Meshtastic/Views/Nodes/NodeMap.swift b/Meshtastic/Views/Nodes/NodeMap.swift index fafc1e9b..7f5498a7 100644 --- a/Meshtastic/Views/Nodes/NodeMap.swift +++ b/Meshtastic/Views/Nodes/NodeMap.swift @@ -32,6 +32,9 @@ struct NodeMap: View { @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "time", ascending: false)], animation: .default) private var positions: FetchedResults + @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)], animation: .default) + private var waypoints: FetchedResults + @State private var mapType: MKMapType = .standard @State var waypointCoordinate: CLLocationCoordinate2D? @State private var presentingWaypointForm = false @@ -42,50 +45,29 @@ struct NodeMap: View { ) @State private var overlays: [MapViewSwiftUI.Overlay] = [] - //@State private var showLabels: Bool = false - //@State private var zoomEnabled: Bool = true - //@State private var showZoomScale: Bool = true - //@State private var useMinZoomBoundary: Bool = false - //@State private var minZoom: Double = 0 - //@State private var useMaxZoomBoundary: Bool = false - //@State private var maxZoom: Double = 3000000 - //@State private var scrollEnabled: Bool = true - //@State private var useScrollBoundaries: Bool = false - //@State private var scrollBoundaries: MKCoordinateRegion = MKCoordinateRegion() - //@State private var rotationEnabled: Bool = true - //@State private var showCompassWhenRotated: Bool = true - //@State private var showUserLocation: Bool = true - //@State private var userTrackingMode: MKUserTrackingMode = MKUserTrackingMode.none - //@State private var userLocation: CLLocationCoordinate2D? = LocationHelper.currentLocation - //@State private var showAnnotations: Bool = true - //@State private var annotations: [MKPointAnnotation] = [] - //@State private var showOverlays: Bool = true - //@State private var showMapCenter: Bool = false - var body: some View { NavigationStack { ZStack { + MapViewSwiftUI(onMarkerTap: { coord in presentingWaypointForm = true waypointCoordinate = coord - }, positions: Array(positions), region: MKCoordinateRegion(center: LocationHelper.currentLocation, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005)), mapViewType: mapType, + }, positions: Array(positions), waypoints: Array(waypoints), region: MKCoordinateRegion(center: LocationHelper.currentLocation, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005)), mapViewType: mapType, customMapOverlay: self.customMapOverlay, overlays: self.overlays ) VStack { Spacer() - - Picker("", selection: $mapType) { + Picker("Map Type", selection: $mapType) { Text("Standard").tag(MKMapType.standard) - Text("Muted").tag(MKMapType.mutedStandard) + Text("Standard Muted").tag(MKMapType.mutedStandard) Text("Hybrid").tag(MKMapType.hybrid) Text("Hybrid Flyover").tag(MKMapType.hybridFlyover) Text("Satellite").tag(MKMapType.satellite) - Text("Sat Flyover").tag(MKMapType.satelliteFlyover) + Text("Satellite Flyover").tag(MKMapType.satelliteFlyover) } - .pickerStyle(SegmentedPickerStyle()) - .padding(.bottom, 30) + .pickerStyle(.menu) } } .ignoresSafeArea(.all, edges: [.top, .leading, .trailing]) @@ -111,11 +93,3 @@ struct NodeMap: View { }) } } - -struct NodeMap_Previews: PreviewProvider { - static let bleManager = BLEManager() - - static var previews: some View { - NodeMap() - } -}