From e07277410f30d223b1e6db116dfbca608001ac5d Mon Sep 17 00:00:00 2001 From: Jacob Powers Date: Tue, 22 Jul 2025 02:03:36 +0000 Subject: [PATCH] wip multiple file uploads - geojson files --- .../Helpers/GeoJSONOverlayManager.swift | 46 ++++++++++++++++++- Meshtastic/Helpers/MapDataManager.swift | 44 ++++++++++++++++-- .../Map/MapContent/MeshMapContent.swift | 11 +++-- .../Nodes/Helpers/Map/MapSettingsForm.swift | 23 ++++++---- Meshtastic/Views/Nodes/MeshMap.swift | 12 ++++- 5 files changed, 116 insertions(+), 20 deletions(-) diff --git a/Meshtastic/Helpers/GeoJSONOverlayManager.swift b/Meshtastic/Helpers/GeoJSONOverlayManager.swift index dcffbaaa..e37956cd 100644 --- a/Meshtastic/Helpers/GeoJSONOverlayManager.swift +++ b/Meshtastic/Helpers/GeoJSONOverlayManager.swift @@ -31,7 +31,48 @@ class GeoJSONOverlayManager { return nil } - /// Load styled features for direct rendering + /// Load styled features for specific enabled configs + func loadStyledFeaturesForConfigs(_ enabledConfigs: Set) -> [GeoJSONStyledFeature] { + Logger.services.debug("πŸ—ΊοΈ GeoJSONOverlayManager: loadStyledFeaturesForConfigs() called with \(enabledConfigs.count) configs") + + // Get files that match the enabled configs + let enabledFiles = MapDataManager.shared.getUploadedFiles().filter { enabledConfigs.contains($0.id) } + + guard !enabledFiles.isEmpty else { + Logger.services.debug("πŸ—ΊοΈ GeoJSONOverlayManager: No enabled files found, returning empty array") + return [] + } + + // Load feature collection from enabled files only + guard let collection = MapDataManager.shared.loadFeatureCollectionForFiles(enabledFiles) else { + Logger.services.debug("πŸ—ΊοΈ GeoJSONOverlayManager: No feature collection available for enabled files, returning empty array") + return [] + } + + var styledFeatures: [GeoJSONStyledFeature] = [] + + Logger.services.info("πŸ—ΊοΈ GeoJSONOverlayManager: Processing \(collection.features.count) features from \(enabledFiles.count) enabled files") + + for feature in collection.features { + // Skip invisible features + guard feature.isVisible else { + Logger.services.debug("πŸ—ΊοΈ GeoJSONOverlayManager: Skipping invisible feature") + continue + } + + let layerId = feature.layerId ?? "default" + let styledFeature = GeoJSONStyledFeature( + feature: feature, + overlayId: layerId + ) + styledFeatures.append(styledFeature) + } + + Logger.services.info("πŸ—ΊοΈ GeoJSONOverlayManager: Returning \(styledFeatures.count) styled features from enabled configs") + return styledFeatures + } + + /// Load styled features for direct rendering (legacy method) func loadStyledFeatures() -> [GeoJSONStyledFeature] { Logger.services.debug("πŸ—ΊοΈ GeoJSONOverlayManager: loadStyledFeatures() called") @@ -123,9 +164,10 @@ class GeoJSONOverlayManager { /// Toggle the active state of an uploaded file func toggleFileActive(_ fileId: UUID) { - Logger.services.debug("πŸ—ΊοΈ GeoJSONOverlayManager: Toggling active state for file: \(fileId)") + Logger.services.error("🚨 GeoJSONOverlayManager: ENTRY - Toggling active state for file: \(fileId)") MapDataManager.shared.toggleFileActive(fileId) // Clear cache to force reload with new file states clearCache() + Logger.services.error("🚨 GeoJSONOverlayManager: EXIT - Completed toggle and cache clear for file: \(fileId)") } } \ No newline at end of file diff --git a/Meshtastic/Helpers/MapDataManager.swift b/Meshtastic/Helpers/MapDataManager.swift index 50ebb0eb..3bdab686 100644 --- a/Meshtastic/Helpers/MapDataManager.swift +++ b/Meshtastic/Helpers/MapDataManager.swift @@ -184,6 +184,38 @@ class MapDataManager { // MARK: - Configuration Loading + /// Load combined feature collection from specific files + func loadFeatureCollectionForFiles(_ files: [MapDataMetadata]) -> GeoJSONFeatureCollection? { + Logger.services.debug("πŸ“ MapDataManager: Loading feature collection for \(files.count) specific files") + + guard !files.isEmpty else { + Logger.services.debug("πŸ“ MapDataManager: No files provided, returning nil") + return nil + } + + var allFeatures: [GeoJSONFeature] = [] + + for file in files { + do { + if let featureCollection = try loadFeatureCollectionFromFile(file) { + allFeatures.append(contentsOf: featureCollection.features) + Logger.services.info("πŸ“ MapDataManager: Successfully loaded \(featureCollection.features.count) features from \(file.filename, privacy: .public)") + } + } catch { + Logger.services.error("πŸ“ MapDataManager: Failed to load feature collection from \(file.filename, privacy: .public): \(error.localizedDescription, privacy: .public)") + continue + } + } + + guard !allFeatures.isEmpty else { + Logger.services.debug("πŸ“ MapDataManager: No features loaded from any files") + return nil + } + + Logger.services.info("πŸ“ MapDataManager: Successfully combined \(allFeatures.count) total features from \(files.count) files") + return GeoJSONFeatureCollection(type: "FeatureCollection", features: allFeatures) + } + /// Load and combine raw GeoJSON feature collections from all active files func loadFeatureCollection() -> GeoJSONFeatureCollection? { if let cached = activeFeatureCollection { @@ -261,23 +293,27 @@ class MapDataManager { /// Toggle the active state of an uploaded file func toggleFileActive(_ fileId: UUID) { - Logger.services.debug("πŸ“ MapDataManager: Toggling active state for file: \(fileId)") + Logger.services.error("🚨 MapDataManager: ENTRY - Toggling active state for file: \(fileId)") if let index = uploadedFiles.firstIndex(where: { $0.id == fileId }) { + let oldState = uploadedFiles[index].isActive uploadedFiles[index].isActive.toggle() - Logger.services.info("πŸ“ MapDataManager: File '\(self.uploadedFiles[index].filename)' active state: \(self.uploadedFiles[index].isActive)") + let newState = uploadedFiles[index].isActive + Logger.services.error("🚨 MapDataManager: File '\(self.uploadedFiles[index].filename)' changed from \(oldState) to \(newState)") // Save metadata changes do { try saveMetadata() // Clear cached data to force reload activeFeatureCollection = nil + Logger.services.error("🚨 MapDataManager: Successfully saved metadata and cleared cache") } catch { - Logger.services.error("πŸ“ MapDataManager: Failed to save metadata after toggling file: \(error.localizedDescription)") + Logger.services.error("🚨 MapDataManager: FAILED to save metadata after toggling file: \(error.localizedDescription)") } } else { - Logger.services.error("πŸ“ MapDataManager: Could not find file with ID: \(fileId)") + Logger.services.error("🚨 MapDataManager: ERROR - Could not find file with ID: \(fileId)") } + Logger.services.error("🚨 MapDataManager: EXIT - Completed toggle operation for file: \(fileId)") } /// Delete uploaded file diff --git a/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift b/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift index cfa92edf..3bd32bab 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift @@ -33,6 +33,7 @@ struct MeshMapContent: MapContent { // Map overlays @AppStorage("mapOverlaysEnabled") private var showMapOverlays = false + @Binding var enabledOverlayConfigs: Set @FetchRequest(fetchRequest: PositionEntity.allPositionsFetchRequest(), animation: .easeIn) var positions: FetchedResults @@ -244,11 +245,15 @@ struct MeshMapContent: MapContent { } var overlayContent: some MapContent { - let styledFeatures = GeoJSONOverlayManager.shared.loadStyledFeatures() + // Get all features but filter by enabled configs + let allStyledFeatures = GeoJSONOverlayManager.shared.loadStyledFeaturesForConfigs(enabledOverlayConfigs) + + // Log with error level to make it visible + print("🚨 MeshMapContent: overlayContent computed - \(enabledOverlayConfigs.count) enabled configs, \(allStyledFeatures.count) features") return Group { - ForEach(0.. var body: some View { @@ -160,15 +161,18 @@ struct MapSettingsForm: View { ForEach(uploadedFiles) { file in Toggle(isOn: Binding( get: { - Logger.services.debug("πŸ”§ MapSettingsForm: File '\(file.originalName)' toggle getter - current state: \(file.isActive)") - return file.isActive + let isEnabled = enabledOverlayConfigs.contains(file.id) + Logger.services.debug("πŸ”§ MapSettingsForm: File '\(file.originalName)' toggle getter - enabled: \(isEnabled)") + return isEnabled }, set: { newValue in - Logger.services.info("πŸ”§ MapSettingsForm: File '\(file.originalName)' toggle setter - changing to: \(newValue)") - GeoJSONOverlayManager.shared.toggleFileActive(file.id) - // Update local state - uploadedFiles = GeoJSONOverlayManager.shared.getUploadedFilesWithState() - Logger.services.info("πŸ”§ MapSettingsForm: Updated local uploadedFiles state after toggle") + Logger.services.error("🚨 SETTER CALLED: File '\(file.originalName)' toggle setter - changing to: \(newValue)") + if newValue { + enabledOverlayConfigs.insert(file.id) + } else { + enabledOverlayConfigs.remove(file.id) + } + Logger.services.error("🚨 SETTER COMPLETED: enabledOverlayConfigs now has \(enabledOverlayConfigs.count) items") } )) { Label { @@ -186,8 +190,9 @@ struct MapSettingsForm: View { } } } icon: { - Image(systemName: file.isActive ? "doc.fill" : "doc") - .foregroundColor(file.isActive ? .accentColor : .secondary) + let isEnabled = enabledOverlayConfigs.contains(file.id) + Image(systemName: isEnabled ? "doc.fill" : "doc") + .foregroundColor(isEnabled ? .accentColor : .secondary) } } .tint(.accentColor) diff --git a/Meshtastic/Views/Nodes/MeshMap.swift b/Meshtastic/Views/Nodes/MeshMap.swift index a0650c41..35989b74 100644 --- a/Meshtastic/Views/Nodes/MeshMap.swift +++ b/Meshtastic/Views/Nodes/MeshMap.swift @@ -26,6 +26,8 @@ struct MeshMap: View { @AppStorage("enableMapTraffic") private var showTraffic: Bool = false @AppStorage("enableMapPointsOfInterest") private var showPointsOfInterest: Bool = false @AppStorage("mapLayer") private var selectedMapLayer: MapLayer = .standard + /// Map overlay configs + @State private var enabledOverlayConfigs: Set = [] // Map Configuration @Namespace var mapScope @State var mapStyle: MapStyle = MapStyle.standard(elevation: .flat, emphasis: MapStyle.StandardEmphasis.muted, pointsOfInterest: .excludingAll, showsTraffic: false) @@ -70,7 +72,8 @@ struct MeshMap: View { showPointsOfInterest: $showPointsOfInterest, selectedMapLayer: $selectedMapLayer, selectedPosition: $selectedPosition, - selectedWaypoint: $selectedWaypoint + selectedWaypoint: $selectedWaypoint, + enabledOverlayConfigs: $enabledOverlayConfigs ) } .mapScope(mapScope) @@ -134,7 +137,7 @@ struct MeshMap: View { .padding() } .sheet(isPresented: $editingSettings) { - MapSettingsForm(traffic: $showTraffic, pointsOfInterest: $showPointsOfInterest, mapLayer: $selectedMapLayer, meshMap: $isMeshMap) + MapSettingsForm(traffic: $showTraffic, pointsOfInterest: $showPointsOfInterest, mapLayer: $selectedMapLayer, meshMap: $isMeshMap, enabledOverlayConfigs: $enabledOverlayConfigs) } .onChange(of: router.navigationState) { guard case .map = router.navigationState.selectedTab else { return } @@ -195,6 +198,11 @@ struct MeshMap: View { }) .onFirstAppear { UIApplication.shared.isIdleTimerDisabled = true + + // Initialize enabled overlay configs with all active files + let activeFiles = GeoJSONOverlayManager.shared.getUploadedFilesWithState().filter { $0.isActive } + enabledOverlayConfigs = Set(activeFiles.map { $0.id }) + print("🚨 MeshMap: Initialized with \(enabledOverlayConfigs.count) enabled overlay configs") // let wayPointEntity = getWaypoint(id: Int64(deepLinkManager.waypointId) ?? -1, context: context) // if wayPointEntity.id > 0 {