diff --git a/Meshtastic Client.xcodeproj/project.xcworkspace/xcuserdata/joshuapirihi.xcuserdatad/UserInterfaceState.xcuserstate b/Meshtastic Client.xcodeproj/project.xcworkspace/xcuserdata/joshuapirihi.xcuserdatad/UserInterfaceState.xcuserstate index 66abbf05..c932fa44 100644 Binary files a/Meshtastic Client.xcodeproj/project.xcworkspace/xcuserdata/joshuapirihi.xcuserdatad/UserInterfaceState.xcuserstate and b/Meshtastic Client.xcodeproj/project.xcworkspace/xcuserdata/joshuapirihi.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/MeshtasticClient/Helpers/BLEManager.swift b/MeshtasticClient/Helpers/BLEManager.swift index 87547c94..6506b552 100644 --- a/MeshtasticClient/Helpers/BLEManager.swift +++ b/MeshtasticClient/Helpers/BLEManager.swift @@ -811,6 +811,31 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph } fetchedNode[0].snr = decodedInfo.packet.rxSnr + + if let positionMessage = try? Position(serializedData: decodedInfo.packet.decoded.payload) { + let position = PositionEntity(context: context!) + position.latitudeI = positionMessage.latitudeI + position.longitudeI = positionMessage.longitudeI + position.altitude = positionMessage.altitude + position.batteryLevel = positionMessage.batteryLevel + position.time = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time))) + + let mutablePositions = fetchedNode[0].positions!.mutableCopy() as! NSMutableOrderedSet + mutablePositions.add(position) + + print("💾 Recieved a Position Packet") + + if position.coordinate == nil { + var newPostions = [PositionEntity]() + newPostions.append(position) + fetchedNode[0].positions? = NSOrderedSet(array: newPostions) + + } else { + + fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet + } + } + } else { return diff --git a/MeshtasticClient/Views/Map/LocalMBTileOverlay.swift b/MeshtasticClient/Views/Map/LocalMBTileOverlay.swift index 2b967097..088f6365 100644 --- a/MeshtasticClient/Views/Map/LocalMBTileOverlay.swift +++ b/MeshtasticClient/Views/Map/LocalMBTileOverlay.swift @@ -9,12 +9,43 @@ import UIKit import MapKit import SQLite +extension MKMapRect { + init(coordinates: [CLLocationCoordinate2D]) { + self = MKMapRect() + var coordinates = coordinates + if !coordinates.isEmpty { + let first = coordinates.removeFirst() + var top = first.latitude + var bottom = first.latitude + var left = first.longitude + var right = first.longitude + coordinates.forEach { coordinate in + top = max(top, coordinate.latitude) + bottom = min(bottom, coordinate.latitude) + left = min(left, coordinate.longitude) + right = max(right, coordinate.longitude) + } + let topLeft = MKMapPoint(CLLocationCoordinate2D(latitude:top, longitude:left)) + let bottomRight = MKMapPoint(CLLocationCoordinate2D(latitude:bottom, longitude:right)) + self = MKMapRect(x:topLeft.x, y:topLeft.y, + width:bottomRight.x - topLeft.x, height:bottomRight.y - topLeft.y) + } + } +} + class LocalMBTileOverlay: MKTileOverlay { var path: String! var mb: Connection! + private var _boundingMapRect: MKMapRect! + override var boundingMapRect: MKMapRect { + get { + return _boundingMapRect + } + } + init(mbTilePath path: String) { super.init(urlTemplate: nil) @@ -35,19 +66,29 @@ class LocalMBTileOverlay: MKTileOverlay { self.isGeometryFlipped = true - //let boundingBoxString = try mb.pluck(metadata.select(value).filter(name == "bounds")) - //let boundCoords = boundingBoxString![value].split(separator: ",") - //self.boundingMapRect = MKMapRect(coordinates: [CLLocationCoordinate2D(latitude: Double(boundCoords[0]) ?? 0, longitude: Double(boundCoords[1]) ?? 0), CLLocationCoordinate2D(latitude: Double(boundCoords[2]) ?? 0, longitude: Double(boundCoords[3]) ?? 0)]) + let boundingBoxString = try mb.pluck(metadata.select(value).filter(name == "bounds")) + let boundCoords = boundingBoxString![value].split(separator: ",") + let coords = [ + CLLocationCoordinate2D(latitude: Double(boundCoords[1]) ?? 0, + longitude: Double(boundCoords[0]) ?? 0), + CLLocationCoordinate2D(latitude: Double(boundCoords[3]) ?? 0, + longitude: Double(boundCoords[2]) ?? 0) + ] + self._boundingMapRect = MKMapRect(coordinates: coords) } catch { - + //print("MAP ERROR \(error)") } } - override func loadTile(at path: MKTileOverlayPath) async throws -> Data { + 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) @@ -58,14 +99,18 @@ class LocalMBTileOverlay: MKTileOverlay { 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)) { + 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 + //return data + result(data, nil) } else { - return Data() + 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/MeshtasticClient/Views/Map/MapViewModule.swift b/MeshtasticClient/Views/Map/MapViewModule.swift index 1cbb43c0..f7468edd 100644 --- a/MeshtasticClient/Views/Map/MapViewModule.swift +++ b/MeshtasticClient/Views/Map/MapViewModule.swift @@ -11,9 +11,9 @@ import CoreData #if canImport(MapKit) && canImport(UIKit) public struct MapView: UIViewRepresentable { - //@Environment(\.managedObjectContext) var context + @Environment(\.managedObjectContext) var context - var context: NSManagedObjectContext? + //var context: NSManagedObjectContext? //@Binding private var region: MKCoordinateRegion @@ -40,8 +40,11 @@ public struct MapView: UIViewRepresentable { private var overlays: [Overlay] - @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "lastHeard", ascending: false)], animation: .default) - private var locationNodes: FetchedResults + //@FetchRequest(sortDescriptors: [NSSortDescriptor(key: "lastHeard", ascending: false)], animation: .default) + // private var locationNodes: FetchedResults + + @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "time", ascending: false)], animation: .default) + private var positions: FetchedResults //@State private var locationNodes: [NodeInfoEntity] @@ -62,8 +65,8 @@ public struct MapView: UIViewRepresentable { userLocation: Binding = .constant(nil), //annotations: [MKPointAnnotation] = [], //locationNodes: [NodeInfoEntity] = [], - overlays: [Overlay] = [], - context: NSManagedObjectContext? = nil + overlays: [Overlay] = [] + //context: NSManagedObjectContext? = nil ) { //self._region = region @@ -111,7 +114,7 @@ public struct MapView: UIViewRepresentable { mapView.delegate = context.coordinator mapView.register(PositionAnnotationView.self, forAnnotationViewWithReuseIdentifier: NSStringFromClass(PositionAnnotationView.self)) - Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { timer in + /*Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { timer in for node in self.locationNodes { // try and get the last position if (node.positions?.count ?? 0) > 0 && (node.positions!.lastObject as! PositionEntity).coordinate != nil { @@ -123,7 +126,7 @@ public struct MapView: UIViewRepresentable { mapView.addAnnotation(annotation) } } - } + }*/ return mapView } @@ -139,18 +142,20 @@ public struct MapView: UIViewRepresentable { mapView.removeOverlays(mapView.overlays) if let customMapOverlay = self.customMapOverlay { - let overlay = LocalMBTileOverlay(mbTilePath: Bundle.main.path(forResource: "offline_map", ofType: "mbtiles")!) - - overlay.canReplaceMapContent = false//customMapOverlay.canReplaceMapContent - - mapView.addOverlay(overlay) + if let tilePath = Bundle.main.path(forResource: "offline_map", ofType: "mbtiles") { + let overlay = LocalMBTileOverlay(mbTilePath: tilePath) + + overlay.canReplaceMapContent = false//customMapOverlay.canReplaceMapContent + + mapView.addOverlay(overlay) + } } DispatchQueue.main.async { self.presentCustomMapOverlayHash = self.customMapOverlay } } - if mapView.overlays.count != (self.overlays.count + (self.customMapOverlay == nil ? 0 : 1)) { + /*if mapView.overlays.count != (self.overlays.count + (self.customMapOverlay == nil ? 0 : 1)) { context.coordinator.overlays = self.overlays mapView.overlays.forEach { overlay in if !(overlay is MKTileOverlay) { @@ -158,7 +163,7 @@ public struct MapView: UIViewRepresentable { } } mapView.addOverlays(self.overlays.map { overlay in overlay.shape }) - } + }*/ if mapView.mapType != self.mapType { mapView.mapType = self.mapType @@ -210,7 +215,7 @@ public struct MapView: UIViewRepresentable { shouldMoveRegion = true } - for node in self.locationNodes { + /*for node in self.locationNodes { // try and get the last position if (node.positions?.count ?? 0) > 0 && (node.positions!.lastObject as! PositionEntity).coordinate != nil { let annotation = PositionAnnotation() @@ -220,6 +225,22 @@ public struct MapView: UIViewRepresentable { mapView.addAnnotation(annotation) } + }*/ + + var displayedNodes: [Int64] = [] + for position in self.positions { + if position.nodePosition == nil || displayedNodes.contains(position.nodePosition!.num) || position.coordinate == nil { + continue + } + + let annotation = PositionAnnotation() + annotation.coordinate = position.coordinate! + annotation.title = position.nodePosition!.user?.longName ?? "Unknown" + annotation.shortName = position.nodePosition!.user?.shortName?.uppercased() ?? "???" + + mapView.addAnnotation(annotation) + + displayedNodes.append(position.nodePosition!.num) } if shouldMoveRegion { diff --git a/MeshtasticClient/Views/Nodes/NodeMap.swift b/MeshtasticClient/Views/Nodes/NodeMap.swift index be4918e1..c9e99e80 100644 --- a/MeshtasticClient/Views/Nodes/NodeMap.swift +++ b/MeshtasticClient/Views/Nodes/NodeMap.swift @@ -108,8 +108,8 @@ struct NodeMap: View { userLocation: self.$userLocation, //annotations: self.annotations, //locationNodes: self.locationNodes.map({ nodeinfo in return nodeinfo }), - overlays: self.overlays, - context: self.context + overlays: self.overlays + //context: self.context ) .frame(maxHeight: .infinity)