From 8c59be875adff065144ea2bfb9b89c97ea0fb3ce Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Thu, 16 May 2024 17:15:17 +0900 Subject: [PATCH 01/12] Core library desugaring (Android); make accuracy values optional --- Package.resolved | 4 +- android/build.gradle | 2 +- android/core/build.gradle | 4 + android/core/src/main/AndroidManifest.xml | 1 + .../Extensions/CoreLocation Extensions.swift | 40 +- apple/Sources/UniFFI/ferrostar.swift | 16 +- common/Cargo.lock | 2 +- common/ferrostar/Cargo.toml | 2 +- common/ferrostar/src/models.rs | 6 +- .../src/routing_adapters/valhalla.rs | 2 +- common/ferrostar/src/simulation.rs | 4 +- .../ferrostar__simulation__tests__None.snap | 8 +- ...rostar__simulation__tests__Some(10.0).snap | 12 +- ...ts__extended_interpolation_simulation.snap | 862 +++++++++--------- ...imulation__tests__state_from_polyline.snap | 2 +- guide/src/platform-support-targets.md | 12 +- 16 files changed, 491 insertions(+), 488 deletions(-) diff --git a/Package.resolved b/Package.resolved index 1c7e72b5..fc9adc52 100644 --- a/Package.resolved +++ b/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/maplibre/maplibre-gl-native-distribution.git", "state" : { - "revision" : "6d0071977ed1f2380c739715f82ac650f99b0824", - "version" : "6.4.0" + "revision" : "6579f48fe126ce2916f7b5d0c84c1869d790c4e4", + "version" : "6.4.1" } }, { diff --git a/android/build.gradle b/android/build.gradle index 7345f8af..5b7b5705 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -23,5 +23,5 @@ publishing { allprojects { group = "com.stadiamaps.ferrostar" - version = "0.0.32" + version = "0.0.33" } diff --git a/android/core/build.gradle b/android/core/build.gradle index 3e1e564a..ac190ae9 100644 --- a/android/core/build.gradle +++ b/android/core/build.gradle @@ -25,6 +25,7 @@ android { } } compileOptions { + coreLibraryDesugaringEnabled true sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } @@ -42,6 +43,9 @@ android { dependencies { implementation(platform("com.squareup.okhttp3:okhttp-bom:4.10.0")) + // For as long as we support API 25; once we can raise support to 26, all is fine + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' + implementation 'androidx.core:core-ktx:1.12.0' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.squareup.okhttp3:okhttp' diff --git a/android/core/src/main/AndroidManifest.xml b/android/core/src/main/AndroidManifest.xml index a5918e68..09a0c769 100644 --- a/android/core/src/main/AndroidManifest.xml +++ b/android/core/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ + \ No newline at end of file diff --git a/apple/Sources/FerrostarCore/Extensions/CoreLocation Extensions.swift b/apple/Sources/FerrostarCore/Extensions/CoreLocation Extensions.swift index a652e541..02adf8c7 100644 --- a/apple/Sources/FerrostarCore/Extensions/CoreLocation Extensions.swift +++ b/apple/Sources/FerrostarCore/Extensions/CoreLocation Extensions.swift @@ -33,14 +33,14 @@ extension CLLocation { convenience init(userLocation: UserLocation) { let invalid: Double = -1.0 - let courseDegrees: CLLocationDirection - let courseAccuracy: CLLocationDirectionAccuracy - if let course = userLocation.courseOverGround { - courseDegrees = CLLocationDirection(course.degrees) - courseAccuracy = CLLocationDirectionAccuracy(course.accuracy) + let courseDegrees = if let degrees = userLocation.courseOverGround?.degrees { CLLocationDirection(degrees) } else { - courseDegrees = invalid - courseAccuracy = invalid + invalid + } + let courseAccuracy = if let accuracy = userLocation.courseOverGround?.accuracy { + CLLocationDirectionAccuracy(accuracy) + } else { + invalid } self.init( @@ -169,28 +169,22 @@ public extension UserLocation { } var clLocation: CLLocation { - let courseDegrees: CLLocationDirection - let courseAccuracy: CLLocationDirectionAccuracy - - if let course = courseOverGround { - courseDegrees = CLLocationDirection(course.degrees) - courseAccuracy = CLLocationDirectionAccuracy(course.accuracy) + let courseDegrees: CLLocationDirection = if let degrees = courseOverGround? + .degrees + { CLLocationDirection(degrees) } else { - courseDegrees = -1 - courseAccuracy = -1 + -1 } - let clSpeed: CLLocationDirection - let clSpeedAccuracy: CLLocationDirectionAccuracy - - if let speed { - clSpeed = speed.value - clSpeedAccuracy = speed.accuracy + let courseAccuracy: CLLocationDirectionAccuracy = if let accuracy = courseOverGround?.accuracy { + CLLocationDirectionAccuracy(accuracy) } else { - clSpeed = -1 - clSpeedAccuracy = -1 + -1 } + let clSpeed: CLLocationDirection = speed?.value ?? -1 + let clSpeedAccuracy: CLLocationDirectionAccuracy = speed?.accuracy ?? -1 + return CLLocation( coordinate: coordinates.clLocationCoordinate2D, altitude: 0, diff --git a/apple/Sources/UniFFI/ferrostar.swift b/apple/Sources/UniFFI/ferrostar.swift index 5a36e839..92fd5baf 100644 --- a/apple/Sources/UniFFI/ferrostar.swift +++ b/apple/Sources/UniFFI/ferrostar.swift @@ -1382,7 +1382,7 @@ public struct CourseOverGround { /** * The accuracy of the course value, measured in degrees. */ - public var accuracy: UInt16 + public var accuracy: UInt16? // Default memberwise initializers are never public by default, so we // declare one manually. @@ -1395,7 +1395,7 @@ public struct CourseOverGround { /** * The accuracy of the course value, measured in degrees. */ - accuracy: UInt16 + accuracy: UInt16? ) { self.degrees = degrees self.accuracy = accuracy @@ -1423,13 +1423,13 @@ public struct FfiConverterTypeCourseOverGround: FfiConverterRustBuffer { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CourseOverGround { try CourseOverGround( degrees: FfiConverterUInt16.read(from: &buf), - accuracy: FfiConverterUInt16.read(from: &buf) + accuracy: FfiConverterOptionUInt16.read(from: &buf) ) } public static func write(_ value: CourseOverGround, into buf: inout [UInt8]) { FfiConverterUInt16.write(value.degrees, into: &buf) - FfiConverterUInt16.write(value.accuracy, into: &buf) + FfiConverterOptionUInt16.write(value.accuracy, into: &buf) } } @@ -1908,7 +1908,7 @@ public struct Speed { /** * The accuracy of the speed value, measured in meters per second. */ - public var accuracy: Double + public var accuracy: Double? // Default memberwise initializers are never public by default, so we // declare one manually. @@ -1920,7 +1920,7 @@ public struct Speed { /** * The accuracy of the speed value, measured in meters per second. */ - accuracy: Double + accuracy: Double? ) { self.value = value self.accuracy = accuracy @@ -1948,13 +1948,13 @@ public struct FfiConverterTypeSpeed: FfiConverterRustBuffer { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Speed { try Speed( value: FfiConverterDouble.read(from: &buf), - accuracy: FfiConverterDouble.read(from: &buf) + accuracy: FfiConverterOptionDouble.read(from: &buf) ) } public static func write(_ value: Speed, into buf: inout [UInt8]) { FfiConverterDouble.write(value.value, into: &buf) - FfiConverterDouble.write(value.accuracy, into: &buf) + FfiConverterOptionDouble.write(value.accuracy, into: &buf) } } diff --git a/common/Cargo.lock b/common/Cargo.lock index 8c8c72c6..49a4cadb 100644 --- a/common/Cargo.lock +++ b/common/Cargo.lock @@ -336,7 +336,7 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "ferrostar" -version = "0.0.32" +version = "0.0.33" dependencies = [ "assert-json-diff", "geo", diff --git a/common/ferrostar/Cargo.toml b/common/ferrostar/Cargo.toml index 68d32820..d0b7f33f 100644 --- a/common/ferrostar/Cargo.toml +++ b/common/ferrostar/Cargo.toml @@ -2,7 +2,7 @@ lints.workspace = true [package] name = "ferrostar" -version = "0.0.32" +version = "0.0.33" readme = "README.md" description = "The core of modern turn-by-turn navigation." keywords = ["navigation", "routing", "valhalla", "osrm"] diff --git a/common/ferrostar/src/models.rs b/common/ferrostar/src/models.rs index 6f81532d..423c938b 100644 --- a/common/ferrostar/src/models.rs +++ b/common/ferrostar/src/models.rs @@ -121,11 +121,11 @@ pub struct CourseOverGround { /// true north (N = 0, E = 90, S = 180, W = 270). pub degrees: u16, /// The accuracy of the course value, measured in degrees. - pub accuracy: u16, + pub accuracy: Option, } impl CourseOverGround { - pub fn new(degrees: u16, accuracy: u16) -> Self { + pub fn new(degrees: u16, accuracy: Option) -> Self { Self { degrees, accuracy } } } @@ -137,7 +137,7 @@ pub struct Speed { /// The user's speed in meters per second. pub value: f64, /// The accuracy of the speed value, measured in meters per second. - pub accuracy: f64, + pub accuracy: Option, } /// The location of the user that is navigating. diff --git a/common/ferrostar/src/routing_adapters/valhalla.rs b/common/ferrostar/src/routing_adapters/valhalla.rs index a7aa7ab1..1fc1d667 100644 --- a/common/ferrostar/src/routing_adapters/valhalla.rs +++ b/common/ferrostar/src/routing_adapters/valhalla.rs @@ -137,7 +137,7 @@ mod tests { horizontal_accuracy: 6.0, course_over_ground: Some(CourseOverGround { degrees: 42, - accuracy: 12, + accuracy: Some(12), }), timestamp: SystemTime::UNIX_EPOCH, speed: None, diff --git a/common/ferrostar/src/simulation.rs b/common/ferrostar/src/simulation.rs index 15586dbc..5bcb58f8 100644 --- a/common/ferrostar/src/simulation.rs +++ b/common/ferrostar/src/simulation.rs @@ -40,7 +40,7 @@ pub fn location_simulation_from_coordinates( horizontal_accuracy: 0.0, course_over_ground: Some(CourseOverGround { degrees: bearing.round() as u16, - accuracy: 0, + accuracy: None, }), timestamp: SystemTime::now(), speed: None, @@ -142,7 +142,7 @@ pub fn advance_location_simulation(state: &LocationSimulationState) -> LocationS horizontal_accuracy: 0.0, course_over_ground: Some(CourseOverGround { degrees: bearing.round() as u16, - accuracy: 0, + accuracy: None, }), timestamp: SystemTime::now(), speed: None, diff --git a/common/ferrostar/src/snapshots/ferrostar__simulation__tests__None.snap b/common/ferrostar/src/snapshots/ferrostar__simulation__tests__None.snap index 5d54869d..77f4cfcd 100644 --- a/common/ferrostar/src/snapshots/ferrostar__simulation__tests__None.snap +++ b/common/ferrostar/src/snapshots/ferrostar__simulation__tests__None.snap @@ -9,7 +9,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 45 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 0.0001 @@ -25,7 +25,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 45 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 0.0002 @@ -39,7 +39,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 45 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 0.0003 @@ -51,6 +51,6 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 45 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: [] diff --git a/common/ferrostar/src/snapshots/ferrostar__simulation__tests__Some(10.0).snap b/common/ferrostar/src/snapshots/ferrostar__simulation__tests__Some(10.0).snap index 9615cbaf..78579375 100644 --- a/common/ferrostar/src/snapshots/ferrostar__simulation__tests__Some(10.0).snap +++ b/common/ferrostar/src/snapshots/ferrostar__simulation__tests__Some(10.0).snap @@ -9,7 +9,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 45 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 0.0001 @@ -29,7 +29,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 45 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 0.00015 @@ -47,7 +47,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 45 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 0.0002 @@ -63,7 +63,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 45 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 0.00025 @@ -77,7 +77,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 45 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 0.0003 @@ -89,6 +89,6 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 45 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: [] diff --git a/common/ferrostar/src/snapshots/ferrostar__simulation__tests__extended_interpolation_simulation.snap b/common/ferrostar/src/snapshots/ferrostar__simulation__tests__extended_interpolation_simulation.snap index ce7f4e09..9af750be 100644 --- a/common/ferrostar/src/snapshots/ferrostar__simulation__tests__extended_interpolation_simulation.snap +++ b/common/ferrostar/src/snapshots/ferrostar__simulation__tests__extended_interpolation_simulation.snap @@ -9,7 +9,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.332715 @@ -879,7 +879,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.332695 @@ -1747,7 +1747,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.332616 @@ -2613,7 +2613,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 181 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.332616 @@ -3477,7 +3477,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.332616 @@ -4339,7 +4339,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.332616 @@ -5199,7 +5199,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.332616 @@ -6057,7 +6057,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.332615 @@ -6913,7 +6913,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 91 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.332613 @@ -7767,7 +7767,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 92 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.332612 @@ -8619,7 +8619,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 91 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.332528 @@ -9469,7 +9469,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 181 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.332445 @@ -10317,7 +10317,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 181 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.332361 @@ -11163,7 +11163,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 181 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.332277 @@ -12007,7 +12007,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 181 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.332193 @@ -12849,7 +12849,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.33211 @@ -13689,7 +13689,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 181 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.332026 @@ -14527,7 +14527,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 181 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331942 @@ -15363,7 +15363,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 181 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331911 @@ -16197,7 +16197,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331884 @@ -17029,7 +17029,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 126 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331857 @@ -17859,7 +17859,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 127 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331787 @@ -18687,7 +18687,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331764 @@ -19513,7 +19513,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 137 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331748 @@ -20337,7 +20337,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 225 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331692 @@ -21159,7 +21159,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331645 @@ -21979,7 +21979,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 182 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331626 @@ -22797,7 +22797,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 132 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.3316 @@ -23613,7 +23613,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 135 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331601 @@ -24427,7 +24427,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331599 @@ -25239,7 +25239,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 92 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331598 @@ -26049,7 +26049,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 92 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331559 @@ -26857,7 +26857,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 186 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331506 @@ -27663,7 +27663,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 176 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331453 @@ -28467,7 +28467,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 177 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.33139 @@ -29269,7 +29269,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 181 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331326 @@ -30069,7 +30069,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331253 @@ -30867,7 +30867,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 185 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331212 @@ -31663,7 +31663,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331171 @@ -32457,7 +32457,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331119 @@ -33249,7 +33249,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.331039 @@ -34039,7 +34039,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 159 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330957 @@ -34827,7 +34827,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 165 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330875 @@ -35613,7 +35613,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 165 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330792 @@ -36397,7 +36397,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 165 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.33071 @@ -37179,7 +37179,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 165 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330628 @@ -37959,7 +37959,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 165 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330618 @@ -38737,7 +38737,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 226 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330531 @@ -39513,7 +39513,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 166 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330457 @@ -40287,7 +40287,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 165 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330403 @@ -41059,7 +41059,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 165 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330413 @@ -41829,7 +41829,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 74 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330419 @@ -42597,7 +42597,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 85 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330406 @@ -43363,7 +43363,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 103 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330393 @@ -44127,7 +44127,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 103 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330386 @@ -44889,7 +44889,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 101 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.33038 @@ -45649,7 +45649,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 99 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330383 @@ -46407,7 +46407,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 82 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330398 @@ -47163,7 +47163,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 72 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330424 @@ -47917,7 +47917,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 72 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.33044 @@ -48669,7 +48669,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 75 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.33044 @@ -49419,7 +49419,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330438 @@ -50167,7 +50167,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 93 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330426 @@ -50913,7 +50913,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 106 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330412 @@ -51657,7 +51657,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 106 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330405 @@ -52399,7 +52399,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 103 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330401 @@ -53139,7 +53139,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 96 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330405 @@ -53877,7 +53877,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 86 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330413 @@ -54613,7 +54613,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 80 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330413 @@ -55347,7 +55347,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330397 @@ -56079,7 +56079,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 114 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330359 @@ -56809,7 +56809,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 135 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330331 @@ -57537,7 +57537,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 125 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330325 @@ -58263,7 +58263,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 101 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330326 @@ -58987,7 +58987,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 88 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.33034 @@ -59709,7 +59709,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 73 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330274 @@ -60429,7 +60429,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 172 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330237 @@ -61147,7 +61147,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 176 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330242 @@ -61863,7 +61863,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 85 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330207 @@ -62577,7 +62577,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 124 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330188 @@ -63289,7 +63289,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 128 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330188 @@ -63999,7 +63999,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330187 @@ -64707,7 +64707,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 91 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330187 @@ -65413,7 +65413,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330186 @@ -66117,7 +66117,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 91 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330186 @@ -66819,7 +66819,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330185 @@ -67519,7 +67519,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 91 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330185 @@ -68217,7 +68217,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330184 @@ -68913,7 +68913,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 91 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330184 @@ -69607,7 +69607,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330184 @@ -70299,7 +70299,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330184 @@ -70989,7 +70989,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330183 @@ -71677,7 +71677,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 91 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330183 @@ -72363,7 +72363,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330183 @@ -73047,7 +73047,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330183 @@ -73729,7 +73729,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330183 @@ -74409,7 +74409,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330183 @@ -75087,7 +75087,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330183 @@ -75763,7 +75763,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330183 @@ -76437,7 +76437,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330183 @@ -77109,7 +77109,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330175 @@ -77779,7 +77779,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 109 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330163 @@ -78447,7 +78447,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 124 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330173 @@ -79113,7 +79113,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 58 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330175 @@ -79777,7 +79777,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330177 @@ -80439,7 +80439,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 88 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330166 @@ -81099,7 +81099,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 135 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330176 @@ -81757,7 +81757,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 52 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330184 @@ -82413,7 +82413,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 70 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330184 @@ -83067,7 +83067,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330184 @@ -83719,7 +83719,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330183 @@ -84369,7 +84369,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 91 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330183 @@ -85017,7 +85017,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330183 @@ -85663,7 +85663,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330183 @@ -86307,7 +86307,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330183 @@ -86949,7 +86949,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330182 @@ -87589,7 +87589,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 91 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330182 @@ -88227,7 +88227,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330182 @@ -88863,7 +88863,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330182 @@ -89497,7 +89497,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330182 @@ -90129,7 +90129,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330182 @@ -90759,7 +90759,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330181 @@ -91387,7 +91387,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 91 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330181 @@ -92013,7 +92013,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330181 @@ -92637,7 +92637,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330181 @@ -93259,7 +93259,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330181 @@ -93879,7 +93879,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.33018 @@ -94497,7 +94497,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 91 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.33018 @@ -95113,7 +95113,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.33018 @@ -95727,7 +95727,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330167 @@ -96339,7 +96339,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 112 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330155 @@ -96949,7 +96949,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 128 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330171 @@ -97557,7 +97557,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 43 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330171 @@ -98163,7 +98163,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330171 @@ -98767,7 +98767,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330163 @@ -99369,7 +99369,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 114 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330171 @@ -99969,7 +99969,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 63 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330179 @@ -100567,7 +100567,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 66 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330179 @@ -101163,7 +101163,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.33018 @@ -101757,7 +101757,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.33018 @@ -102349,7 +102349,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330181 @@ -102939,7 +102939,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330181 @@ -103527,7 +103527,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330173 @@ -104113,7 +104113,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 102 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330163 @@ -104697,7 +104697,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 115 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.33018 @@ -105279,7 +105279,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 21 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330177 @@ -105859,7 +105859,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 94 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330174 @@ -106437,7 +106437,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 94 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.33017 @@ -107013,7 +107013,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 93 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330158 @@ -107587,7 +107587,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 137 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330175 @@ -108159,7 +108159,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 61 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330177 @@ -108729,7 +108729,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330178 @@ -109297,7 +109297,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.33018 @@ -109863,7 +109863,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330177 @@ -110427,7 +110427,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 94 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330173 @@ -110989,7 +110989,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 95 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330157 @@ -111549,7 +111549,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 104 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330142 @@ -112107,7 +112107,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 103 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330126 @@ -112663,7 +112663,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 104 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330108 @@ -113217,7 +113217,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 127 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330119 @@ -113769,7 +113769,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 49 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330113 @@ -114319,7 +114319,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 94 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330105 @@ -114867,7 +114867,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 95 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330094 @@ -115413,7 +115413,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 139 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330108 @@ -115957,7 +115957,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 63 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330103 @@ -116499,7 +116499,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 93 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330098 @@ -117039,7 +117039,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 93 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330093 @@ -117577,7 +117577,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 93 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330088 @@ -118113,7 +118113,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 93 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330083 @@ -118647,7 +118647,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 93 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330078 @@ -119179,7 +119179,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 93 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330074 @@ -119709,7 +119709,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 93 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330069 @@ -120237,7 +120237,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 93 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330064 @@ -120763,7 +120763,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 93 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330059 @@ -121287,7 +121287,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 93 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330055 @@ -121809,7 +121809,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 94 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.33005 @@ -122329,7 +122329,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 95 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330035 @@ -122847,7 +122847,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 100 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330019 @@ -123363,7 +123363,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 109 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.330002 @@ -123877,7 +123877,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 110 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.329965 @@ -124389,7 +124389,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 115 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.329928 @@ -124899,7 +124899,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 115 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.329909 @@ -125407,7 +125407,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 109 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.329889 @@ -125913,7 +125913,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 110 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.329868 @@ -126417,7 +126417,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 106 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.329847 @@ -126919,7 +126919,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 105 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.329825 @@ -127419,7 +127419,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 106 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.329804 @@ -127917,7 +127917,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 105 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.329783 @@ -128413,7 +128413,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 106 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.329759 @@ -128907,7 +128907,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 122 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.329728 @@ -129399,7 +129399,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 160 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.329639 @@ -129889,7 +129889,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32955 @@ -130377,7 +130377,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.329461 @@ -130863,7 +130863,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.329371 @@ -131347,7 +131347,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.329282 @@ -131829,7 +131829,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.329193 @@ -132309,7 +132309,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.329104 @@ -132787,7 +132787,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.329015 @@ -133263,7 +133263,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.328926 @@ -133737,7 +133737,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.328837 @@ -134209,7 +134209,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.328747 @@ -134679,7 +134679,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.328658 @@ -135147,7 +135147,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.328569 @@ -135613,7 +135613,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32848 @@ -136077,7 +136077,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.328391 @@ -136539,7 +136539,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.328302 @@ -136999,7 +136999,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.328213 @@ -137457,7 +137457,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.328123 @@ -137913,7 +137913,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.328034 @@ -138367,7 +138367,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.327945 @@ -138819,7 +138819,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.327856 @@ -139269,7 +139269,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.327815 @@ -139717,7 +139717,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 161 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.327768 @@ -140163,7 +140163,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.327681 @@ -140607,7 +140607,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.327594 @@ -141049,7 +141049,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.327507 @@ -141489,7 +141489,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32742 @@ -141927,7 +141927,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.327333 @@ -142363,7 +142363,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.327246 @@ -142797,7 +142797,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.327158 @@ -143229,7 +143229,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.327071 @@ -143659,7 +143659,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.326984 @@ -144087,7 +144087,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.326897 @@ -144513,7 +144513,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32681 @@ -144937,7 +144937,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.326723 @@ -145359,7 +145359,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.326656 @@ -145779,7 +145779,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.326589 @@ -146197,7 +146197,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.326521 @@ -146613,7 +146613,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.326453 @@ -147027,7 +147027,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32637 @@ -147439,7 +147439,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.326286 @@ -147849,7 +147849,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.326203 @@ -148257,7 +148257,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32612 @@ -148663,7 +148663,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.326037 @@ -149067,7 +149067,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.325954 @@ -149469,7 +149469,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32587 @@ -149869,7 +149869,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.325787 @@ -150267,7 +150267,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.325702 @@ -150663,7 +150663,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.325617 @@ -151057,7 +151057,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.325532 @@ -151449,7 +151449,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.325448 @@ -151839,7 +151839,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.325363 @@ -152227,7 +152227,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.325278 @@ -152613,7 +152613,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.325193 @@ -152997,7 +152997,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.325108 @@ -153379,7 +153379,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.325023 @@ -153759,7 +153759,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.324938 @@ -154137,7 +154137,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.324854 @@ -154513,7 +154513,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.324769 @@ -154887,7 +154887,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.324684 @@ -155259,7 +155259,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.324599 @@ -155629,7 +155629,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.324561 @@ -155997,7 +155997,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 211 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32455 @@ -156363,7 +156363,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 139 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.324469 @@ -156727,7 +156727,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 175 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32439 @@ -157089,7 +157089,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 177 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.324375 @@ -157449,7 +157449,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 100 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.324361 @@ -157807,7 +157807,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.324286 @@ -158163,7 +158163,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 181 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.324212 @@ -158517,7 +158517,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.324137 @@ -158869,7 +158869,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 181 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.324075 @@ -159219,7 +159219,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323987 @@ -159567,7 +159567,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323899 @@ -159913,7 +159913,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323812 @@ -160257,7 +160257,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323724 @@ -160599,7 +160599,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323636 @@ -160939,7 +160939,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323548 @@ -161277,7 +161277,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323505 @@ -161613,7 +161613,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323434 @@ -161947,7 +161947,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323362 @@ -162279,7 +162279,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323291 @@ -162609,7 +162609,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323219 @@ -162937,7 +162937,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323153 @@ -163263,7 +163263,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 178 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323086 @@ -163587,7 +163587,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 178 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -163909,7 +163909,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 178 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -164229,7 +164229,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -164547,7 +164547,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -164863,7 +164863,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -165177,7 +165177,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -165489,7 +165489,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -165799,7 +165799,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -166107,7 +166107,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -166413,7 +166413,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -166717,7 +166717,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -167019,7 +167019,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -167319,7 +167319,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -167617,7 +167617,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -167913,7 +167913,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -168207,7 +168207,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -168499,7 +168499,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -168789,7 +168789,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323019 @@ -169077,7 +169077,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 91 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323019 @@ -169363,7 +169363,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323019 @@ -169647,7 +169647,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323019 @@ -169929,7 +169929,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323019 @@ -170209,7 +170209,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323019 @@ -170487,7 +170487,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323019 @@ -170763,7 +170763,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323019 @@ -171037,7 +171037,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323018 @@ -171309,7 +171309,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 91 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323019 @@ -171579,7 +171579,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -171847,7 +171847,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32302 @@ -172113,7 +172113,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323021 @@ -172377,7 +172377,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323022 @@ -172639,7 +172639,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323023 @@ -172899,7 +172899,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323024 @@ -173157,7 +173157,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323025 @@ -173413,7 +173413,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323026 @@ -173667,7 +173667,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323026 @@ -173919,7 +173919,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323027 @@ -174169,7 +174169,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323027 @@ -174417,7 +174417,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323028 @@ -174663,7 +174663,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323028 @@ -174907,7 +174907,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323029 @@ -175149,7 +175149,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323029 @@ -175389,7 +175389,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32303 @@ -175627,7 +175627,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323031 @@ -175863,7 +175863,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323031 @@ -176097,7 +176097,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323032 @@ -176329,7 +176329,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323032 @@ -176559,7 +176559,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323033 @@ -176787,7 +176787,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323033 @@ -177013,7 +177013,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323034 @@ -177237,7 +177237,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323035 @@ -177459,7 +177459,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323035 @@ -177679,7 +177679,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323036 @@ -177897,7 +177897,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323037 @@ -178113,7 +178113,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323037 @@ -178327,7 +178327,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323037 @@ -178539,7 +178539,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323037 @@ -178749,7 +178749,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323037 @@ -178957,7 +178957,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323037 @@ -179163,7 +179163,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323037 @@ -179367,7 +179367,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323037 @@ -179569,7 +179569,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323037 @@ -179769,7 +179769,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323037 @@ -179967,7 +179967,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323038 @@ -180163,7 +180163,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323038 @@ -180357,7 +180357,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323038 @@ -180549,7 +180549,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323038 @@ -180739,7 +180739,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323038 @@ -180927,7 +180927,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323037 @@ -181113,7 +181113,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 91 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323037 @@ -181297,7 +181297,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323037 @@ -181479,7 +181479,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323037 @@ -181659,7 +181659,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323036 @@ -181837,7 +181837,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 91 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323036 @@ -182013,7 +182013,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323036 @@ -182187,7 +182187,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323036 @@ -182359,7 +182359,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323036 @@ -182529,7 +182529,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323037 @@ -182697,7 +182697,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323037 @@ -182863,7 +182863,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323037 @@ -183027,7 +183027,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323037 @@ -183189,7 +183189,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323038 @@ -183349,7 +183349,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323038 @@ -183507,7 +183507,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323039 @@ -183663,7 +183663,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32304 @@ -183817,7 +183817,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323041 @@ -183969,7 +183969,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323041 @@ -184119,7 +184119,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323042 @@ -184267,7 +184267,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323042 @@ -184413,7 +184413,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323042 @@ -184557,7 +184557,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323043 @@ -184699,7 +184699,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 89 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323043 @@ -184839,7 +184839,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323043 @@ -184977,7 +184977,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323031 @@ -185113,7 +185113,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 106 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.323021 @@ -185247,7 +185247,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 115 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.322977 @@ -185379,7 +185379,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 138 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.322942 @@ -185509,7 +185509,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 138 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.322906 @@ -185637,7 +185637,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 139 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32286 @@ -185763,7 +185763,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.322813 @@ -185887,7 +185887,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.322803 @@ -186009,7 +186009,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.322782 @@ -186129,7 +186129,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 178 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.322708 @@ -186247,7 +186247,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 177 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.322633 @@ -186363,7 +186363,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 178 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.322563 @@ -186477,7 +186477,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.322493 @@ -186589,7 +186589,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.322404 @@ -186699,7 +186699,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.322341 @@ -186807,7 +186807,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.322267 @@ -186913,7 +186913,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.322192 @@ -187017,7 +187017,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.322111 @@ -187119,7 +187119,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32204 @@ -187219,7 +187219,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.321968 @@ -187317,7 +187317,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.321905 @@ -187413,7 +187413,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 181 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.321842 @@ -187507,7 +187507,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 181 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.321765 @@ -187599,7 +187599,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.321688 @@ -187689,7 +187689,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 181 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.321611 @@ -187777,7 +187777,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.321529 @@ -187863,7 +187863,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.321447 @@ -187947,7 +187947,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.321365 @@ -188029,7 +188029,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.321301 @@ -188109,7 +188109,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.321238 @@ -188187,7 +188187,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.321174 @@ -188263,7 +188263,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 180 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32114 @@ -188337,7 +188337,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 128 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.321097 @@ -188409,7 +188409,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 178 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.321053 @@ -188479,7 +188479,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.320979 @@ -188547,7 +188547,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.320896 @@ -188613,7 +188613,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 182 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.320812 @@ -188677,7 +188677,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 182 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.320728 @@ -188739,7 +188739,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 198 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.32067 @@ -188799,7 +188799,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 178 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.320612 @@ -188857,7 +188857,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 178 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.320532 @@ -188913,7 +188913,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 178 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.320453 @@ -188967,7 +188967,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 178 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.320373 @@ -189019,7 +189019,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.320294 @@ -189069,7 +189069,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 178 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.320214 @@ -189117,7 +189117,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 178 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.320135 @@ -189163,7 +189163,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 178 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.320055 @@ -189207,7 +189207,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.319999 @@ -189249,7 +189249,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 178 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.319942 @@ -189289,7 +189289,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.319942 @@ -189327,7 +189327,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.319941 @@ -189363,7 +189363,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 91 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.319941 @@ -189397,7 +189397,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 90 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.319867 @@ -189429,7 +189429,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.319792 @@ -189459,7 +189459,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.319708 @@ -189487,7 +189487,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.319623 @@ -189513,7 +189513,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.319539 @@ -189537,7 +189537,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.319454 @@ -189559,7 +189559,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.31937 @@ -189579,7 +189579,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.319285 @@ -189597,7 +189597,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.319223 @@ -189613,7 +189613,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.319162 @@ -189627,7 +189627,7 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 37.3191 @@ -189639,6 +189639,6 @@ expression: states horizontal_accuracy: 0 course_over_ground: degrees: 179 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: [] diff --git a/common/ferrostar/src/snapshots/ferrostar__simulation__tests__state_from_polyline.snap b/common/ferrostar/src/snapshots/ferrostar__simulation__tests__state_from_polyline.snap index fde32dea..41bd94f3 100644 --- a/common/ferrostar/src/snapshots/ferrostar__simulation__tests__state_from_polyline.snap +++ b/common/ferrostar/src/snapshots/ferrostar__simulation__tests__state_from_polyline.snap @@ -9,7 +9,7 @@ current_location: horizontal_accuracy: 0 course_over_ground: degrees: 0 - accuracy: 0 + accuracy: ~ speed: ~ remaining_locations: - lat: 60.534782 diff --git a/guide/src/platform-support-targets.md b/guide/src/platform-support-targets.md index 3586471a..8ccd4d64 100644 --- a/guide/src/platform-support-targets.md +++ b/guide/src/platform-support-targets.md @@ -42,10 +42,14 @@ to set API level requirements going forward. API levels lower than 26 do not include support for several Java 8 APIs. Crucially, the `Instant` API, which is essential for the library, is not present. -If you cannot raise your minimum SDK to 26 or higher, -you may need to enable [Java 8+ API desugaring support](https://developer.android.com/studio/write/java8-support). - -Additionally, Android before API 30 has to fall back on older ICU APIs. +We work around this using [Java 8+ API desugaring support](https://developer.android.com/studio/write/java8-support). +As we are able to raise our support target, we will be able to remove this, +but for the interim, we need the compatibility shims / backports. +This requirement probably extends to your apps as well if you target API 25. + +Additionally, when running on Android API lower than 30, +we have to fall back on older ICU APIs. +This does not require any change to your app code; it’s an internal consideration. We recommend supporting the newest API version possible for your user base, as Google officially drops support for older releases after just a few years. \ No newline at end of file From 6c898199c3c41e01f07a9e58d88f696152111ceb Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Mon, 20 May 2024 17:09:55 +0900 Subject: [PATCH 02/12] Remove extraneous location managers --- Package.resolved | 4 ++-- .../Views/DynamicallyOrientingNavigationView.swift | 1 - .../FerrostarMapLibreUI/Views/PortraitNavigationView.swift | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Package.resolved b/Package.resolved index fc9adc52..7e0e4a89 100644 --- a/Package.resolved +++ b/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/maplibre/maplibre-gl-native-distribution.git", "state" : { - "revision" : "6579f48fe126ce2916f7b5d0c84c1869d790c4e4", - "version" : "6.4.1" + "revision" : "816a24eb56b7cd004e389f7da404047839d5dc2b", + "version" : "6.4.2" } }, { diff --git a/apple/Sources/FerrostarMapLibreUI/Views/DynamicallyOrientingNavigationView.swift b/apple/Sources/FerrostarMapLibreUI/Views/DynamicallyOrientingNavigationView.swift index 29c19be7..a5a66221 100644 --- a/apple/Sources/FerrostarMapLibreUI/Views/DynamicallyOrientingNavigationView.swift +++ b/apple/Sources/FerrostarMapLibreUI/Views/DynamicallyOrientingNavigationView.swift @@ -18,7 +18,6 @@ public struct DynamicallyOrientingNavigationView: View { private var navigationState: NavigationState? private let userLayers: [StyleLayerDefinition] - @State private var locationManager = StaticLocationManager(initialLocation: CLLocation()) @Binding var camera: MapViewCamera @Binding var snappedZoom: Double @Binding var useSnappedCamera: Bool diff --git a/apple/Sources/FerrostarMapLibreUI/Views/PortraitNavigationView.swift b/apple/Sources/FerrostarMapLibreUI/Views/PortraitNavigationView.swift index b867ddee..39cf5ea2 100644 --- a/apple/Sources/FerrostarMapLibreUI/Views/PortraitNavigationView.swift +++ b/apple/Sources/FerrostarMapLibreUI/Views/PortraitNavigationView.swift @@ -15,7 +15,6 @@ public struct PortraitNavigationView: View { private var navigationState: NavigationState? private let userLayers: [StyleLayerDefinition] - @State private var locationManager = StaticLocationManager(initialLocation: CLLocation()) @Binding var camera: MapViewCamera @Binding var snappedZoom: Double @Binding var useSnappedCamera: Bool From 320a0ed781be248e292a45d67fe4c0887016a582 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Mon, 20 May 2024 17:10:21 +0900 Subject: [PATCH 03/12] Fix issue with super mega puck lag on iOS --- .../Views/NavigationMapView.swift | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/apple/Sources/FerrostarMapLibreUI/Views/NavigationMapView.swift b/apple/Sources/FerrostarMapLibreUI/Views/NavigationMapView.swift index 47881f47..5aa430d5 100644 --- a/apple/Sources/FerrostarMapLibreUI/Views/NavigationMapView.swift +++ b/apple/Sources/FerrostarMapLibreUI/Views/NavigationMapView.swift @@ -93,17 +93,7 @@ public struct NavigationMapView: View { identifier: "remaining-route-polyline") } - if let snappedLocation = navigationState?.snappedLocation { - locationManager.lastLocation = snappedLocation.clLocation - - // TODO: Be less forceful about this. - DispatchQueue.main.async { - if useSnappedCamera { - camera = .trackUserLocationWithCourse(zoom: snappedZoom, - pitch: .fixed(45)) - } - } - } + updateCameraIfNeeded() // Overlay any additional user layers. userLayers @@ -132,6 +122,25 @@ public struct NavigationMapView: View { ) .ignoresSafeArea(.all) } + + private func updateCameraIfNeeded() { + if let snappedLocation = navigationState?.snappedLocation, + // There is no reason to push an update if the coordinate and heading are the same. + // That's all that gets displayed, so it's all that MapLibre should care about. + locationManager.lastLocation.coordinate != snappedLocation.coordinates + .clLocationCoordinate2D || locationManager.lastLocation.course != snappedLocation.clLocation.course + { + locationManager.lastLocation = snappedLocation.clLocation + + // TODO: Be less forceful about this. + DispatchQueue.main.async { + if useSnappedCamera { + camera = .trackUserLocationWithCourse(zoom: snappedZoom, + pitch: .fixed(45)) + } + } + } + } } #Preview("Navigation Map View") { From 2dbd4e6ad0d79d728073cb7303b622e280a37cc1 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Mon, 27 May 2024 12:17:42 +0300 Subject: [PATCH 04/12] Core library desugaring for demo app --- .../com/stadiamaps/ferrostar/core/FerrostarCore.kt | 11 ++++++++--- android/demo-app/build.gradle | 6 +++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/android/core/src/main/java/com/stadiamaps/ferrostar/core/FerrostarCore.kt b/android/core/src/main/java/com/stadiamaps/ferrostar/core/FerrostarCore.kt index cc44d14d..c3364a33 100644 --- a/android/core/src/main/java/com/stadiamaps/ferrostar/core/FerrostarCore.kt +++ b/android/core/src/main/java/com/stadiamaps/ferrostar/core/FerrostarCore.kt @@ -4,6 +4,7 @@ import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import com.squareup.moshi.adapter import java.net.URL +import java.time.Instant import java.util.concurrent.Executors import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -194,10 +195,13 @@ class FerrostarCore( route, config, ) - val startingLocation = locationProvider.lastLocation ?: throw UserLocationUnknown() + val startingLocation = + locationProvider.lastLocation + ?: UserLocation(route.geometry.first(), 0.0, null, Instant.now(), null) val initialTripState = controller.getInitialState(startingLocation) - val stateFlow = MutableStateFlow(FerrostarCoreState(tripState = initialTripState, false)) + val stateFlow = + MutableStateFlow(FerrostarCoreState(tripState = initialTripState, route.geometry, false)) handleStateUpdate(initialTripState, startingLocation) _navigationController = controller @@ -205,7 +209,8 @@ class FerrostarCore( locationProvider.addListener(this, _executor) - return NavigationViewModel(stateFlow, startingLocation, route.geometry) + return NavigationViewModel(stateFlow, startingLocation) + } } fun advanceToNextStep() { diff --git a/android/demo-app/build.gradle b/android/demo-app/build.gradle index f5239232..7d8452b9 100644 --- a/android/demo-app/build.gradle +++ b/android/demo-app/build.gradle @@ -28,6 +28,7 @@ android { } } compileOptions { + coreLibraryDesugaringEnabled true sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } @@ -48,6 +49,9 @@ android { } dependencies { + // Temporary until we can drop support for API < 26 + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' + implementation 'androidx.core:core-ktx:1.12.0' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0' implementation 'androidx.activity:activity-compose:1.8.2' @@ -63,7 +67,7 @@ dependencies { implementation project(':core') implementation project(':maplibreui') - implementation 'io.github.rallista:maplibre-compose:0.0.5' + implementation 'io.github.rallista:maplibre-compose:0.0.7' implementation platform("com.squareup.okhttp3:okhttp-bom:4.10.0") implementation 'com.squareup.okhttp3:okhttp' From c0eef63316fdbba6864f021e23d895cf52c32480 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Wed, 29 May 2024 11:24:30 +0200 Subject: [PATCH 05/12] First pass at Android location provider --- android/composeui/build.gradle | 4 + android/core/build.gradle | 2 +- .../ferrostar/core/FerrostarCore.kt | 49 +++++- .../com/stadiamaps/ferrostar/core/Location.kt | 145 ++++++++++++++++-- .../ferrostar/core/NavigationViewModel.kt | 3 +- .../com/stadiamaps/ferrostar/MainActivity.kt | 3 +- android/maplibreui/build.gradle | 4 + 7 files changed, 193 insertions(+), 17 deletions(-) diff --git a/android/composeui/build.gradle b/android/composeui/build.gradle index 771090b7..52a46566 100644 --- a/android/composeui/build.gradle +++ b/android/composeui/build.gradle @@ -22,6 +22,7 @@ android { } } compileOptions { + coreLibraryDesugaringEnabled true sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } @@ -43,6 +44,9 @@ android { } dependencies { + // For as long as we support API 25; once we can raise support to 26, all is fine + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' + implementation 'androidx.core:core-ktx:1.12.0' implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0') implementation 'androidx.appcompat:appcompat:1.6.1' diff --git a/android/core/build.gradle b/android/core/build.gradle index ac190ae9..7352da5e 100644 --- a/android/core/build.gradle +++ b/android/core/build.gradle @@ -92,7 +92,7 @@ android.libraryVariants.all { variant -> } def sourceSet = variant.sourceSets.find { it.name == variant.name } - sourceSet.java.srcDir new File(buildDir, "generated/source/uniffi/${variant.name}/java") + sourceSet.java.srcDir layout.buildDirectory.file("generated/source/uniffi/${variant.name}/java") // UniFFI tutorial notes that they made several attempts like this but were unsuccessful coming // to a good solution for forcing the directory to be marked as generated (short of checking in diff --git a/android/core/src/main/java/com/stadiamaps/ferrostar/core/FerrostarCore.kt b/android/core/src/main/java/com/stadiamaps/ferrostar/core/FerrostarCore.kt index c3364a33..7646921e 100644 --- a/android/core/src/main/java/com/stadiamaps/ferrostar/core/FerrostarCore.kt +++ b/android/core/src/main/java/com/stadiamaps/ferrostar/core/FerrostarCore.kt @@ -14,6 +14,7 @@ import kotlinx.coroutines.launch import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody +import uniffi.ferrostar.GeographicCoordinate import uniffi.ferrostar.Heading import uniffi.ferrostar.NavigationController import uniffi.ferrostar.NavigationControllerConfig @@ -28,6 +29,7 @@ import uniffi.ferrostar.Waypoint data class FerrostarCoreState( /** The raw trip state from the core. */ val tripState: TripState, + val routeGeometry: List, /** Indicates when the core is calculating a new route (ex: due to the user being off route). */ val isCalculatingNewRoute: Boolean ) @@ -53,6 +55,10 @@ class FerrostarCore( val httpClient: OkHttpClient, val locationProvider: LocationProvider, ) : LocationUpdateListener { + companion object { + private const val TAG = "FerrostarCore" + } + /** * The minimum time to wait before initiating another route recalculation. * @@ -100,7 +106,7 @@ class FerrostarCore( var isCalculatingNewRoute: Boolean = false private set - private val _executor = Executors.newSingleThreadExecutor() + private val _executor = Executors.newSingleThreadScheduledExecutor() private val _scope = CoroutineScope(Dispatchers.IO) private var _navigationController: NavigationController? = null private var _state: MutableStateFlow? = null @@ -185,6 +191,10 @@ class FerrostarCore( * subscribe to location provider updates. Returns a view model which is tied to the navigation * session. You can observe this in either your own or one of the provided navigation compose * views. + * + * WARNING: If you want to reuse the existing view model, ex: when getting a new route after going + * off course, use [replaceRoute] instead! Otherwise, you will miss out on updates as the old view + * model is "orphaned"! */ @Throws(UserLocationUnknown::class) fun startNavigation(route: Route, config: NavigationControllerConfig): NavigationViewModel { @@ -211,6 +221,34 @@ class FerrostarCore( return NavigationViewModel(stateFlow, startingLocation) } + + /** + * Replace the currently running route with a new one. + * + * This allows you to reuse the existing view model. Do not call this method unless you are + * already navigating. + */ + fun replaceRoute(route: Route, config: NavigationControllerConfig) { + val controller = + NavigationController( + route, + config, + ) + val startingLocation = + locationProvider.lastLocation + ?: UserLocation(route.geometry.first(), 0.0, null, Instant.now(), null) + + _navigationController = controller + if (_state == null) { + android.util.Log.e(TAG, "Unexpected null state") + } + _state?.update { + val newState = controller.getInitialState(startingLocation) + + handleStateUpdate(newState, startingLocation) + + FerrostarCoreState(tripState = newState, route.geometry, false) + } } fun advanceToNextStep() { @@ -223,7 +261,7 @@ class FerrostarCore( handleStateUpdate(newState, location) - FerrostarCoreState(tripState = newState, isCalculatingNewRoute) + FerrostarCoreState(tripState = newState, currentValue.routeGeometry, isCalculatingNewRoute) } } } @@ -275,9 +313,11 @@ class FerrostarCore( // Default behavior when there is no user-defined behavior: // accept the first route, as this is what most users want when they go off // route. - startNavigation(routes.first(), config) + replaceRoute(routes.first(), config) } } + } catch (e: Throwable) { + android.util.Log.e(TAG, "Failed to recalculate route: $e") } finally { _lastAutomaticRecalculation = System.nanoTime() isCalculatingNewRoute = false @@ -301,6 +341,7 @@ class FerrostarCore( _lastLocation = location val controller = _navigationController + android.util.Log.i(TAG, "Location Updated: $location") if (controller != null) { _state?.update { currentValue -> val newState = @@ -308,7 +349,7 @@ class FerrostarCore( handleStateUpdate(newState, location) - FerrostarCoreState(tripState = newState, isCalculatingNewRoute) + FerrostarCoreState(tripState = newState, currentValue.routeGeometry, isCalculatingNewRoute) } } } diff --git a/android/core/src/main/java/com/stadiamaps/ferrostar/core/Location.kt b/android/core/src/main/java/com/stadiamaps/ferrostar/core/Location.kt index c0eb97bc..715353d0 100644 --- a/android/core/src/main/java/com/stadiamaps/ferrostar/core/Location.kt +++ b/android/core/src/main/java/com/stadiamaps/ferrostar/core/Location.kt @@ -1,7 +1,14 @@ package com.stadiamaps.ferrostar.core +import android.annotation.SuppressLint +import android.content.Context +import android.location.LocationListener +import android.location.LocationManager import android.os.Build +import android.os.Handler +import android.os.Looper import android.os.SystemClock +import java.time.Instant import java.util.concurrent.Executor import kotlin.time.DurationUnit import kotlin.time.toDuration @@ -10,9 +17,12 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import uniffi.ferrostar.CourseOverGround +import uniffi.ferrostar.GeographicCoordinate import uniffi.ferrostar.Heading import uniffi.ferrostar.LocationSimulationState import uniffi.ferrostar.Route +import uniffi.ferrostar.Speed import uniffi.ferrostar.UserLocation import uniffi.ferrostar.advanceLocationSimulation import uniffi.ferrostar.locationSimulationFromRoute @@ -33,6 +43,86 @@ interface LocationUpdateListener { fun onHeadingUpdated(heading: Heading) } +/** + * A location provider that uses the Android system location services. + * + * NOTE: This does NOT attempt to check permissions. The caller is responsible for ensuring that + * permissions are granted. + */ +class AndroidSystemLocationProvider(context: Context) : LocationProvider { + companion object { + private const val TAG = "AndroidLocationProvider" + } + + override var lastLocation: UserLocation? = null + private set + + override var lastHeading: Heading? = null + private set + + val locationManager: LocationManager = + context.getSystemService(Context.LOCATION_SERVICE) as LocationManager + private val listeners: MutableMap = mutableMapOf() + + /** + * Adds a location update listener. + * + * NOTE: This does NOT attempt to check permissions. The caller is responsible for ensuring that + * permissions are enabled before calling this. + */ + // TODO: This SuppressLint feels wrong; can't we push this "taint" up? + @SuppressLint("MissingPermission") + override fun addListener(listener: LocationUpdateListener, executor: Executor) { + android.util.Log.d(TAG, "Add location listener") + if (listeners.contains(listener)) { + android.util.Log.d(TAG, "Already registered; skipping") + return + } + val androidListener = LocationListener { + android.util.Log.i("LocationListener", "Location Updated: $it") + listener.onLocationUpdated(it.toUserLocation()) + } + listeners[listener] = androidListener + + val handler = Handler(Looper.getMainLooper()) + + executor.execute { + handler.post { + locationManager.requestLocationUpdates(getBestProvider(), 100L, 5.0f, androidListener) + } + } + } + + override fun removeListener(listener: LocationUpdateListener) { + android.util.Log.d(TAG, "Remove location listener") + val androidListener = listeners.remove(listener) + + if (androidListener != null) { + locationManager.removeUpdates(androidListener) + } + } + + private fun getBestProvider(): String { + val providers = locationManager.getProviders(true).toSet() + // Oh, how we love Android... Fused provider is brand new, + // and we can't express this any other way than with duplicate clauses. + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + when { + providers.contains(LocationManager.FUSED_PROVIDER) -> LocationManager.FUSED_PROVIDER + providers.contains(LocationManager.GPS_PROVIDER) -> LocationManager.GPS_PROVIDER + providers.contains(LocationManager.NETWORK_PROVIDER) -> LocationManager.NETWORK_PROVIDER + else -> LocationManager.PASSIVE_PROVIDER + } + } else { + when { + providers.contains(LocationManager.GPS_PROVIDER) -> LocationManager.GPS_PROVIDER + providers.contains(LocationManager.NETWORK_PROVIDER) -> LocationManager.NETWORK_PROVIDER + else -> LocationManager.PASSIVE_PROVIDER + } + } + } +} + /** * Location provider for testing without relying on simulator location spoofing. * @@ -131,19 +221,14 @@ fun UserLocation.toAndroidLocation(): android.location.Location { if (course != null) { location.bearing = course.degrees.toFloat() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val accuracy = course.accuracy + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && accuracy != null) { // NOTE: Course accuracy information is not available until API 26 - location.bearingAccuracyDegrees = course.accuracy.toFloat() + location.bearingAccuracyDegrees = accuracy.toFloat() } } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - // NOTE: Instant.toEpochMilli() is not available until API 26, - location.time = this.timestamp.toEpochMilli() - } else { - // TODO: Figure out a better approach for this (if we want to support it at all) - location.time = System.currentTimeMillis() - } + location.time = this.timestamp.toEpochMilli() // FIXME: This is not entirely correct, but might be an acceptable approximation. // Feedback welcome as the purpose is not really documented. @@ -151,3 +236,45 @@ fun UserLocation.toAndroidLocation(): android.location.Location { return location } + +fun android.location.Location.toUserLocation(): UserLocation { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + UserLocation( + GeographicCoordinate(latitude, longitude), + if (hasAccuracy()) { + accuracy.toDouble() + } else { + Double.MAX_VALUE + }, + if (hasBearing() && hasBearingAccuracy()) { + CourseOverGround(bearing.toUInt().toUShort(), bearingAccuracyDegrees.toUInt().toUShort()) + } else { + null + }, + Instant.ofEpochMilli(time), + if (hasSpeed() && hasSpeedAccuracy()) { + Speed(speed.toDouble(), speedAccuracyMetersPerSecond.toDouble()) + } else { + null + }) + } else { + UserLocation( + GeographicCoordinate(latitude, longitude), + if (hasAccuracy()) { + accuracy.toDouble() + } else { + Double.MAX_VALUE + }, + if (hasBearing()) { + CourseOverGround(bearing.toUInt().toUShort(), null) + } else { + null + }, + Instant.ofEpochMilli(time), + if (hasSpeed()) { + Speed(speed.toDouble(), null) + } else { + null + }) + } +} diff --git a/android/core/src/main/java/com/stadiamaps/ferrostar/core/NavigationViewModel.kt b/android/core/src/main/java/com/stadiamaps/ferrostar/core/NavigationViewModel.kt index 2c6fd4ad..ab0792d1 100644 --- a/android/core/src/main/java/com/stadiamaps/ferrostar/core/NavigationViewModel.kt +++ b/android/core/src/main/java/com/stadiamaps/ferrostar/core/NavigationViewModel.kt @@ -27,7 +27,6 @@ data class NavigationUiState( class NavigationViewModel( stateFlow: StateFlow, initialUserLocation: UserLocation, - private val routeGeometry: List, ) : ViewModel() { private var lastLocation: UserLocation = initialUserLocation @@ -55,7 +54,7 @@ class NavigationViewModel( snappedLocation = location, // TODO: Heading/course over ground heading = null, - routeGeometry = routeGeometry, + routeGeometry = coreState.routeGeometry, visualInstruction = visualInstructionForState(coreState.tripState), spokenInstruction = null, distanceToNextManeuver = distanceForState(coreState.tripState), diff --git a/android/demo-app/src/main/java/com/stadiamaps/ferrostar/MainActivity.kt b/android/demo-app/src/main/java/com/stadiamaps/ferrostar/MainActivity.kt index 3122003a..3b764e5a 100644 --- a/android/demo-app/src/main/java/com/stadiamaps/ferrostar/MainActivity.kt +++ b/android/demo-app/src/main/java/com/stadiamaps/ferrostar/MainActivity.kt @@ -110,7 +110,8 @@ class MainActivity : ComponentActivity(), AndroidTtsStatusListener { } core.alternativeRouteProcessor = AlternativeRouteProcessor { core, routes -> if (routes.isNotEmpty()) { - core.startNavigation( + // NB: Use `replaceRoute` for cases like this!! + core.replaceRoute( routes.first(), NavigationControllerConfig( StepAdvanceMode.RelativeLineStringDistance( diff --git a/android/maplibreui/build.gradle b/android/maplibreui/build.gradle index d181d65b..92db23b4 100644 --- a/android/maplibreui/build.gradle +++ b/android/maplibreui/build.gradle @@ -22,6 +22,7 @@ android { } } compileOptions { + coreLibraryDesugaringEnabled true sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } @@ -43,6 +44,9 @@ android { } dependencies { + // For as long as we support API 25; once we can raise support to 26, all is fine + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' + implementation 'androidx.core:core-ktx:1.12.0' implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0') implementation 'androidx.appcompat:appcompat:1.6.1' From 115258569a24f54050e06a1a4516f1d8d7d1b68c Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Wed, 29 May 2024 17:17:32 +0200 Subject: [PATCH 06/12] Remove logging --- .../src/main/java/com/stadiamaps/ferrostar/core/FerrostarCore.kt | 1 - .../core/src/main/java/com/stadiamaps/ferrostar/core/Location.kt | 1 - 2 files changed, 2 deletions(-) diff --git a/android/core/src/main/java/com/stadiamaps/ferrostar/core/FerrostarCore.kt b/android/core/src/main/java/com/stadiamaps/ferrostar/core/FerrostarCore.kt index 7646921e..17d3d93b 100644 --- a/android/core/src/main/java/com/stadiamaps/ferrostar/core/FerrostarCore.kt +++ b/android/core/src/main/java/com/stadiamaps/ferrostar/core/FerrostarCore.kt @@ -341,7 +341,6 @@ class FerrostarCore( _lastLocation = location val controller = _navigationController - android.util.Log.i(TAG, "Location Updated: $location") if (controller != null) { _state?.update { currentValue -> val newState = diff --git a/android/core/src/main/java/com/stadiamaps/ferrostar/core/Location.kt b/android/core/src/main/java/com/stadiamaps/ferrostar/core/Location.kt index 715353d0..c4ff6f6e 100644 --- a/android/core/src/main/java/com/stadiamaps/ferrostar/core/Location.kt +++ b/android/core/src/main/java/com/stadiamaps/ferrostar/core/Location.kt @@ -79,7 +79,6 @@ class AndroidSystemLocationProvider(context: Context) : LocationProvider { return } val androidListener = LocationListener { - android.util.Log.i("LocationListener", "Location Updated: $it") listener.onLocationUpdated(it.toUserLocation()) } listeners[listener] = androidListener From 7d958ee7fc9c98f939bf2b3b5e53b90b8e87e998 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Fri, 31 May 2024 16:03:44 +0900 Subject: [PATCH 07/12] Minor cleanup --- .../src/main/java/com/stadiamaps/ferrostar/MainActivity.kt | 1 + common/ferrostar/src/routing_adapters/osrm/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/android/demo-app/src/main/java/com/stadiamaps/ferrostar/MainActivity.kt b/android/demo-app/src/main/java/com/stadiamaps/ferrostar/MainActivity.kt index 3b764e5a..439a5c40 100644 --- a/android/demo-app/src/main/java/com/stadiamaps/ferrostar/MainActivity.kt +++ b/android/demo-app/src/main/java/com/stadiamaps/ferrostar/MainActivity.kt @@ -109,6 +109,7 @@ class MainActivity : ComponentActivity(), AndroidTtsStatusListener { CorrectiveAction.GetNewRoutes(remainingWaypoints) } core.alternativeRouteProcessor = AlternativeRouteProcessor { core, routes -> + android.util.Log.i(TAG, "Received alternate route(s): $routes") if (routes.isNotEmpty()) { // NB: Use `replaceRoute` for cases like this!! core.replaceRoute( diff --git a/common/ferrostar/src/routing_adapters/osrm/mod.rs b/common/ferrostar/src/routing_adapters/osrm/mod.rs index 8b0c17cd..08505361 100644 --- a/common/ferrostar/src/routing_adapters/osrm/mod.rs +++ b/common/ferrostar/src/routing_adapters/osrm/mod.rs @@ -5,7 +5,7 @@ use crate::models::{ GeographicCoordinate, RouteStep, SpokenInstruction, VisualInstruction, VisualInstructionContent, Waypoint, WaypointKind, }; -use crate::routing_adapters::{osrm::models::RouteResponse, Route, RoutingResponseParseError}; +use crate::routing_adapters::{osrm::models::{RouteResponse, RouteStep as OsrmRouteStep}, Route, RoutingResponseParseError}; use geo::BoundingRect; use polyline::decode_polyline; use std::collections::HashSet; @@ -96,7 +96,7 @@ impl RouteResponseParser for OsrmResponseParser { impl RouteStep { fn from_osrm( - value: &models::RouteStep, + value: &OsrmRouteStep, polyline_precision: u32, ) -> Result { let linestring = decode_polyline(&value.geometry, polyline_precision) From 3af7121cbfb98ee02072e888c1620060e3a22254 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Fri, 31 May 2024 16:04:12 +0900 Subject: [PATCH 08/12] cargo fmt --- common/ferrostar/src/routing_adapters/osrm/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/ferrostar/src/routing_adapters/osrm/mod.rs b/common/ferrostar/src/routing_adapters/osrm/mod.rs index 08505361..2442ddbe 100644 --- a/common/ferrostar/src/routing_adapters/osrm/mod.rs +++ b/common/ferrostar/src/routing_adapters/osrm/mod.rs @@ -5,7 +5,10 @@ use crate::models::{ GeographicCoordinate, RouteStep, SpokenInstruction, VisualInstruction, VisualInstructionContent, Waypoint, WaypointKind, }; -use crate::routing_adapters::{osrm::models::{RouteResponse, RouteStep as OsrmRouteStep}, Route, RoutingResponseParseError}; +use crate::routing_adapters::{ + osrm::models::{RouteResponse, RouteStep as OsrmRouteStep}, + Route, RoutingResponseParseError, +}; use geo::BoundingRect; use polyline::decode_polyline; use std::collections::HashSet; From 7c8cc25b697f60108794b6586d4ae7a93ff7be7a Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Fri, 31 May 2024 16:17:35 +0900 Subject: [PATCH 09/12] ktftmformat --- .../src/main/java/com/stadiamaps/ferrostar/core/Location.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/android/core/src/main/java/com/stadiamaps/ferrostar/core/Location.kt b/android/core/src/main/java/com/stadiamaps/ferrostar/core/Location.kt index c4ff6f6e..e9a293ed 100644 --- a/android/core/src/main/java/com/stadiamaps/ferrostar/core/Location.kt +++ b/android/core/src/main/java/com/stadiamaps/ferrostar/core/Location.kt @@ -78,9 +78,7 @@ class AndroidSystemLocationProvider(context: Context) : LocationProvider { android.util.Log.d(TAG, "Already registered; skipping") return } - val androidListener = LocationListener { - listener.onLocationUpdated(it.toUserLocation()) - } + val androidListener = LocationListener { listener.onLocationUpdated(it.toUserLocation()) } listeners[listener] = androidListener val handler = Handler(Looper.getMainLooper()) From 2049a803bac451fa04ebd10d3e6e603b2bdeb13c Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Sun, 2 Jun 2024 06:21:25 +0900 Subject: [PATCH 10/12] Pin cargo-ndk --- .github/workflows/android.yml | 2 +- .github/workflows/gradle-publish.yml | 2 +- CONTRIBUTING.md | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 1380c9fa..6e2fa874 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -31,7 +31,7 @@ jobs: working-directory: android - name: Install cargo-ndk - run: cargo install cargo-ndk + run: cargo install cargo-ndk --version 3.5.4 - name: Touch local.properties (required for cargo-ndk) run: touch local.properties diff --git a/.github/workflows/gradle-publish.yml b/.github/workflows/gradle-publish.yml index abf0a827..f48e43ae 100644 --- a/.github/workflows/gradle-publish.yml +++ b/.github/workflows/gradle-publish.yml @@ -30,7 +30,7 @@ jobs: working-directory: android - name: Install cargo-ndk - run: cargo install cargo-ndk + run: cargo install cargo-ndk --version 3.5.4 - name: Touch local.properties (required for cargo-ndk) run: touch local.properties diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e75d4728..b3f3e3d5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -125,9 +125,12 @@ to ensure consistent formatting. automatically build what it needs. ```sh -cargo install cargo-ndk +cargo install cargo-ndk --version 3.5.4 ``` +NOTE: cargo-ndk is currently pinned due to [this issue](https://github.com/bbqsrc/cargo-ndk/issues/137) +which breaks workspaces. + 3. Ensure that the latest NDK is installed (refer to the `ndkVersion` number in [`core/build.gradle`](android/core/build.gradle) and ensure you have the same version available). From 5837b16621d57f53be00e6a3251aec7c06c4a07d Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Sun, 2 Jun 2024 06:41:58 +0900 Subject: [PATCH 11/12] Add coarse location permission (lint) --- android/core/src/main/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/core/src/main/AndroidManifest.xml b/android/core/src/main/AndroidManifest.xml index 09a0c769..bfdcc832 100644 --- a/android/core/src/main/AndroidManifest.xml +++ b/android/core/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file From c6b4c4058d5792c130f32a0c21a519244381fd11 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Mon, 3 Jun 2024 12:43:57 +0900 Subject: [PATCH 12/12] Unpin cargo-ndk after identifying a fix --- .github/workflows/android.yml | 2 +- .github/workflows/gradle-publish.yml | 2 +- CONTRIBUTING.md | 5 +---- android/core/build.gradle | 1 + 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 6e2fa874..1380c9fa 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -31,7 +31,7 @@ jobs: working-directory: android - name: Install cargo-ndk - run: cargo install cargo-ndk --version 3.5.4 + run: cargo install cargo-ndk - name: Touch local.properties (required for cargo-ndk) run: touch local.properties diff --git a/.github/workflows/gradle-publish.yml b/.github/workflows/gradle-publish.yml index f48e43ae..abf0a827 100644 --- a/.github/workflows/gradle-publish.yml +++ b/.github/workflows/gradle-publish.yml @@ -30,7 +30,7 @@ jobs: working-directory: android - name: Install cargo-ndk - run: cargo install cargo-ndk --version 3.5.4 + run: cargo install cargo-ndk - name: Touch local.properties (required for cargo-ndk) run: touch local.properties diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b3f3e3d5..e75d4728 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -125,12 +125,9 @@ to ensure consistent formatting. automatically build what it needs. ```sh -cargo install cargo-ndk --version 3.5.4 +cargo install cargo-ndk ``` -NOTE: cargo-ndk is currently pinned due to [this issue](https://github.com/bbqsrc/cargo-ndk/issues/137) -which breaks workspaces. - 3. Ensure that the latest NDK is installed (refer to the `ndkVersion` number in [`core/build.gradle`](android/core/build.gradle) and ensure you have the same version available). diff --git a/android/core/build.gradle b/android/core/build.gradle index 7352da5e..fa977915 100644 --- a/android/core/build.gradle +++ b/android/core/build.gradle @@ -69,6 +69,7 @@ dependencies { cargoNdk { module = "../common" // Directory containing Cargo.toml librariesNames = ["libferrostar.so"] + extraCargoBuildArguments = ["-p", "ferrostar"] } android.libraryVariants.all { variant ->