mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Move node map into map content view
Dont change channel for node infos directed at other nodes
This commit is contained in:
parent
7464f8d4cd
commit
550add228c
4 changed files with 177 additions and 117 deletions
|
|
@ -110,6 +110,7 @@
|
|||
DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8ED9C7289CE4B900B3B0AB /* RoutingError.swift */; };
|
||||
DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD90860D26F69BAE00DC5189 /* NodeMap.swift */; };
|
||||
DD913639270DFF4C00D7ACF3 /* LocalNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */; };
|
||||
DD93800B2BA3F968008BEC06 /* NodeMapContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD93800A2BA3F968008BEC06 /* NodeMapContent.swift */; };
|
||||
DD94B7402ACCE3BE00DCD1D1 /* MapSettingsForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD94B73F2ACCE3BE00DCD1D1 /* MapSettingsForm.swift */; };
|
||||
DD964FBD296E6B01007C176F /* EmojiOnlyTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FBC296E6B01007C176F /* EmojiOnlyTextField.swift */; };
|
||||
DD964FBF296E76EF007C176F /* WaypointFormMapKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FBE296E76EF007C176F /* WaypointFormMapKit.swift */; };
|
||||
|
|
@ -360,6 +361,7 @@
|
|||
DD90860A26F645B700DC5189 /* Meshtastic.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Meshtastic.entitlements; sourceTree = "<group>"; };
|
||||
DD90860D26F69BAE00DC5189 /* NodeMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeMap.swift; sourceTree = "<group>"; };
|
||||
DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotificationManager.swift; sourceTree = "<group>"; };
|
||||
DD93800A2BA3F968008BEC06 /* NodeMapContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeMapContent.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 /* WaypointFormMapKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaypointFormMapKit.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -748,6 +750,7 @@
|
|||
DDAD49EB2AFAE82500B4425D /* Map */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DD93800A2BA3F968008BEC06 /* NodeMapContent.swift */,
|
||||
DD94B73F2ACCE3BE00DCD1D1 /* MapSettingsForm.swift */,
|
||||
DDB6CCFA2AAF805100945AF6 /* NodeMapSwiftUI.swift */,
|
||||
DD13AA482AB73BF400BA0C98 /* PositionPopover.swift */,
|
||||
|
|
@ -1365,6 +1368,7 @@
|
|||
DD5E5205298EE33B00D21B61 /* mesh.pb.swift in Sources */,
|
||||
DDF6B2482A9AEBF500BA6931 /* StoreForwardConfig.swift in Sources */,
|
||||
DD8169F9271F1A6100F4AB02 /* MeshLogger.swift in Sources */,
|
||||
DD93800B2BA3F968008BEC06 /* NodeMapContent.swift in Sources */,
|
||||
DD41582A28585C32009B0E59 /* RangeTestConfig.swift in Sources */,
|
||||
DD1925B728CDA5A400720036 /* CannedMessagesConfigEnums.swift in Sources */,
|
||||
DDDB444429F8A8DD00EE2349 /* Float.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -144,9 +144,10 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
newNode.snr = packet.rxSnr
|
||||
newNode.rssi = packet.rxRssi
|
||||
newNode.viaMqtt = packet.viaMqtt
|
||||
newNode.channel = Int32(packet.channel)
|
||||
if packet.to == 4294967295 || packet.to == UserDefaults.preferredPeripheralNum {
|
||||
newNode.channel = Int32(packet.channel)
|
||||
}
|
||||
if let nodeInfoMessage = try? NodeInfo(serializedData: packet.decoded.payload) {
|
||||
newNode.channel = Int32(nodeInfoMessage.channel)
|
||||
newNode.hopsAway = Int32(truncatingIfNeeded: nodeInfoMessage.hopsAway)
|
||||
}
|
||||
if let newUserMessage = try? User(serializedData: packet.decoded.payload) {
|
||||
|
|
@ -212,7 +213,9 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
fetchedNode[0].snr = packet.rxSnr
|
||||
fetchedNode[0].rssi = packet.rxRssi
|
||||
fetchedNode[0].viaMqtt = packet.viaMqtt
|
||||
fetchedNode[0].channel = Int32(packet.channel)
|
||||
if packet.to == 4294967295 || packet.to == UserDefaults.preferredPeripheralNum {
|
||||
fetchedNode[0].channel = Int32(packet.channel)
|
||||
}
|
||||
|
||||
if let nodeInfoMessage = try? NodeInfo(serializedData: packet.decoded.payload) {
|
||||
fetchedNode[0].channel = Int32(nodeInfoMessage.channel)
|
||||
|
|
|
|||
166
Meshtastic/Views/Nodes/Helpers/Map/NodeMapContent.swift
Normal file
166
Meshtastic/Views/Nodes/Helpers/Map/NodeMapContent.swift
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
//
|
||||
// RouteLines.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Created by Garth Vander Houwen on 3/14/24.
|
||||
//
|
||||
import SwiftUI
|
||||
import MapKit
|
||||
|
||||
@available(iOS 17.0, macOS 14.0, *)
|
||||
struct NodeMapContent: MapContent {
|
||||
|
||||
@ObservedObject var node: NodeInfoEntity
|
||||
@State var showUserLocation: Bool = false
|
||||
@State var positions: [PositionEntity] = []
|
||||
/// Map State User Defaults
|
||||
@AppStorage("meshMapShowNodeHistory") private var showNodeHistory = false
|
||||
@AppStorage("meshMapShowRouteLines") private var showRouteLines = false
|
||||
@AppStorage("enableMapConvexHull") private var showConvexHull = false
|
||||
@AppStorage("enableMapTraffic") private var showTraffic: Bool = false
|
||||
@AppStorage("enableMapPointsOfInterest") private var showPointsOfInterest: Bool = false
|
||||
@AppStorage("mapLayer") private var selectedMapLayer: MapLayer = .hybrid
|
||||
// Map Configuration
|
||||
@Namespace var mapScope
|
||||
@State var mapStyle: MapStyle = MapStyle.hybrid(elevation: .realistic, pointsOfInterest: .all, showsTraffic: true)
|
||||
@State var position = MapCameraPosition.automatic
|
||||
@State var scene: MKLookAroundScene?
|
||||
@State var isLookingAround = false
|
||||
@State var isShowingAltitude = false
|
||||
@State var isEditingSettings = false
|
||||
@State var selectedPosition: PositionEntity?
|
||||
@State var showWaypoints = false
|
||||
@State var selectedWaypoint: WaypointEntity?
|
||||
@State var isMeshMap = false
|
||||
|
||||
//let region: MKCoordinateRegion
|
||||
|
||||
|
||||
@MapContentBuilder
|
||||
var nodeMap: some MapContent {
|
||||
let positionArray = node.positions?.array as? [PositionEntity] ?? []
|
||||
let lineCoords = positionArray.compactMap({(position) -> CLLocationCoordinate2D in
|
||||
return position.nodeCoordinate ?? LocationsHandler.DefaultLocation
|
||||
})
|
||||
/// Node Color from node.num
|
||||
let nodeColor = UIColor(hex: UInt32(node.num))
|
||||
|
||||
|
||||
/// Node Annotations
|
||||
ForEach(positionArray, id: \.id) { position in
|
||||
let pf = PositionFlags(rawValue: Int(position.nodePosition?.metadata?.positionFlags ?? 771))
|
||||
let headingDegrees = Angle.degrees(Double(position.heading))
|
||||
/// Reduced Precision Map Circle
|
||||
if position.latest && 11...16 ~= position.precisionBits {
|
||||
let pp = PositionPrecision(rawValue: Int(position.precisionBits))
|
||||
let radius : CLLocationDistance = pp?.precisionMeters ?? 0
|
||||
if radius > 0.0 {
|
||||
MapCircle(center: position.coordinate, radius: radius)
|
||||
.foregroundStyle(Color(nodeColor).opacity(0.25))
|
||||
.stroke(.white, lineWidth: 2)
|
||||
}
|
||||
}
|
||||
if showConvexHull {
|
||||
if lineCoords.count > 0 {
|
||||
let hull = lineCoords.getConvexHull()
|
||||
MapPolygon(coordinates: hull)
|
||||
.stroke(Color(nodeColor.darker()), lineWidth: 3)
|
||||
.foregroundStyle(Color(nodeColor).opacity(0.4))
|
||||
}
|
||||
}
|
||||
/// Route Lines
|
||||
if showRouteLines {
|
||||
let gradient = LinearGradient(
|
||||
colors: [Color(nodeColor.lighter().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: lineCoords)
|
||||
.stroke(gradient, style: dashed)
|
||||
}
|
||||
|
||||
/// Node Annotations
|
||||
ForEach(positionArray, id: \.id) { position in
|
||||
Annotation(position.latest ? node.user?.shortName ?? "?": "", coordinate: position.coordinate) {
|
||||
LazyVStack {
|
||||
if position.latest {
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(Color(nodeColor.lighter()).opacity(0.4).shadow(.drop(color: Color(nodeColor).isLight() ? .black : .white, radius: 5)))
|
||||
.foregroundStyle(Color(nodeColor.lighter()).opacity(0.3))
|
||||
.frame(width: 50, height: 50)
|
||||
if pf.contains(.Heading) {
|
||||
Image(systemName: pf.contains(.Speed) && position.speed > 1 ? "location.north" : "octagon")
|
||||
.symbolEffect(.pulse.byLayer)
|
||||
.padding(5)
|
||||
.foregroundStyle(Color(nodeColor).isLight() ? .black : .white)
|
||||
.background(Color(nodeColor.darker()))
|
||||
.clipShape(Circle())
|
||||
.rotationEffect(headingDegrees)
|
||||
.onTapGesture {
|
||||
selectedPosition = (selectedPosition == position ? nil : position)
|
||||
}
|
||||
.popover(item: $selectedPosition) { selection in
|
||||
PositionPopover(position: selection)
|
||||
.padding()
|
||||
.opacity(0.8)
|
||||
.presentationCompactAdaptation(.popover)
|
||||
}
|
||||
|
||||
} else {
|
||||
Image(systemName: "flipphone")
|
||||
.symbolEffect(.pulse.byLayer)
|
||||
.padding(5)
|
||||
.foregroundStyle(Color(nodeColor).isLight() ? .black : .white)
|
||||
.background(Color(UIColor(hex: UInt32(node.num)).darker()))
|
||||
.clipShape(Circle())
|
||||
.onTapGesture {
|
||||
selectedPosition = (selectedPosition == position ? nil : position)
|
||||
}
|
||||
.popover(item: $selectedPosition) { selection in
|
||||
PositionPopover(position: selection)
|
||||
.padding()
|
||||
.opacity(0.8)
|
||||
.presentationCompactAdaptation(.popover)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if showNodeHistory {
|
||||
if pf.contains(.Heading) {
|
||||
Image(systemName: "location.north.circle")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.foregroundStyle(Color(UIColor(hex: UInt32(node.num))).isLight() ? .black : .white)
|
||||
.background(Color(UIColor(hex: UInt32(node.num))))
|
||||
.clipShape(Circle())
|
||||
.rotationEffect(headingDegrees)
|
||||
.frame(width: 16, height: 16)
|
||||
|
||||
} else {
|
||||
Circle()
|
||||
.fill(Color(UIColor(hex: UInt32(node.num))))
|
||||
.strokeBorder(Color(UIColor(hex: UInt32(node.num))).isLight() ? .black : .white ,lineWidth: 2)
|
||||
.frame(width: 12, height: 12)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.tag(position.time)
|
||||
.annotationTitles(.automatic)
|
||||
.annotationSubtitles(.automatic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MapContentBuilder
|
||||
var body: some MapContent {
|
||||
if node.positions?.count ?? 0 > 0 {
|
||||
nodeMap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -57,120 +57,7 @@ struct NodeMapSwiftUI: View {
|
|||
ZStack {
|
||||
MapReader { reader in
|
||||
Map(position: $position, bounds: MapCameraBounds(minimumDistance: 1, maximumDistance: .infinity), scope: mapScope) {
|
||||
/// Node Color from node.num
|
||||
let nodeColor = UIColor(hex: UInt32(node.num))
|
||||
/// Convex Hull
|
||||
if showConvexHull {
|
||||
if lineCoords.count > 0 {
|
||||
let hull = lineCoords.getConvexHull()
|
||||
MapPolygon(coordinates: hull)
|
||||
.stroke(Color(nodeColor.darker()), lineWidth: 3)
|
||||
.foregroundStyle(Color(nodeColor).opacity(0.4))
|
||||
}
|
||||
}
|
||||
/// Route Lines
|
||||
if showRouteLines {
|
||||
let gradient = LinearGradient(
|
||||
colors: [Color(nodeColor.lighter().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: lineCoords)
|
||||
.stroke(gradient, style: dashed)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Node Annotations
|
||||
ForEach(positionArray, id: \.id) { position in
|
||||
let pf = PositionFlags(rawValue: Int(position.nodePosition?.metadata?.positionFlags ?? 771))
|
||||
let headingDegrees = Angle.degrees(Double(position.heading))
|
||||
// /// Reduced Precision Map Circle
|
||||
// if position.latest && 11...16 ~= position.precisionBits {
|
||||
// let pp = PositionPrecision(rawValue: Int(position.precisionBits))
|
||||
// let radius : CLLocationDistance = pp?.precisionMeters ?? 0
|
||||
// if radius > 0.0 {
|
||||
// MapCircle(center: position.coordinate, radius: radius)
|
||||
// .foregroundStyle(Color(nodeColor).opacity(0.25))
|
||||
// .stroke(.white, lineWidth: 2)
|
||||
// }
|
||||
// }
|
||||
// Annotation(position.latest ? node.user?.shortName ?? "?": "", coordinate: position.coordinate) {
|
||||
// LazyVStack {
|
||||
// if position.latest {
|
||||
// ZStack {
|
||||
// Circle()
|
||||
// .fill(Color(nodeColor.lighter()).opacity(0.4).shadow(.drop(color: Color(nodeColor).isLight() ? .black : .white, radius: 5)))
|
||||
// .foregroundStyle(Color(nodeColor.lighter()).opacity(0.3))
|
||||
// .frame(width: 50, height: 50)
|
||||
// if pf.contains(.Heading) {
|
||||
// Image(systemName: pf.contains(.Speed) && position.speed > 1 ? "location.north" : "octagon")
|
||||
// .symbolEffect(.pulse.byLayer)
|
||||
// .padding(5)
|
||||
// .foregroundStyle(Color(nodeColor).isLight() ? .black : .white)
|
||||
// .background(Color(nodeColor.darker()))
|
||||
// .clipShape(Circle())
|
||||
// .rotationEffect(headingDegrees)
|
||||
// .onTapGesture {
|
||||
// selectedPosition = (selectedPosition == position ? nil : position)
|
||||
// }
|
||||
// .popover(item: $selectedPosition) { selection in
|
||||
// PositionPopover(position: selection)
|
||||
// .padding()
|
||||
// .opacity(0.8)
|
||||
// .presentationCompactAdaptation(.popover)
|
||||
// }
|
||||
//
|
||||
// } else {
|
||||
// Image(systemName: "flipphone")
|
||||
// .symbolEffect(.pulse.byLayer)
|
||||
// .padding(5)
|
||||
// .foregroundStyle(Color(nodeColor).isLight() ? .black : .white)
|
||||
// .background(Color(UIColor(hex: UInt32(node.num)).darker()))
|
||||
// .clipShape(Circle())
|
||||
// .onTapGesture {
|
||||
// selectedPosition = (selectedPosition == position ? nil : position)
|
||||
// }
|
||||
// .popover(item: $selectedPosition) { selection in
|
||||
// PositionPopover(position: selection)
|
||||
// .padding()
|
||||
// .opacity(0.8)
|
||||
// .presentationCompactAdaptation(.popover)
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// if showNodeHistory {
|
||||
// if pf.contains(.Heading) {
|
||||
// Image(systemName: "location.north.circle")
|
||||
// .resizable()
|
||||
// .scaledToFit()
|
||||
// .foregroundStyle(Color(UIColor(hex: UInt32(node.num))).isLight() ? .black : .white)
|
||||
// .background(Color(UIColor(hex: UInt32(node.num))))
|
||||
// .clipShape(Circle())
|
||||
// .rotationEffect(headingDegrees)
|
||||
// .frame(width: 16, height: 16)
|
||||
//
|
||||
// } else {
|
||||
// Circle()
|
||||
// .fill(Color(UIColor(hex: UInt32(node.num))))
|
||||
// .strokeBorder(Color(UIColor(hex: UInt32(node.num))).isLight() ? .black : .white ,lineWidth: 2)
|
||||
// .frame(width: 12, height: 12)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
// }
|
||||
// .tag(position.time)
|
||||
// .annotationTitles(.automatic)
|
||||
// .annotationSubtitles(.automatic)
|
||||
// }
|
||||
NodeMapContent(node: node)
|
||||
}
|
||||
.mapScope(mapScope)
|
||||
.mapStyle(mapStyle)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue