mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Tidy up some location things
This commit is contained in:
parent
ca4354d853
commit
f9cf71ad4e
11 changed files with 109 additions and 93 deletions
|
|
@ -784,25 +784,25 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
|
|||
if lastPosition != nil {
|
||||
let connectedNode = getNodeInfo(id: connectedPeripheral?.num ?? 0, context: context!)
|
||||
if connectedNode?.positionConfig?.smartPositionEnabled ?? false {
|
||||
if lastPosition!.distance(from: LocationHelper.currentLocation) < Double(connectedNode?.positionConfig?.broadcastSmartMinimumDistance ?? 50) {
|
||||
if lastPosition!.distance(from: LocationHelper.currentLocation.coordinate) < Double(connectedNode?.positionConfig?.broadcastSmartMinimumDistance ?? 50) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lastPosition = LocationHelper.currentLocation
|
||||
lastPosition = LocationHelper.currentLocation.coordinate
|
||||
var positionPacket = Position()
|
||||
positionPacket.latitudeI = Int32(LocationHelper.currentLocation.latitude * 1e7)
|
||||
positionPacket.longitudeI = Int32(LocationHelper.currentLocation.longitude * 1e7)
|
||||
positionPacket.time = UInt32(LocationHelper.currentTimestamp.timeIntervalSince1970)
|
||||
positionPacket.timestamp = UInt32(LocationHelper.currentTimestamp.timeIntervalSince1970)
|
||||
positionPacket.altitude = Int32(LocationHelper.currentAltitude)
|
||||
positionPacket.latitudeI = Int32(LocationHelper.currentLocation.coordinate.latitude * 1e7)
|
||||
positionPacket.longitudeI = Int32(LocationHelper.currentLocation.coordinate.longitude * 1e7)
|
||||
positionPacket.time = UInt32(LocationHelper.currentLocation.timestamp.timeIntervalSince1970)
|
||||
positionPacket.timestamp = UInt32(LocationHelper.currentLocation.timestamp.timeIntervalSince1970)
|
||||
positionPacket.altitude = Int32(LocationHelper.currentLocation.altitude)
|
||||
positionPacket.satsInView = UInt32(LocationHelper.satsInView)
|
||||
if LocationHelper.currentSpeed >= 0 {
|
||||
positionPacket.groundSpeed = UInt32(LocationHelper.currentSpeed * 3.6)
|
||||
if LocationHelper.currentLocation.speed >= 0 {
|
||||
positionPacket.groundSpeed = UInt32(LocationHelper.currentLocation.speed * 3.6)
|
||||
}
|
||||
if LocationHelper.currentHeading >= 0 {
|
||||
positionPacket.groundTrack = UInt32(LocationHelper.currentHeading)
|
||||
if LocationHelper.currentLocation.course >= 0 {
|
||||
positionPacket.groundTrack = UInt32(LocationHelper.currentLocation.course)
|
||||
}
|
||||
var meshPacket = MeshPacket()
|
||||
meshPacket.to = UInt32(destNum)
|
||||
|
|
|
|||
|
|
@ -1,58 +1,48 @@
|
|||
import CoreLocation
|
||||
|
||||
class LocationHelper: NSObject, ObservableObject {
|
||||
class LocationHelper: NSObject, ObservableObject, CLLocationManagerDelegate {
|
||||
|
||||
private let locationManager = CLLocationManager()
|
||||
static let shared = LocationHelper()
|
||||
@Published var lastLocation: CLLocation?
|
||||
|
||||
override init() {
|
||||
|
||||
super.init()
|
||||
locationManager.delegate = self
|
||||
locationManager.desiredAccuracy = kCLLocationAccuracyBest
|
||||
locationManager.pausesLocationUpdatesAutomatically = true
|
||||
locationManager.allowsBackgroundLocationUpdates = true
|
||||
locationManager.activityType = .otherNavigation
|
||||
locationManager.requestWhenInUseAuthorization()
|
||||
locationManager.distanceFilter = 5
|
||||
locationManager.startUpdatingLocation()
|
||||
}
|
||||
|
||||
// Apple Park
|
||||
static let DefaultLocation = CLLocationCoordinate2D(latitude: 37.3346, longitude: -122.0090)
|
||||
static let DefaultAltitude = CLLocationDistance(integerLiteral: 0)
|
||||
static let DefaultSpeed = CLLocationSpeed(integerLiteral: 0)
|
||||
static let DefaultHeading = CLLocationDirection(integerLiteral: 0)
|
||||
static let DefaultLocation = CLLocation(latitude: 37.3346, longitude: -122.0090)
|
||||
//static let DefaultAltitude = CLLocationDistance(integerLiteral: 0)
|
||||
//static let DefaultSpeed = CLLocationSpeed(integerLiteral: 0)
|
||||
//static let DefaultHeading = CLLocationDirection(integerLiteral: 0)
|
||||
|
||||
static var currentLocation: CLLocationCoordinate2D {
|
||||
static var currentLocation: CLLocation {
|
||||
|
||||
guard let location = shared.locationManager.location else {
|
||||
return DefaultLocation
|
||||
}
|
||||
return location.coordinate
|
||||
return location
|
||||
}
|
||||
|
||||
static var currentAltitude: CLLocationDistance {
|
||||
|
||||
guard let altitude = shared.locationManager.location?.altitude else {
|
||||
return DefaultAltitude
|
||||
}
|
||||
return altitude
|
||||
}
|
||||
|
||||
static var currentSpeed: CLLocationSpeed {
|
||||
|
||||
guard let speed = shared.locationManager.location?.speed else {
|
||||
return DefaultSpeed
|
||||
}
|
||||
return speed
|
||||
}
|
||||
|
||||
static var currentHeading: CLLocationDirection {
|
||||
|
||||
guard let heading = shared.locationManager.location?.course else {
|
||||
return DefaultHeading
|
||||
}
|
||||
return heading
|
||||
}
|
||||
|
||||
static var currentTimestamp: Date {
|
||||
|
||||
guard let timestamp = shared.locationManager.location?.timestamp else {
|
||||
return Date.now
|
||||
}
|
||||
return timestamp
|
||||
}
|
||||
|
||||
/// Sats In View Estimator using horizontal and vertical accuracy since
|
||||
/// CoreLocation does not have number of sats available
|
||||
static var satsInView: Int {
|
||||
// Invalid Coordinates
|
||||
if shared.locationManager.location?.verticalAccuracy ?? 0 < 0 || shared.locationManager.location?.horizontalAccuracy ?? 0 < 0 {
|
||||
return 0
|
||||
}
|
||||
// If we have a position we have a sat
|
||||
var sats = 1
|
||||
// If we have a 3D fix we have at least 4 sats
|
||||
if shared.locationManager.location?.verticalAccuracy ?? 0 > 0 {
|
||||
sats = 4
|
||||
if 0...5 ~= shared.locationManager.location?.horizontalAccuracy ?? 0 {
|
||||
|
|
@ -66,37 +56,37 @@ class LocationHelper: NSObject, ObservableObject {
|
|||
} else if 46...60 ~= shared.locationManager.location?.horizontalAccuracy ?? 0 {
|
||||
sats = 5
|
||||
}
|
||||
} else if shared.locationManager.location?.verticalAccuracy ?? 0 < 0 && 60...300 ~= shared.locationManager.location?.horizontalAccuracy ?? 0 {
|
||||
} else if shared.locationManager.location?.horizontalAccuracy ?? 0 <= 300 {
|
||||
// Need at least 3 sats to be under 300, over that could be wifi or cell triangulation
|
||||
sats = 3
|
||||
} else if shared.locationManager.location?.verticalAccuracy ?? 0 < 0 && shared.locationManager.location?.horizontalAccuracy ?? 0 > 300 {
|
||||
} else if shared.locationManager.location?.horizontalAccuracy ?? 0 > 300 {
|
||||
sats = 2
|
||||
}
|
||||
return sats
|
||||
}
|
||||
|
||||
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
|
||||
|
||||
private let locationManager = CLLocationManager()
|
||||
guard let mostRecentLocation = locations.last else {
|
||||
return
|
||||
}
|
||||
// Extra Smart positioning logic throwing out bad readings from the phone GPS
|
||||
let age = -mostRecentLocation.timestamp.timeIntervalSinceNow
|
||||
print("Location: HA-\(mostRecentLocation.horizontalAccuracy) VA-\(mostRecentLocation.verticalAccuracy) AGE-\(age)")
|
||||
manager.stopUpdatingLocation()
|
||||
if age > 10 || mostRecentLocation.horizontalAccuracy < 0 || mostRecentLocation.horizontalAccuracy > 100 {
|
||||
print("Bad Location: HA-\(mostRecentLocation.horizontalAccuracy) VA-\(mostRecentLocation.verticalAccuracy) AGE-\(age)")
|
||||
manager.startUpdatingLocation()
|
||||
} else {
|
||||
lastLocation = mostRecentLocation
|
||||
}
|
||||
}
|
||||
|
||||
public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
|
||||
print("Location manager failed with error: \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
private override init() {
|
||||
|
||||
super.init()
|
||||
locationManager.delegate = self
|
||||
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
|
||||
locationManager.pausesLocationUpdatesAutomatically = true
|
||||
locationManager.allowsBackgroundLocationUpdates = true
|
||||
locationManager.activityType = .otherNavigation
|
||||
locationManager.requestWhenInUseAuthorization()
|
||||
locationManager.startUpdatingLocation()
|
||||
}
|
||||
}
|
||||
|
||||
extension LocationHelper: CLLocationManagerDelegate {
|
||||
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { }
|
||||
|
||||
public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
|
||||
print("Location manager failed with error: \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
|
||||
print("Location manager changed the status: \(status)")
|
||||
}
|
||||
public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
|
||||
print("Location manager changed the status: \(status)")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ extension PositionEntity {
|
|||
}
|
||||
|
||||
extension PositionEntity: MKAnnotation {
|
||||
public var coordinate: CLLocationCoordinate2D { nodeCoordinate ?? LocationHelper.DefaultLocation }
|
||||
public var coordinate: CLLocationCoordinate2D { nodeCoordinate ?? LocationHelper.DefaultLocation.coordinate }
|
||||
public var title: String? { nodePosition?.user?.shortName ?? NSLocalizedString("unknown", comment: "Unknown") }
|
||||
public var subtitle: String? { time?.formatted() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ extension WaypointEntity {
|
|||
}
|
||||
|
||||
extension WaypointEntity: MKAnnotation {
|
||||
public var coordinate: CLLocationCoordinate2D { waypointCoordinate ?? LocationHelper.DefaultLocation }
|
||||
public var coordinate: CLLocationCoordinate2D { waypointCoordinate ?? LocationHelper.DefaultLocation.coordinate }
|
||||
public var title: String? { name ?? "Dropped Pin" }
|
||||
public var subtitle: String? {
|
||||
(longDescription ?? "") +
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ struct NodeWeatherForecast {
|
|||
|
||||
struct NodeWeatherForecastView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NodeWeatherForecastView(location: CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude) )
|
||||
NodeWeatherForecastView(location: CLLocation(latitude: LocationHelper.currentLocation.coordinate.latitude, longitude: LocationHelper.currentLocation.coordinate.longitude) )
|
||||
.aspectRatio(2, contentMode: .fit)
|
||||
.padding()
|
||||
.previewLayout(.sizeThatFits)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
.filter { $0.latest == true }
|
||||
.sorted { $0.nodePosition?.num ?? 0 > $1.nodePosition?.num ?? -1 }
|
||||
let span = MKCoordinateSpan(latitudeDelta: 0.003, longitudeDelta: 0.003)
|
||||
let center = (latest.count > 0 && userTrackingMode == MKUserTrackingMode.none) ? latest[0].coordinate : LocationHelper.currentLocation
|
||||
let center = (latest.count > 0 && userTrackingMode == MKUserTrackingMode.none) ? latest[0].coordinate : LocationHelper.currentLocation.coordinate
|
||||
let region = MKCoordinateRegion(center: center, span: span)
|
||||
mapView.addAnnotations(showNodeHistory ? positions : latest)
|
||||
mapView.setRegion(region, animated: true)
|
||||
|
|
@ -263,7 +263,7 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
annotationView.glyphImage = UIImage(systemName: "flipphone")
|
||||
}
|
||||
if LocationHelper.currentLocation.distance(from: LocationHelper.DefaultLocation) > 0.0 {
|
||||
let metersAway = positionAnnotation.coordinate.distance(from: LocationHelper.currentLocation)
|
||||
let metersAway = positionAnnotation.coordinate.distance(from: LocationHelper.currentLocation.coordinate)
|
||||
subtitle.text! += NSLocalizedString("distance", comment: "") + ": \(distanceFormatter.string(fromDistance: Double(metersAway))) \n"
|
||||
}
|
||||
subtitle.text! += positionAnnotation.time?.formatted() ?? "Unknown \n"
|
||||
|
|
@ -296,7 +296,7 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
subtitle.text = ""
|
||||
}
|
||||
if LocationHelper.currentLocation.distance(from: LocationHelper.DefaultLocation) > 0.0 {
|
||||
let metersAway = waypointAnnotation.coordinate.distance(from: LocationHelper.currentLocation)
|
||||
let metersAway = waypointAnnotation.coordinate.distance(from: LocationHelper.currentLocation.coordinate)
|
||||
let distanceFormatter = MKDistanceFormatter()
|
||||
subtitle.text! += NSLocalizedString("distance", comment: "") + ": \(distanceFormatter.string(fromDistance: Double(metersAway))) \n"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,14 +30,14 @@ struct WaypointFormView: View {
|
|||
var body: some View {
|
||||
|
||||
Form {
|
||||
let distance = CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude).distance(from: CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude))
|
||||
let distance = CLLocation(latitude: LocationHelper.currentLocation.coordinate.latitude, longitude: LocationHelper.currentLocation.coordinate.longitude).distance(from: CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude))
|
||||
Section(header: Text((waypointId > 0) ? "Editing Waypoint" : "Create Waypoint")) {
|
||||
HStack {
|
||||
Text("Location: \(String(format: "%.5f", latitude) + "," + String(format: "%.5f", longitude))")
|
||||
.textSelection(.enabled)
|
||||
.foregroundColor(Color.gray)
|
||||
.font(.caption2)
|
||||
if coordinate.latitude != LocationHelper.DefaultLocation.latitude && coordinate.longitude != LocationHelper.DefaultLocation.longitude {
|
||||
if coordinate.latitude != LocationHelper.DefaultLocation.coordinate.latitude && coordinate.longitude != LocationHelper.DefaultLocation.coordinate.longitude {
|
||||
DistanceText(meters: distance)
|
||||
.foregroundColor(Color.gray)
|
||||
.font(.caption2)
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ struct NodeDetail: View {
|
|||
.font(.title)
|
||||
.padding()
|
||||
let nodeLocation = node.positions?.lastObject as? PositionEntity
|
||||
NodeWeatherForecastView(location: CLLocation(latitude: nodeLocation?.nodeCoordinate!.latitude ?? LocationHelper.currentLocation.latitude, longitude: nodeLocation?.nodeCoordinate!.longitude ?? LocationHelper.currentLocation.longitude) )
|
||||
NodeWeatherForecastView(location: CLLocation(latitude: nodeLocation?.nodeCoordinate!.latitude ?? LocationHelper.currentLocation.coordinate.latitude, longitude: nodeLocation?.nodeCoordinate!.longitude ?? LocationHelper.currentLocation.coordinate.longitude) )
|
||||
.frame(height: 250)
|
||||
}
|
||||
#else
|
||||
|
|
@ -117,7 +117,7 @@ struct NodeDetail: View {
|
|||
.font(.title)
|
||||
.padding()
|
||||
let nodeLocation = node.positions?.lastObject as? PositionEntity
|
||||
NodeWeatherForecastView(location: CLLocation(latitude: nodeLocation?.nodeCoordinate!.latitude ?? LocationHelper.currentLocation.latitude, longitude: nodeLocation?.nodeCoordinate!.longitude ?? LocationHelper.currentLocation.longitude) ).frame(height: 250)
|
||||
NodeWeatherForecastView(location: CLLocation(latitude: nodeLocation?.nodeCoordinate!.latitude ?? LocationHelper.currentLocation.coordinate.latitude, longitude: nodeLocation?.nodeCoordinate!.longitude ?? LocationHelper.currentLocation.coordinate.longitude) ).frame(height: 250)
|
||||
.presentationDetents([.medium])
|
||||
.presentationDragIndicator(.automatic)
|
||||
}
|
||||
|
|
@ -211,7 +211,7 @@ struct NodeDetail: View {
|
|||
}
|
||||
.edgesIgnoringSafeArea([.leading, .trailing])
|
||||
.sheet(isPresented: $presentingWaypointForm ) {// , onDismiss: didDismissSheet) {
|
||||
WaypointFormView(coordinate: waypointCoordinate ?? LocationHelper.DefaultLocation, waypointId: editingWaypoint)
|
||||
WaypointFormView(coordinate: waypointCoordinate ?? LocationHelper.DefaultLocation.coordinate, waypointId: editingWaypoint)
|
||||
.presentationDetents([.medium, .large])
|
||||
.presentationDragIndicator(.automatic)
|
||||
}
|
||||
|
|
@ -250,7 +250,7 @@ struct NodeDetail: View {
|
|||
|
||||
let mostRecent = node.positions?.lastObject as? PositionEntity
|
||||
|
||||
let weather = try await WeatherService.shared.weather(for: mostRecent?.nodeLocation ?? CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude))
|
||||
let weather = try await WeatherService.shared.weather(for: mostRecent?.nodeLocation ?? CLLocation(latitude: LocationHelper.currentLocation.coordinate.latitude, longitude: LocationHelper.currentLocation.coordinate.longitude))
|
||||
condition = weather.currentWeather.condition
|
||||
temperature = weather.currentWeather.temperature
|
||||
humidity = Int(weather.currentWeather.humidity * 100)
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@ struct NodeList: View {
|
|||
if node.positions?.count ?? 0 > 0 && (bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral?.num ?? -1 != node.num) {
|
||||
HStack(alignment: .bottom) {
|
||||
let lastPostion = node.positions!.reversed()[0] as! PositionEntity
|
||||
let myCoord = CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude)
|
||||
if lastPostion.nodeCoordinate != nil && myCoord.coordinate.longitude != LocationHelper.DefaultLocation.longitude && myCoord.coordinate.latitude != LocationHelper.DefaultLocation.latitude {
|
||||
let myCoord = CLLocation(latitude: LocationHelper.currentLocation.coordinate.latitude, longitude: LocationHelper.currentLocation.coordinate.longitude)
|
||||
if lastPostion.nodeCoordinate != nil && myCoord.coordinate.longitude != LocationHelper.DefaultLocation.coordinate.longitude && myCoord.coordinate.latitude != LocationHelper.DefaultLocation.coordinate.latitude {
|
||||
let nodeCoord = CLLocation(latitude: lastPostion.nodeCoordinate!.latitude, longitude: lastPostion.nodeCoordinate!.longitude)
|
||||
let metersAway = nodeCoord.distance(from: myCoord)
|
||||
Image(systemName: "lines.measurement.horizontal")
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ struct NodeMap: View {
|
|||
|
||||
@State private var mapType: MKMapType = .standard
|
||||
@State private var userTrackingMode: MKUserTrackingMode = .none
|
||||
@State var waypointCoordinate: CLLocationCoordinate2D = LocationHelper.DefaultLocation
|
||||
@State var waypointCoordinate: CLLocationCoordinate2D = LocationHelper.DefaultLocation.coordinate
|
||||
@State var editingWaypoint: Int = 0
|
||||
@State private var presentingWaypointForm = false
|
||||
@State private var customMapOverlay: MapViewSwiftUI.CustomMapOverlay? = MapViewSwiftUI.CustomMapOverlay(
|
||||
|
|
@ -63,7 +63,7 @@ struct NodeMap: View {
|
|||
MapViewSwiftUI(onLongPress: { coord in
|
||||
waypointCoordinate = coord
|
||||
editingWaypoint = 0
|
||||
if waypointCoordinate.distance(from: LocationHelper.DefaultLocation) == 0.0 {
|
||||
if waypointCoordinate.distance(from: LocationHelper.DefaultLocation.coordinate) == 0.0 {
|
||||
print("Apple Park")
|
||||
} else {
|
||||
presentingWaypointForm = true
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ struct AppSettings: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@EnvironmentObject var userSettings: UserSettings
|
||||
@StateObject var locationHelper = LocationHelper()
|
||||
|
||||
@State private var isPresentingCoreDataResetConfirm = false
|
||||
@State private var preferredDeviceConnected = false
|
||||
|
|
@ -39,7 +40,32 @@ struct AppSettings: View {
|
|||
}
|
||||
|
||||
Section(header: Text("phone.gps")) {
|
||||
|
||||
let accuracy = Measurement(value: locationHelper.lastLocation?.horizontalAccuracy ?? 300, unit: UnitLength.meters)
|
||||
let altitiude = Measurement(value: locationHelper.lastLocation?.altitude ?? 0, unit: UnitLength.meters)
|
||||
let speed = Measurement(value: locationHelper.lastLocation?.speed ?? 0, unit: UnitSpeed.kilometersPerHour)
|
||||
Label("Coordinates \(String(format: "%.5f", locationHelper.lastLocation?.coordinate.latitude ?? 0)), \(String(format: "%.5f", locationHelper.lastLocation?.coordinate.longitude ?? 0))", systemImage: "mappin")
|
||||
.textSelection(.enabled)
|
||||
HStack {
|
||||
Label("GPS Accuracy \(accuracy.formatted())", systemImage: "scope")
|
||||
.font(.caption)
|
||||
|
||||
if LocationHelper.currentLocation.verticalAccuracy > 0 {
|
||||
Label("Altitude \(altitiude.formatted())", systemImage: "mountain.2")
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
if locationHelper.lastLocation?.courseAccuracy ?? 0 > 0 || locationHelper.lastLocation?.speedAccuracy ?? 0 > 0 {
|
||||
HStack {
|
||||
if locationHelper.lastLocation?.courseAccuracy ?? 0 > 0 {
|
||||
Label("Bearing \(String(format: "%.2f", locationHelper.lastLocation?.course ?? 0))°", systemImage: "location.circle")
|
||||
.font(.caption)
|
||||
}
|
||||
if locationHelper.lastLocation?.speedAccuracy ?? 0 > 0 {
|
||||
Label("Speed \(speed.formatted())", systemImage: "speedometer")
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
}
|
||||
Toggle(isOn: $userSettings.provideLocation) {
|
||||
|
||||
Label("provide.location", systemImage: "location.circle.fill")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue