More map cleanup

This commit is contained in:
Garth Vander Houwen 2023-11-09 17:43:38 -08:00
parent 0c70921935
commit 70e1bdb835
13 changed files with 210 additions and 126 deletions

View file

@ -88,7 +88,7 @@
DD913639270DFF4C00D7ACF3 /* LocalNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */; };
DD94B7402ACCE3BE00DCD1D1 /* MapSettingsForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD94B73F2ACCE3BE00DCD1D1 /* MapSettingsForm.swift */; };
DD964FBD296E6B01007C176F /* EmojiOnlyTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FBC296E6B01007C176F /* EmojiOnlyTextField.swift */; };
DD964FBF296E76EF007C176F /* WaypointFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FBE296E76EF007C176F /* WaypointFormView.swift */; };
DD964FBF296E76EF007C176F /* WaypointFormMapKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FBE296E76EF007C176F /* WaypointFormMapKit.swift */; };
DD964FC2297272AE007C176F /* WaypointEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FC1297272AE007C176F /* WaypointEntityExtension.swift */; };
DD964FC42974767D007C176F /* MapViewFitExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FC32974767D007C176F /* MapViewFitExtension.swift */; };
DD964FC62975DBFD007C176F /* QueryCoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FC52975DBFD007C176F /* QueryCoreData.swift */; };
@ -299,7 +299,7 @@
DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotificationManager.swift; sourceTree = "<group>"; };
DD94B73F2ACCE3BE00DCD1D1 /* MapSettingsForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapSettingsForm.swift; sourceTree = "<group>"; };
DD964FBC296E6B01007C176F /* EmojiOnlyTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiOnlyTextField.swift; sourceTree = "<group>"; };
DD964FBE296E76EF007C176F /* WaypointFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaypointFormView.swift; sourceTree = "<group>"; };
DD964FBE296E76EF007C176F /* WaypointFormMapKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaypointFormMapKit.swift; sourceTree = "<group>"; };
DD964FC029724F6D007C176F /* MeshtasticDataModelV6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV6.xcdatamodel; sourceTree = "<group>"; };
DD964FC1297272AE007C176F /* WaypointEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaypointEntityExtension.swift; sourceTree = "<group>"; };
DD964FC32974767D007C176F /* MapViewFitExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewFitExtension.swift; sourceTree = "<group>"; };
@ -437,13 +437,14 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
C9483F6B2773016700998F6B /* Map */ = {
C9483F6B2773016700998F6B /* MapKitMap */ = {
isa = PBXGroup;
children = (
C9A7BC0E27759A6800760B50 /* Custom */,
DD964FBE296E76EF007C176F /* WaypointFormView.swift */,
DDDB26472AACD6D1003AFCB7 /* NodeMapMapkit.swift */,
DD964FBE296E76EF007C176F /* WaypointFormMapKit.swift */,
);
path = Map;
path = MapKitMap;
sourceTree = "<group>";
};
C9A7BC0E27759A6800760B50 /* Custom */ = {
@ -632,7 +633,6 @@
isa = PBXGroup;
children = (
DDB6CCFA2AAF805100945AF6 /* NodeMapSwiftUI.swift */,
DDDB26472AACD6D1003AFCB7 /* NodeMapMapkit.swift */,
);
path = Map;
sourceTree = "<group>";
@ -731,7 +731,7 @@
DDC2E18726CE24E40042C5E4 /* Views */ = {
isa = PBXGroup;
children = (
C9483F6B2773016700998F6B /* Map */,
C9483F6B2773016700998F6B /* MapKitMap */,
DDC2E18D26CE25CB0042C5E4 /* Helpers */,
DD47E3D726F2F21A00029299 /* Bluetooth */,
DDC2E18B26CE25A70042C5E4 /* Messages */,
@ -1079,7 +1079,7 @@
DDFEB3BB29900C1200EE7472 /* CurrentConditionsCompact.swift in Sources */,
DD836AE726F6B38600ABCC23 /* Connect.swift in Sources */,
DD5E523F298F5A9E00D21B61 /* AirQualityIndexCompact.swift in Sources */,
DD964FBF296E76EF007C176F /* WaypointFormView.swift in Sources */,
DD964FBF296E76EF007C176F /* WaypointFormMapKit.swift in Sources */,
DD3501892852FC3B000FC853 /* Settings.swift in Sources */,
DDDB443629F6287000EE2349 /* MapButtons.swift in Sources */,
DD5D0A9C2931B9F200F7EA61 /* EthernetModes.swift in Sources */,

View file

@ -145,7 +145,7 @@ struct NodeMapMapkit: View {
}
.edgesIgnoringSafeArea([.leading, .trailing])
.sheet(item: $waypointCoordinate, content: { wpc in
WaypointFormView(coordinate: wpc)
WaypointFormMapKit(coordinate: wpc)
.presentationDetents([.medium, .large])
.presentationDragIndicator(.automatic)
})

View file

@ -8,7 +8,7 @@
import SwiftUI
import CoreLocation
struct WaypointFormView: View {
struct WaypointFormMapKit: View {
@EnvironmentObject var bleManager: BLEManager
@Environment(\.dismiss) private var dismiss

View file

@ -99,7 +99,6 @@ struct NodeMapSwiftUI: View {
/// Node Annotations
ForEach(positionArray, id: \.id) { position in
let pf = PositionFlags(rawValue: Int(position.nodePosition?.metadata?.positionFlags ?? 3))
let formatter = MeasurementFormatter()
let headingDegrees = Angle.degrees(Double(position.heading))
Annotation(position.latest ? node.user?.shortName ?? "?": "", coordinate: position.coordinate) {
ZStack {
@ -199,91 +198,22 @@ struct NodeMapSwiftUI: View {
.padding()
}
.sheet(isPresented: $isEditingSettings) {
VStack {
Form {
Section(header: Text("Map Options")) {
Picker(selection: $selectedMapLayer, label: Text("")) {
ForEach(MapLayer.allCases, id: \.self) { layer in
if layer != MapLayer.offline {
Text(layer.localized)
}
}
}
.pickerStyle(SegmentedPickerStyle())
.onChange(of: (selectedMapLayer)) { newMapLayer in
switch selectedMapLayer {
case .standard:
UserDefaults.mapLayer = newMapLayer
mapStyle = MapStyle.standard(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
case .hybrid:
UserDefaults.mapLayer = newMapLayer
mapStyle = MapStyle.hybrid(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
case .satellite:
UserDefaults.mapLayer = newMapLayer
mapStyle = MapStyle.imagery(elevation: .realistic)
case .offline:
return
}
}
.padding(.top, 5)
.padding(.bottom, 5)
Toggle(isOn: $showNodeHistory) {
Label("Node History", systemImage: "building.columns.fill")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
.onTapGesture {
self.showNodeHistory.toggle()
UserDefaults.enableMapNodeHistoryPins = self.showNodeHistory
}
Toggle(isOn: $showRouteLines) {
Label("Route Lines", systemImage: "road.lanes")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
.onTapGesture {
self.showRouteLines.toggle()
UserDefaults.enableMapRouteLines = self.showRouteLines
}
Toggle(isOn: $showConvexHull) {
Label("Convex Hull", systemImage: "button.angledbottom.horizontal.right")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
.onTapGesture {
self.showConvexHull.toggle()
UserDefaults.enableMapConvexHull = self.showConvexHull
}
Toggle(isOn: $showTraffic) {
Label("Traffic", systemImage: "car")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
.onTapGesture {
self.showTraffic.toggle()
UserDefaults.enableMapTraffic = self.showTraffic
}
Toggle(isOn: $showPointsOfInterest) {
Label("Points of Interest", systemImage: "mappin.and.ellipse")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
.onTapGesture {
self.showPointsOfInterest.toggle()
UserDefaults.enableMapPointsOfInterest = self.showPointsOfInterest
}
MapSettingsForm(nodeHistory: $showNodeHistory, routeLines: $showRouteLines, convexHull: $showConvexHull, traffic: $showTraffic, pointsOfInterest: $showPointsOfInterest, mapLayer: $selectedMapLayer)
.onChange(of: (selectedMapLayer)) { newMapLayer in
switch selectedMapLayer {
case .standard:
UserDefaults.mapLayer = newMapLayer
mapStyle = MapStyle.standard(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
case .hybrid:
UserDefaults.mapLayer = newMapLayer
mapStyle = MapStyle.hybrid(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
case .satellite:
UserDefaults.mapLayer = newMapLayer
mapStyle = MapStyle.imagery(elevation: .realistic)
case .offline:
return
}
}
#if targetEnvironment(macCatalyst)
Button {
isEditingSettings = false
} label: {
Label("close", systemImage: "xmark")
}
.buttonStyle(.bordered)
.buttonBorderShape(.capsule)
.controlSize(.large)
.padding()
#endif
}
.presentationDetents([.fraction(0.4), .medium])
.presentationDragIndicator(.visible)
}
.onChange(of: node) {
let mostRecent = node.positions?.lastObject as? PositionEntity

View file

@ -13,12 +13,12 @@ import MapKit
@available(iOS 17.0, macOS 14.0, *)
struct MapSettingsForm: View {
@Environment(\.dismiss) private var dismiss
@State var nodeHistory = false
@State var routeLines = false
@State var convexHull = false
@State var traffic: Bool = false
@State var pointsOfInterest: Bool = false
@State var mapLayer: MapLayer = .standard
@Binding var nodeHistory: Bool
@Binding var routeLines: Bool
@Binding var convexHull: Bool
@Binding var traffic: Bool
@Binding var pointsOfInterest: Bool
@Binding var mapLayer: MapLayer
var body: some View {
@ -35,6 +35,9 @@ struct MapSettingsForm: View {
.pickerStyle(SegmentedPickerStyle())
.padding(.top, 5)
.padding(.bottom, 5)
.onChange(of: mapLayer) { newMapLayer in
UserDefaults.mapLayer = newMapLayer
}
Toggle(isOn: $nodeHistory) {
Label("Node History", systemImage: "building.columns.fill")
}
@ -78,19 +81,18 @@ struct MapSettingsForm: View {
}
}
#if targetEnvironment(macCatalyst)
Button {
dismiss()
} label: {
Label("close", systemImage: "xmark")
}
.buttonStyle(.bordered)
.buttonBorderShape(.capsule)
.controlSize(.large)
.padding()
Spacer()
Button {
dismiss()
} label: {
Label("close", systemImage: "xmark")
}
.buttonStyle(.bordered)
.buttonBorderShape(.capsule)
.controlSize(.large)
#endif
}
.presentationDetents([.fraction(0.60)])
//.presentationDetents([.medium, .large])
.presentationDragIndicator(.automatic)
.presentationDetents([.fraction(0.45), .fraction(0.65)])
.presentationDragIndicator(.visible)
}
}

View file

@ -9,10 +9,12 @@ import SwiftUI
import MapKit
struct PositionPopover: View {
@Environment(\.dismiss) private var dismiss
var position: PositionEntity
var popover: Bool = true
let distanceFormatter = MKDistanceFormatter()
var body: some View {
VStack (alignment: .leading) {
VStack {
HStack {
CircleText(text: position.nodePosition?.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(position.nodePosition?.user?.num ?? 0))), circleSize: 65)
Spacer()
@ -129,6 +131,20 @@ struct PositionPopover: View {
}
}
.padding(.top)
if !popover {
#if targetEnvironment(macCatalyst)
Spacer()
Button {
dismiss()
} label: {
Label("close", systemImage: "xmark")
}
.buttonStyle(.bordered)
.buttonBorderShape(.capsule)
.controlSize(.large)
.padding(.bottom)
#endif
}
}
.presentationDetents([.fraction(0.45), .medium])
.presentationDragIndicator(.visible)

View file

@ -52,6 +52,10 @@ struct MeshMap: View {
private var waypoints: FetchedResults<WaypointEntity>
var body: some View {
let lineCoords = Array(positions).compactMap({(position) -> CLLocationCoordinate2D in
return position.nodeCoordinate ?? LocationHelper.DefaultLocation
})
NavigationStack {
ZStack {
MapReader { reader in
@ -71,8 +75,21 @@ struct MeshMap: View {
}
}
}
/// Convex Hull
if showConvexHull {
let hull = lineCoords.getConvexHull()
MapPolygon(coordinates: hull)
.stroke(.blue, lineWidth: 3)
.foregroundStyle(.indigo.opacity(0.4))
//.stroke(Color(nodeColor.darker()), lineWidth: 3)
//.foregroundStyle(Color(nodeColor).opacity(0.4))
}
/// Position Annotations
ForEach(Array(positions), id: \.id) { position in
let pf = PositionFlags(rawValue: Int(position.nodePosition?.metadata?.positionFlags ?? 3))
/// Node Color from node.num
let nodeColor = UIColor(hex: UInt32(position.nodePosition?.num ?? 0))
Annotation(position.nodePosition?.user?.longName ?? "?", coordinate: position.coordinate) {
ZStack {
let nodeColor = UIColor(hex: UInt32(position.nodePosition?.num ?? 0))
@ -99,22 +116,131 @@ struct MeshMap: View {
selectedPosition = (selectedPosition == position ? nil : position)
}
}
// routeLines
if showRouteLines {
let positionArray = position.nodePosition?.positions?.array as? [PositionEntity] ?? []
let routeCoords = positionArray.compactMap({(position) -> CLLocationCoordinate2D in
return position.nodeCoordinate ?? LocationHelper.DefaultLocation
})
if showRouteLines {
let gradient = LinearGradient(
colors: [Color(nodeColor.lighter().lighter()), Color(nodeColor.lighter()), Color(nodeColor)],
startPoint: .leading, endPoint: .trailing
)
let dashed = StrokeStyle(
lineWidth: 3,
lineCap: .round, lineJoin: .round, dash: [10, 10]
)
MapPolyline(coordinates: routeCoords)
.stroke(gradient, style: dashed)
}
} // Node History
if showNodeHistory {
ForEach(position.nodePosition!.positions!.reversed() as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in
Annotation(position.latest ? position.nodePosition?.user?.shortName ?? "?": "", coordinate: position.coordinate) {
ZStack {
Circle()
.fill(Color(UIColor(hex: UInt32(position.nodePosition?.num ?? 0))))
.strokeBorder(Color(UIColor(hex: UInt32(position.nodePosition?.num ?? 0))).isLight() ? .black : .white ,lineWidth: 2)
.frame(width: 12, height: 12)
}
}
.annotationTitles(.hidden)
.annotationSubtitles(.hidden)
}
}
}
}
}
}
.ignoresSafeArea(.all, edges: [.top, .leading, .trailing])
.frame(maxHeight: .infinity)
// .popover(item: $selectedPosition) { selection in
// PositionPopover(position: selection)
// .padding()
// .opacity(0.8)
// .presentationCompactAdaptation(.sheet)
// }
.mapScope(mapScope)
.mapStyle(mapStyle)
.mapControls {
MapScaleView(scope: mapScope)
.mapControlVisibility(.visible)
if showUserLocation {
MapUserLocationButton(scope: mapScope)
.mapControlVisibility(.visible)
}
MapPitchToggle(scope: mapScope)
.mapControlVisibility(.visible)
MapCompass(scope: mapScope)
.mapControlVisibility(.visible)
}
.controlSize(.regular)
.sheet(item: $selectedPosition) { selection in
PositionPopover(position: selection)
PositionPopover(position: selection, popover: false)
.padding()
}
.sheet(item: $selectedWaypoint) { selection in
WaypointPopover(waypoint: selection)
.padding()
}
.sheet(isPresented: $isEditingSettings) {
MapSettingsForm(nodeHistory: $showNodeHistory, routeLines: $showRouteLines, convexHull: $showConvexHull, traffic: $showTraffic, pointsOfInterest: $showPointsOfInterest, mapLayer: $selectedMapLayer)
}
.onChange(of: (selectedMapLayer)) { newMapLayer in
switch selectedMapLayer {
case .standard:
UserDefaults.mapLayer = newMapLayer
mapStyle = MapStyle.standard(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
case .hybrid:
UserDefaults.mapLayer = newMapLayer
mapStyle = MapStyle.hybrid(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
case .satellite:
UserDefaults.mapLayer = newMapLayer
mapStyle = MapStyle.imagery(elevation: .realistic)
case .offline:
return
}
}
.safeAreaInset(edge: .bottom, alignment: UIDevice.current.userInterfaceIdiom == .phone ? .leading : .trailing) {
HStack {
Button(action: {
withAnimation {
isEditingSettings = !isEditingSettings
}
}) {
Image(systemName: isEditingSettings ? "info.circle.fill" : "info.circle")
.padding(.vertical, 5)
}
.tint(Color(UIColor.secondarySystemBackground))
.foregroundColor(.accentColor)
.buttonStyle(.borderedProminent)
/// Show / Hide Waypoints Button
if waypoints.count > 0 {
Button(action: {
withAnimation {
showWaypoints = !showWaypoints
}
}) {
Image(systemName: showWaypoints ? "signpost.right.and.left.fill" : "signpost.right.and.left")
.padding(.vertical, 5)
}
.tint(Color(UIColor.secondarySystemBackground))
.foregroundColor(.accentColor)
.buttonStyle(.borderedProminent)
}
/// Look Around Button
if self.scene != nil {
Button(action: {
withAnimation {
isLookingAround = !isLookingAround
}
}) {
Image(systemName: isLookingAround ? "binoculars.fill" : "binoculars")
.padding(.vertical, 5)
}
.tint(Color(UIColor.secondarySystemBackground))
.foregroundColor(.accentColor)
.buttonStyle(.borderedProminent)
}
}
.controlSize(.regular)
.padding(5)
}
}
.navigationTitle("Mesh Map")
.navigationBarItems(leading:
@ -126,12 +252,22 @@ struct MeshMap: View {
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName :
"?")
})
.onAppear(perform: {
.onAppear {
UIApplication.shared.isIdleTimerDisabled = true
if self.bleManager.context == nil {
self.bleManager.context = context
}
})
switch selectedMapLayer {
case .standard:
mapStyle = MapStyle.standard(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
case .hybrid:
mapStyle = MapStyle.hybrid(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
case .satellite:
mapStyle = MapStyle.imagery(elevation: .realistic)
case .offline:
mapStyle = MapStyle.hybrid(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
}
}
.onDisappear(perform: {
UIApplication.shared.isIdleTimerDisabled = false
})

View file

@ -76,7 +76,7 @@ struct NodeMap: View {
.ignoresSafeArea(.all, edges: [.top, .leading, .trailing])
.frame(maxHeight: .infinity)
.sheet(item: $waypointCoordinate, content: { wpc in
WaypointFormView(coordinate: wpc)
WaypointFormMapKit(coordinate: wpc)
.presentationDetents([.medium, .large])
.presentationDragIndicator(.automatic)
})

View file

@ -121,7 +121,7 @@ struct UserConfig: View {
HStack {
Image(systemName: "antenna.radiowaves.left.and.right")
.foregroundColor(.accentColor)
Stepper("\(txPower)db Transmit Power", value: $txPower, in: 0...30, step: 1)
Stepper("\(txPower)db Transmit Power", value: $txPower, in: 1...30, step: 1)
.padding(5)
}
}