mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
* New Tile Server
* More Emoji * Signal Strength indicators for lora * Restrict tile overlays to zoom levels
This commit is contained in:
parent
c425486658
commit
255e10bbe5
15 changed files with 111 additions and 68 deletions
|
|
@ -148,6 +148,7 @@ enum MapTileServerLinks: String, CaseIterable, Identifiable {
|
|||
case usgsTopo
|
||||
case usgsImageryTopo
|
||||
case usgsImageryOnly
|
||||
case terrain
|
||||
case toner
|
||||
case watercolor
|
||||
var id: String { self.rawValue }
|
||||
|
|
@ -173,8 +174,10 @@ enum MapTileServerLinks: String, CaseIterable, Identifiable {
|
|||
return "[USGS](https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer) [National Map](http://nationalmap.gov/) imagery and topographic overlay."
|
||||
case .usgsImageryOnly:
|
||||
return "[USGS](https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer) [National Map](http://nationalmap.gov/) imagery only overlay."
|
||||
case .terrain:
|
||||
return "[Map Tiles](http://maps.stamen.com/#terrain/) by [Stamen Design](https://stamen.com), under [CC BY 3.0](https://creativecommons.org/licenses/by/3.0/). Data © [OpenStreetMap](http://www.openstreetmap.org) contributors under [CC-BY-SA](http://creativecommons.org/licenses/by-sa/2.0/)."
|
||||
case .toner:
|
||||
return "[Stamen Design's](https://github.com/stamen/toner-carto) black and white map tiles."
|
||||
return "[Map Tiles](http://maps.stamen.com/#toner/) by [Stamen Design](https://stamen.com), under [CC BY 3.0](https://creativecommons.org/licenses/by/3.0/). Data © [OpenStreetMap](http://www.openstreetmap.org) contributors under [CC-BY-SA](http://creativecommons.org/licenses/by-sa/2.0/)."
|
||||
case .watercolor:
|
||||
return "Cooper Hewitt, Smithsonian Design Museum's [Watercolor Maptiles](https://watercolormaps.collection.cooperhewitt.org/) is a open-source mapping tool created by Stamen Design and built on [OpenStreetMap](http://www.openstreetmap.org) data."
|
||||
}
|
||||
|
|
@ -199,6 +202,8 @@ enum MapTileServerLinks: String, CaseIterable, Identifiable {
|
|||
return "USGS Topo Imagery"
|
||||
case .usgsImageryOnly:
|
||||
return "USGS Imagery Only"
|
||||
case .terrain:
|
||||
return "Terrain"
|
||||
case .toner:
|
||||
return "Toner"
|
||||
case .watercolor:
|
||||
|
|
@ -225,6 +230,8 @@ enum MapTileServerLinks: String, CaseIterable, Identifiable {
|
|||
return "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer/tile/{z}/{y}/{x}"
|
||||
case .usgsImageryOnly:
|
||||
return "https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}"
|
||||
case .terrain:
|
||||
return "https://stamen-tiles-d.a.ssl.fastly.net/terrain/{z}/{x}/{y}.png"
|
||||
case .toner:
|
||||
return "https://stamen-tiles.a.ssl.fastly.net/toner/{z}/{x}/{y}.png"
|
||||
case .watercolor:
|
||||
|
|
@ -252,6 +259,8 @@ enum MapTileServerLinks: String, CaseIterable, Identifiable {
|
|||
return [Int](6...15)
|
||||
case .usgsImageryOnly:
|
||||
return [Int](6...15)
|
||||
case .terrain:
|
||||
return [Int](0...15)
|
||||
case .toner:
|
||||
return [Int](0...18)
|
||||
case .watercolor:
|
||||
|
|
|
|||
|
|
@ -52,11 +52,11 @@ public extension FileManager {
|
|||
do {
|
||||
accumulatedSize += try contentItemURL.regularFileAllocatedSize()
|
||||
} catch {
|
||||
print("❤️ \(error.localizedDescription)")
|
||||
print("💥 File Manager Error: \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
}
|
||||
if let error = enumeratorError { print("❤️ AllocatedSizeOfDirectory enumeratorError = \(error.localizedDescription)") }
|
||||
if let error = enumeratorError { print("💥 AllocatedSizeOfDirectory enumeratorError = \(error.localizedDescription)") }
|
||||
|
||||
return Double(accumulatedSize).toBytes
|
||||
|
||||
|
|
|
|||
|
|
@ -49,12 +49,10 @@ struct Connect: View {
|
|||
Section(header: Text("connected.radio").font(.title)) {
|
||||
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == .connected {
|
||||
HStack {
|
||||
Image(systemName: "antenna.radiowaves.left.and.right")
|
||||
.resizable()
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.foregroundColor(.green)
|
||||
.frame(width: 60, height: 60)
|
||||
.padding(.trailing)
|
||||
VStack(alignment: .center) {
|
||||
CircleText(text: node?.user?.shortName ?? "???", color: Color(UIColor(hex: UInt32(node?.num ?? 0))), circleSize: 80, fontSize: (node?.user?.shortName ?? "???").isEmoji() ? 52 : 30, textColor: UIColor(hex: UInt32(node?.num ?? 0)).isLight() ? .black : .white )
|
||||
}
|
||||
.padding(.trailing)
|
||||
VStack(alignment: .leading) {
|
||||
if node != nil {
|
||||
Text(bleManager.connectedPeripheral.longName).font(.title2)
|
||||
|
|
@ -74,7 +72,8 @@ struct Connect: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.font(.caption).foregroundColor(Color.gray)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.gray)
|
||||
.padding([.top, .bottom])
|
||||
.swipeActions {
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import SwiftUI
|
|||
struct ConnectedDevice: View {
|
||||
var bluetoothOn: Bool
|
||||
var deviceConnected: Bool
|
||||
var name: String?
|
||||
var name: String
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ struct ConnectedDevice: View {
|
|||
.imageScale(.large)
|
||||
.foregroundColor(.green)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text(name!).font(.callout).foregroundColor(.gray)
|
||||
Text(name).font(name.isEmoji() ? .title : .callout).foregroundColor(.gray)
|
||||
} else {
|
||||
|
||||
Image(systemName: "antenna.radiowaves.left.and.right.slash")
|
||||
|
|
|
|||
|
|
@ -69,3 +69,34 @@ func getLoRaSignalStrength(snr: Float, rssi: Int32) -> LoRaSignalStrength {
|
|||
return .fair
|
||||
}
|
||||
}
|
||||
|
||||
func getRssiColor(rssi: Int32) -> Color {
|
||||
if rssi > -115 {
|
||||
/// Good
|
||||
return .green
|
||||
} else if rssi > -115 && rssi < -120 {
|
||||
/// Fair
|
||||
return .yellow
|
||||
} else if rssi > -126 {
|
||||
/// Bad
|
||||
return .orange
|
||||
} else {
|
||||
// None
|
||||
return .red
|
||||
}
|
||||
}
|
||||
|
||||
func getSnrColor(snr: Float) -> Color {
|
||||
if snr > -7 {
|
||||
/// Good
|
||||
return .green
|
||||
} else if snr < -7 && snr > -13 {
|
||||
/// Fair
|
||||
return .yellow
|
||||
} else if snr >= -14 {
|
||||
/// Bad
|
||||
return .orange
|
||||
} else {
|
||||
return .red
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,10 +51,10 @@ struct NodeInfoView: View {
|
|||
LoRaSignalStrengthIndicator(signalStrength: signalStrength)
|
||||
Text("Signal \(signalStrength.description)").font(.title)
|
||||
Text("SNR \(String(format: "%.2f", node.snr))dB")
|
||||
.foregroundColor(.gray)
|
||||
.foregroundColor(getSnrColor(snr: node.snr))
|
||||
.font(.title3)
|
||||
Text("RSSI \(node.rssi)dB")
|
||||
.foregroundColor(.gray)
|
||||
.foregroundColor(getRssiColor(rssi: node.rssi))
|
||||
.font(.title3)
|
||||
}
|
||||
Divider()
|
||||
|
|
@ -141,35 +141,35 @@ struct NodeInfoView: View {
|
|||
VStack(alignment: .center) {
|
||||
CircleText(text: node.user?.shortName ?? "???", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65, fontSize: (node.user?.shortName ?? "???").isEmoji() ? 42 : 20, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white )
|
||||
}
|
||||
Divider()
|
||||
VStack {
|
||||
if node.user != nil {
|
||||
|
||||
if node.user != nil {
|
||||
Divider()
|
||||
VStack {
|
||||
Image(node.user!.hwModel ?? "unset".localized)
|
||||
.resizable()
|
||||
.frame(width: 75, height: 75)
|
||||
.cornerRadius(5)
|
||||
Text(String(node.user!.hwModel ?? "unset".localized))
|
||||
.font(.caption).fixedSize()
|
||||
.font(.caption2).fixedSize()
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
if node.snr != 0 {
|
||||
Divider()
|
||||
VStack(alignment: .center) {
|
||||
let signalStrength = getLoRaSignalStrength(snr: node.snr, rssi: node.rssi)
|
||||
LoRaSignalStrengthIndicator(signalStrength: signalStrength)
|
||||
Text("Signal \(signalStrength.description)").font(.footnote)
|
||||
Text("SNR \(String(format: "%.2f", node.snr))dB")
|
||||
.foregroundColor(.gray)
|
||||
.foregroundColor(getSnrColor(snr: node.snr))
|
||||
.font(.caption2)
|
||||
Text("RSSI \(node.rssi)dB")
|
||||
.foregroundColor(.gray)
|
||||
.foregroundColor(getRssiColor(rssi: node.rssi))
|
||||
.font(.caption2)
|
||||
}
|
||||
Divider()
|
||||
}
|
||||
let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0"))
|
||||
if deviceMetrics?.count ?? 0 >= 1 {
|
||||
|
||||
Divider()
|
||||
let mostRecent = deviceMetrics?.lastObject as? TelemetryEntity
|
||||
VStack(alignment: .center) {
|
||||
BatteryGauge(batteryLevel: Double(mostRecent?.batteryLevel ?? 0))
|
||||
|
|
|
|||
|
|
@ -105,8 +105,8 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
if !UserDefaults.enableOfflineMapsMBTiles {
|
||||
let overlay = TileOverlay()
|
||||
overlay.canReplaceMapContent = false
|
||||
//overlay.minimumZ = 0
|
||||
//overlay.maximumZ = 17
|
||||
overlay.minimumZ = UserDefaults.mapTileServer.zoomRange.startIndex
|
||||
overlay.maximumZ = UserDefaults.mapTileServer.zoomRange.endIndex
|
||||
mapView.addOverlay(overlay, level: UserDefaults.mapTilesAboveLabels ? .aboveLabels : .aboveRoads)
|
||||
}
|
||||
case .satellite:
|
||||
|
|
@ -199,7 +199,6 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
print("Annotation Count: \(annotationCount) Map Annotations: \(mapView.annotations.count)")
|
||||
mapView.removeAnnotations(mapView.annotations)
|
||||
mapView.addAnnotations(waypoints)
|
||||
mapView.addAnnotations(showNodeHistory ? positions : latest)
|
||||
}
|
||||
if userTrackingMode == MKUserTrackingMode.none {
|
||||
mapView.showsUserLocation = false
|
||||
|
|
@ -209,14 +208,15 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
if latest.count == 1 {
|
||||
mapView.fit(annotations: showNodeHistory ? positions : latest, andShow: true)
|
||||
} else {
|
||||
mapView.addAnnotations(showNodeHistory ? positions : latest)
|
||||
mapView.fitAllAnnotations()
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
mapView.addAnnotations(showNodeHistory ? positions : latest)
|
||||
mapView.showsUserLocation = true
|
||||
}
|
||||
|
||||
mapView.setUserTrackingMode(userTrackingMode, animated: true)
|
||||
}
|
||||
|
||||
|
|
@ -381,7 +381,6 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
if view.tag > 0 {
|
||||
parent.onWaypointEdit(view.tag)
|
||||
}
|
||||
print(waypointAnnotation)
|
||||
|
||||
default: break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,7 +204,6 @@ struct UserMessageList: View {
|
|||
.id(message.messageId)
|
||||
.alert(isPresented: $showDeleteMessageAlert) {
|
||||
Alert(title: Text("Are you sure you want to delete this message?"), message: Text("This action is permanent."), primaryButton: .destructive(Text("Delete")) {
|
||||
print("OK button tapped")
|
||||
if deleteMessageId > 0 {
|
||||
let message = user.messageList.first(where: { $0.messageId == deleteMessageId })
|
||||
context.delete(message!)
|
||||
|
|
|
|||
|
|
@ -119,11 +119,13 @@ struct DeviceMetricsLog: View {
|
|||
} else {
|
||||
ScrollView {
|
||||
let columns = [
|
||||
GridItem(.flexible(minimum: 30, maximum: 60), spacing: 0.1),
|
||||
GridItem(.flexible(minimum: 30, maximum: 60), spacing: 0.1),
|
||||
//GridItem(.flexible(minimum: 30, maximum: 60), spacing: 0.1),
|
||||
GridItem(.flexible(minimum: 30, maximum: 45), spacing: 0.1),
|
||||
GridItem(.flexible(minimum: 30, maximum: 50), spacing: 0.1),
|
||||
GridItem(.flexible(minimum: 30, maximum: 70), spacing: 0.1),
|
||||
GridItem(.flexible(minimum: 30, maximum: 65), spacing: 0.1),
|
||||
GridItem(spacing: 0)
|
||||
GridItem(.flexible(minimum: 130, maximum: 200), spacing: 0.1)
|
||||
|
||||
]
|
||||
LazyVGrid(columns: columns, alignment: .leading, spacing: 1) {
|
||||
GridRow {
|
||||
|
|
@ -159,7 +161,7 @@ struct DeviceMetricsLog: View {
|
|||
Text("\(String(format: "%.2f", dm.airUtilTx))%")
|
||||
.font(.caption)
|
||||
Text(dm.time?.formattedDate(format: dateFormatString) ?? "Unknown time")
|
||||
.font(.caption2)
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -177,7 +179,8 @@ struct DeviceMetricsLog: View {
|
|||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
.padding(.bottom)
|
||||
.padding(.trailing)
|
||||
.confirmationDialog(
|
||||
"are.you.sure",
|
||||
isPresented: $isPresentingClearLogConfirm,
|
||||
|
|
@ -200,7 +203,8 @@ struct DeviceMetricsLog: View {
|
|||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
.padding(.bottom)
|
||||
.padding(.trailing)
|
||||
}
|
||||
.navigationTitle("device.metrics.log")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
|
|
|
|||
|
|
@ -108,10 +108,10 @@ struct EnvironmentMetricsLog: View {
|
|||
} else {
|
||||
ScrollView {
|
||||
let columns = [
|
||||
GridItem(.flexible(minimum: 30, maximum: 50), spacing: 0.1),
|
||||
GridItem(.flexible(minimum: 30, maximum: 60), spacing: 0.1),
|
||||
GridItem(.flexible(minimum: 30, maximum: 60), spacing: 0.1),
|
||||
GridItem(.flexible(minimum: 30, maximum: 60), spacing: 0.1),
|
||||
GridItem(.flexible(minimum: 30, maximum: 60), spacing: 0.1),
|
||||
GridItem(.flexible(minimum: 30, maximum: 50), spacing: 0.1),
|
||||
GridItem(spacing: 0)
|
||||
]
|
||||
LazyVGrid(columns: columns, alignment: .leading, spacing: 1, pinnedViews: [.sectionHeaders]) {
|
||||
|
|
@ -146,7 +146,7 @@ struct EnvironmentMetricsLog: View {
|
|||
Text("\(String(format: "%.2f", em.gasResistance))")
|
||||
.font(.caption)
|
||||
Text(em.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized)
|
||||
.font(.caption2)
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -160,12 +160,13 @@ struct EnvironmentMetricsLog: View {
|
|||
Button(role: .destructive) {
|
||||
isPresentingClearLogConfirm = true
|
||||
} label: {
|
||||
Label("Clear Log", systemImage: "trash.fill")
|
||||
Label("clear.log", systemImage: "trash.fill")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
.padding(.bottom)
|
||||
.padding(.trailing)
|
||||
.confirmationDialog(
|
||||
"are.you.sure",
|
||||
isPresented: $isPresentingClearLogConfirm,
|
||||
|
|
@ -186,7 +187,8 @@ struct EnvironmentMetricsLog: View {
|
|||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
.padding(.bottom)
|
||||
.padding(.leading)
|
||||
}
|
||||
.navigationTitle("Environment Metrics Log")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
|
|
|
|||
|
|
@ -38,13 +38,15 @@ struct NodeList: View {
|
|||
CircleText(text: node.user?.shortName ?? "???", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65, fontSize: (node.user?.shortName ?? "???").isEmoji() ? 44 : 22, brightness: 0.0, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white)
|
||||
.padding(.trailing, 5)
|
||||
VStack(alignment: .leading) {
|
||||
Text(node.user?.longName ?? "unknown".localized).font(.headline)
|
||||
Text(node.user?.longName ?? "unknown".localized)
|
||||
.fontWeight(.medium)
|
||||
.font(.callout)
|
||||
if connected {
|
||||
HStack(alignment: .bottom) {
|
||||
HStack {
|
||||
Image(systemName: "repeat.circle.fill")
|
||||
.font(.title3)
|
||||
.font(.callout)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("connected").font(.subheadline)
|
||||
Text("connected").font(.callout)
|
||||
.foregroundColor(.green)
|
||||
}
|
||||
}
|
||||
|
|
@ -56,28 +58,27 @@ struct NodeList: View {
|
|||
let nodeCoord = CLLocation(latitude: lastPostion.nodeCoordinate!.latitude, longitude: lastPostion.nodeCoordinate!.longitude)
|
||||
let metersAway = nodeCoord.distance(from: myCoord)
|
||||
Image(systemName: "lines.measurement.horizontal")
|
||||
.font(.title3)
|
||||
.font(.footnote)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
|
||||
DistanceText(meters: metersAway).font(.subheadline)
|
||||
DistanceText(meters: metersAway).font(.footnote)
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.channel > 0 {
|
||||
HStack(alignment: .bottom) {
|
||||
Image(systemName: "fibrechannel")
|
||||
.font(.title3)
|
||||
.font(.footnote)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Channel: \(node.channel)")
|
||||
.font(.subheadline)
|
||||
.font(.footnote)
|
||||
}
|
||||
}
|
||||
HStack(alignment: .bottom) {
|
||||
Image(systemName: "clock.badge.checkmark.fill")
|
||||
.font(.title3)
|
||||
.font(.caption)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
LastHeardText(lastHeard: node.lastHeard)
|
||||
.font(.subheadline)
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
|
|
|||
|
|
@ -166,7 +166,6 @@ struct NodeMap: View {
|
|||
.pickerStyle(DefaultPickerStyle())
|
||||
.onChange(of: (selectedTileServer)) { newSelectedTileServer in
|
||||
UserDefaults.mapTileServer = newSelectedTileServer
|
||||
//tileManager.removeAll()
|
||||
selectedMapLayer = .standard
|
||||
}
|
||||
Text("Attribution:")
|
||||
|
|
|
|||
|
|
@ -121,12 +121,13 @@ struct PositionLog: View {
|
|||
Button(role: .destructive) {
|
||||
isPresentingClearLogConfirm = true
|
||||
} label: {
|
||||
Label("Clear Log", systemImage: "trash.fill")
|
||||
Label("clear.log", systemImage: "trash.fill")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
.padding(.bottom)
|
||||
.padding(.trailing)
|
||||
.confirmationDialog(
|
||||
"are.you.sure",
|
||||
isPresented: $isPresentingClearLogConfirm,
|
||||
|
|
@ -143,10 +144,8 @@ struct PositionLog: View {
|
|||
}
|
||||
|
||||
Button {
|
||||
|
||||
exportString = positionToCsvFile(positions: node.positions!.array as? [PositionEntity] ?? [])
|
||||
isExporting = true
|
||||
|
||||
} label: {
|
||||
|
||||
Label("save", systemImage: "square.and.arrow.down")
|
||||
|
|
@ -154,7 +153,8 @@ struct PositionLog: View {
|
|||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
.padding(.bottom)
|
||||
.padding(.leading)
|
||||
}
|
||||
.fileExporter(
|
||||
isPresented: $isExporting,
|
||||
|
|
|
|||
|
|
@ -35,29 +35,30 @@ struct AppSettings: View {
|
|||
let speed = Measurement(value: locationHelper.locationManager.location?.speed ?? 0, unit: UnitSpeed.kilometersPerHour)
|
||||
HStack {
|
||||
Label("Accuracy \(accuracy.formatted())", systemImage: "scope")
|
||||
.font(.callout)
|
||||
.font(.footnote)
|
||||
Label("Sats \(LocationHelper.satsInView)", systemImage: "sparkles")
|
||||
.font(.callout)
|
||||
.font(.footnote)
|
||||
}
|
||||
Label("Coordinates \(String(format: "%.5f", locationHelper.locationManager.location?.coordinate.latitude ?? 0)), \(String(format: "%.5f", locationHelper.locationManager.location?.coordinate.longitude ?? 0))", systemImage: "mappin")
|
||||
.font(.callout)
|
||||
Label("Coordinate \(String(format: "%.5f", locationHelper.locationManager.location?.coordinate.latitude ?? 0)), \(String(format: "%.5f", locationHelper.locationManager.location?.coordinate.longitude ?? 0))", systemImage: "mappin")
|
||||
.font(.footnote)
|
||||
.textSelection(.enabled)
|
||||
if locationHelper.locationManager.location?.verticalAccuracy ?? 0 > 0 {
|
||||
Label("Altitude \(altitiude.formatted())", systemImage: "mountain.2")
|
||||
.font(.callout)
|
||||
.font(.footnote)
|
||||
}
|
||||
if locationHelper.locationManager.location?.courseAccuracy ?? 0 > 0 {
|
||||
Label("Heading \(String(format: "%.2f", locationHelper.locationManager.location?.course ?? 0))°", systemImage: "location.circle")
|
||||
.font(.callout)
|
||||
.font(.footnote)
|
||||
}
|
||||
if locationHelper.locationManager.location?.speedAccuracy ?? 0 > 0 {
|
||||
Label("Speed \(speed.formatted())", systemImage: "speedometer")
|
||||
.font(.callout)
|
||||
.font(.footnote)
|
||||
}
|
||||
|
||||
Toggle(isOn: $provideLocation) {
|
||||
|
||||
Label("provide.location", systemImage: "location.circle.fill")
|
||||
.font(.footnote)
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
|
|
@ -74,10 +75,9 @@ struct AppSettings: View {
|
|||
}
|
||||
|
||||
Text("phone.gps.interval.description")
|
||||
.font(.caption)
|
||||
.font(.caption2)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
|
||||
}
|
||||
TilesView()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
"channel.utilization"="Channel Utilization";
|
||||
"channels"="Channels";
|
||||
"clear.app.data"="Clear App Data";
|
||||
"clear.log"="Clear Log";
|
||||
"clear.log"="Clear";
|
||||
"close"="Close";
|
||||
"config.save.confirm"="After config values save the node will reboot.";
|
||||
"communicating"="Communicating with device. .";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue