From ea0342d0e9f3f4ab4b3bd90f3bbdd5f55eab888f Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 10 Apr 2023 19:14:54 -0700 Subject: [PATCH] Clean up map type code Add custom tile server url to app settings --- Meshtastic/Enums/AppSettingsEnums.swift | 51 ++++++++++++++----- Meshtastic/Helpers/MeshPackets.swift | 4 +- Meshtastic/Model/UserSettings.swift | 5 ++ .../Views/Map/Custom/MapViewSwiftUI.swift | 40 +++++++++++++++ Meshtastic/Views/Nodes/NodeDetail.swift | 18 +------ Meshtastic/Views/Nodes/NodeMap.swift | 20 ++------ Meshtastic/Views/Settings/AppSettings.swift | 14 ++++- 7 files changed, 104 insertions(+), 48 deletions(-) diff --git a/Meshtastic/Enums/AppSettingsEnums.swift b/Meshtastic/Enums/AppSettingsEnums.swift index e0772c35..35d75707 100644 --- a/Meshtastic/Enums/AppSettingsEnums.swift +++ b/Meshtastic/Enums/AppSettingsEnums.swift @@ -9,13 +9,13 @@ import Foundation import MapKit enum KeyboardType: Int, CaseIterable, Identifiable { - + case defaultKeyboard = 0 case asciiCapable = 1 case twitter = 9 case emailAddress = 7 case numbersAndPunctuation = 2 - + var id: Int { self.rawValue } var description: String { switch self { @@ -34,16 +34,16 @@ enum KeyboardType: Int, CaseIterable, Identifiable { } enum MeshMapType: String, CaseIterable, Identifiable { - + case standard case mutedStandard case hybrid case hybridFlyover case satellite case satelliteFlyover - + var id: String { self.rawValue } - + var description: String { switch self { case .standard: @@ -61,7 +61,7 @@ enum MeshMapType: String, CaseIterable, Identifiable { } } func MKMapTypeValue() -> MKMapType { - + switch self { case .standard: return MKMapType.standard @@ -80,13 +80,13 @@ enum MeshMapType: String, CaseIterable, Identifiable { } enum UserTrackingModes: Int, CaseIterable, Identifiable { - + case none = 0 case follow = 1 case followWithHeading = 2 - + var id: Int { self.rawValue } - + var description: String { switch self { case .none: @@ -98,7 +98,7 @@ enum UserTrackingModes: Int, CaseIterable, Identifiable { } } func MKUserTrackingModeValue() -> MKUserTrackingMode { - + switch self { case .none: return MKUserTrackingMode.none @@ -111,7 +111,7 @@ enum UserTrackingModes: Int, CaseIterable, Identifiable { } enum LocationUpdateInterval: Int, CaseIterable, Identifiable { - + case fiveSeconds = 5 case tenSeconds = 10 case fifteenSeconds = 15 @@ -120,7 +120,7 @@ enum LocationUpdateInterval: Int, CaseIterable, Identifiable { case fiveMinutes = 300 case tenMinutes = 600 case fifteenMinutes = 900 - + var id: Int { self.rawValue } var description: String { switch self { @@ -143,3 +143,30 @@ enum LocationUpdateInterval: Int, CaseIterable, Identifiable { } } } +enum MapTileServerLinks: Int, CaseIterable, Identifiable { + + case openStreetMaps + case wikimedia + case nationalMap + var id: Int { self.rawValue } + var tileUrl: String { + switch self { + case .wikimedia: + return "https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png" + case .openStreetMaps: + return "https://tile.openstreetmap.org/{z}/{x}/{y}.png" + case .nationalMap: + return "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}" + } + } + var zoomRange: [Int] { + switch self { + case .wikimedia: + return [Int](0...24) + case .openStreetMaps: + return [Int](0...24) + case .nationalMap: + return [Int](0...24) + } + } +} diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index da1b2edd..bc1a9f26 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -827,7 +827,7 @@ func waypointPacket (packet: MeshPacket, context: NSManagedObjectContext) { waypoint.longitudeI = waypointMessage.longitudeI waypoint.icon = Int64(waypointMessage.icon) waypoint.locked = Int64(waypointMessage.lockedTo) - if waypointMessage.expire > 0 { + if waypointMessage.expire >= 1 { waypoint.expire = Date(timeIntervalSince1970: TimeInterval(Int64(waypointMessage.expire))) } else { waypoint.expire = nil @@ -849,7 +849,7 @@ func waypointPacket (packet: MeshPacket, context: NSManagedObjectContext) { fetchedWaypoint[0].longitudeI = waypointMessage.longitudeI fetchedWaypoint[0].icon = Int64(waypointMessage.icon) fetchedWaypoint[0].locked = Int64(waypointMessage.lockedTo) - if waypointMessage.expire > 1 { + if waypointMessage.expire >= 1 { fetchedWaypoint[0].expire = Date(timeIntervalSince1970: TimeInterval(Int64(waypointMessage.expire))) } else { fetchedWaypoint[0].expire = nil diff --git a/Meshtastic/Model/UserSettings.swift b/Meshtastic/Model/UserSettings.swift index 585559ee..1c44c965 100644 --- a/Meshtastic/Model/UserSettings.swift +++ b/Meshtastic/Model/UserSettings.swift @@ -11,6 +11,7 @@ class UserSettings: ObservableObject { @Published var meshtasticUsername: String { didSet { UserDefaults.standard.set(meshtasticUsername, forKey: "meshtasticusername") + UserDefaults.standard.synchronize() } } @Published var preferredPeripheralId: String { @@ -27,16 +28,19 @@ class UserSettings: ObservableObject { @Published var provideLocationInterval: Int { didSet { UserDefaults.standard.set(provideLocationInterval, forKey: "provideLocationInterval") + UserDefaults.standard.synchronize() } } @Published var keyboardType: Int { didSet { UserDefaults.standard.set(keyboardType, forKey: "keyboardType") + UserDefaults.standard.synchronize() } } @Published var meshMapType: String { didSet { UserDefaults.standard.set(meshMapType, forKey: "meshMapType") + UserDefaults.standard.synchronize() } } @Published var meshMapCenteringMode: Int { @@ -54,6 +58,7 @@ class UserSettings: ObservableObject { @Published var meshMapCustomTileServer: String { didSet { UserDefaults.standard.set(meshMapCustomTileServer, forKey: "meshMapCustomTileServer") + UserDefaults.standard.synchronize() } } @Published var meshMapUserTrackingMode: Int { diff --git a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index 9581e603..197fcdd1 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -31,6 +31,11 @@ struct MapViewSwiftUI: UIViewRepresentable { var customMapOverlay: CustomMapOverlay? @State private var presentCustomMapOverlayHash: CustomMapOverlay? + // Custom Tile Server + @AppStorage("meshMapCustomTileServer") private var tileServerUrl = "" + var tileRenderer: MKTileOverlayRenderer? + let tileServer: MapTileServerLinks = .openStreetMaps + func makeUIView(context: Context) -> MKMapView { // Map View Parameters mapView.mapType = mapViewType @@ -85,6 +90,10 @@ struct MapViewSwiftUI: UIViewRepresentable { #endif #endif + + if tileServerUrl.count > 0 { + context.coordinator.setupTileServerRenderer() + } mapView.delegate = context.coordinator return mapView } @@ -162,6 +171,29 @@ struct MapViewSwiftUI: UIViewRepresentable { mapView.showsUserLocation = true } mapView.setUserTrackingMode(userTrackingMode, animated: true) + + if tileServerUrl.count > 0 { + + tileRenderer?.alpha = 0.0 + let overlays = mapView.overlays + if mapView.mapType == .standard { + let overlay = MKTileOverlay(urlTemplate: tileServerUrl) + if overlays.contains(where: {$0 is MKPolyline}) { + mapView.addOverlay(overlay, level: .aboveLabels) + if let poly_overlay = overlays.filter({$0 is MKPolyline}).first { + mapView.addOverlay(poly_overlay, level: .aboveLabels) + } + } else { + mapView.addOverlay(overlay, level: .aboveLabels) + } + } else { + for overlay in overlays { + if let ove = overlay as? MKTileOverlay { + mapView.removeOverlay(ove) + } + } + } + } } func makeCoordinator() -> MapCoordinator { @@ -352,6 +384,14 @@ struct MapViewSwiftUI: UIViewRepresentable { return MKOverlayRenderer() } } + + func setupTileServerRenderer() { + //let template = "https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}.jpg" + //let template = "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}.jpg" + let overlay = MKTileOverlay(urlTemplate: parent.tileServerUrl) + parent.mapView.addOverlay(overlay, level: .aboveLabels) + parent.tileRenderer = MKTileOverlayRenderer(tileOverlay: overlay) + } } /// is supposed to be located in the folder with the map name diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index 976db5fe..3c4d0026 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -217,22 +217,8 @@ struct NodeDetail: View { }) .onAppear { self.bleManager.context = context - switch meshMapType { - case "standard": - mapType = .standard - case "mutedStandard": - mapType = .mutedStandard - case "hybrid": - mapType = .hybrid - case "hybridFlyover": - mapType = .hybridFlyover - case "satellite": - mapType = .satellite - case "satelliteFlyover": - mapType = .satelliteFlyover - default: - mapType = .hybridFlyover - } + let currentMapType = MeshMapType(rawValue: meshMapType) + mapType = currentMapType?.MKMapTypeValue() ?? .standard } .task(id: node.num) { if !loadedWeather { diff --git a/Meshtastic/Views/Nodes/NodeMap.swift b/Meshtastic/Views/Nodes/NodeMap.swift index 2583f940..94bd7b96 100644 --- a/Meshtastic/Views/Nodes/NodeMap.swift +++ b/Meshtastic/Views/Nodes/NodeMap.swift @@ -37,7 +37,7 @@ struct NodeMap: View { } } } - @AppStorage("meshMapType") private var meshMapType = "hybridFlyover" + @AppStorage("meshMapType") private var meshMapType = "standard" @AppStorage("meshMapUserTrackingMode") private var meshMapUserTrackingMode = 0 @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "time", ascending: true)], @@ -111,22 +111,8 @@ struct NodeMap: View { self.bleManager.context = context self.bleManager.userSettings = userSettings userTrackingMode = UserTrackingModes(rawValue: meshMapUserTrackingMode)?.MKUserTrackingModeValue() ?? MKUserTrackingMode.none - switch meshMapType { - case "standard": - mapType = .standard - case "mutedStandard": - mapType = .mutedStandard - case "hybrid": - mapType = .hybrid - case "hybridFlyover": - mapType = .hybridFlyover - case "satellite": - mapType = .satellite - case "satelliteFlyover": - mapType = .satelliteFlyover - default: - mapType = .hybridFlyover - } + let currentMapType = MeshMapType(rawValue: meshMapType) + mapType = currentMapType?.MKMapTypeValue() ?? .standard }) .onDisappear(perform: { UIApplication.shared.isIdleTimerDisabled = false diff --git a/Meshtastic/Views/Settings/AppSettings.swift b/Meshtastic/Views/Settings/AppSettings.swift index a5a49d67..fcfa98e7 100644 --- a/Meshtastic/Views/Settings/AppSettings.swift +++ b/Meshtastic/Views/Settings/AppSettings.swift @@ -26,7 +26,6 @@ struct AppSettings: View { } .keyboardType(.asciiCapable) .disableAutocorrection(true) - .listRowSeparator(.visible) } Section(header: Text("options")) { @@ -120,6 +119,19 @@ struct AppSettings: View { Label("Show Route Lines", systemImage: "road.lanes") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + + HStack { + + Label("Tile Server", systemImage: "square.grid.3x2") + TextField( + "Tile Server", + text: $userSettings.meshMapCustomTileServer, + axis: .vertical + ) + .foregroundColor(.gray) + } + .keyboardType(.asciiCapable) + .disableAutocorrection(true) } } HStack {