stadiamaps / ferrostar Goto Github PK
View Code? Open in Web Editor NEWA FOSS navigation SDK built from the ground up for the future
Home Page: https://stadiamaps.github.io/ferrostar/
License: Other
A FOSS navigation SDK built from the ground up for the future
Home Page: https://stadiamaps.github.io/ferrostar/
License: Other
The ask:
FerrostarCore
in iOS / Android: navigating, stopped, and paused.pauseNavigation
method which stops updates from the location provider but preservers all other stateThis does not have to be final; only a proof-of-concept implementation. The basic requirement would be to flag that the user is off-route (ex: by a static amount) in the core and signaling back to the native layer.
When the user doesn’t have an accurate GPS signal, the core should be able to (optionally) assume progress along the route. This is useful for cases such as driving through a tunnel, particularly for navigation in vehicles like cars and trucks.
We want the core to calculate most parameters so that all rendering frontends can implement the standard "following" camera consistently.
This information should be returned as part of TripState
.
Components needed:
The Route
schema (and its children) need some fleshing out to support proof of concept features. We should keep this as barebones as possible for the PoC and defer as many non-essential decisions as possible before onboarding more stakeholders using diverse routing backends.
Lots of things TBD, but we'll need at least the following:
In Swift it is not possible to simply mark a type for re-export. This is a fairly deep seated design decision, and there are several consequences. Downstream code depending on the core Swift package cannot construct or have an explicitly typed reference to any types from dependencies, including our own FFI(!). The types are "there" and you can store one in a non-explicitly-typed variable, pass this back to another function, and even inspect any of the properties, methods, etc. with full code completion in Xcode. But you cannot construct one yourself, nor can you store it in a typed field.
This is annoying for things like NavigationStateUpdate
, which we currently basically re-export with our own type that (as an enum that is constructible only from the original enum) will generally cause compilation errors in the case of a divergence from the underlying type.
This is extremely problematic with types like Route
in the context of enabling local route generation from user code, since we currently lack a good solution for constructing the Route
in Swift code.
Decide on a more formal policy somewhat more organically as the project matures.
Initial pass at showing a banner for the next instruction. Non-PoC requirements like icons will be added later (note: iOS might actually have usable icons built in with SFSymbols).
This will require surfacing a banner object in the navigation state updates and creating the following basic UI elements:
We need to decide on iconography for navigation banners at some point. These symbols will show along banners when the user is supposed to turn left, continue straight, do a barrel roll, and... you get the idea ;)
SFSymbols on iOS actually get us pretty close to something usable. It's not exactly what I'd envision for the end state, but it's pretty good. Material icons on the other hand seem rather lacking.
So, we'll need to find some better iconography (I'm tentatively thinking we'd use the same set for both iOS and Android, but that's a loosely held opinion of convenience) that's available under a permissive license.
We need this in order to support banners (properly) and to properly visualize the user's progress on the route polyline.
This would not replace the ability to set a specific preference manually, but would be nice for most navigation applications.
@Archdoog mentioned that maplibre-navigation-android has a pretty good solution on this for Android (see screenshot).
For SPM we can look at having a PLIST file that's gitignored, which would let us have previews using non-trivial commercial or self-hosted maps (ex: Stadia, Mapbox, etc.). In both cases, I think we can store not only the API key but also things like tile URLs to use for previews.
When we detect that the user is off route(#8), initiate recalculation of the route.
Every frontend will be in charge of camera handling, and in many cases, the user may be allowed to temporarily take over. However, for the case of the "default" camera following the user around during navigation, we want to outsource this logic in a fairly generic manner to the core, which returns the key bits to be translated into camera control of the renderer by the frontend.
This issue will track individual cases until we finish the next milestone.
Android doesn't appear to have a built-in distance formatter analogous to MKDistanceFormatter
on iOS.
Known options
DecimalFormat
- not deprecated, but Google basically says you shouldn't use thisNumberFormatter
- Google says to use this for localized number formatting.Unfortunately, NumberFormatter
requires API level 30, which would seem to exclude a large number of users. It also only solves half the problem. We also need to determine which units we should be using, and this is not trivial (ex: UK prefers miles for distances), and the class doesn't appear to help with those.
Requirements off the top of my head:
Suggestions welcome on high-quality / de-facto standard Android libraries that can accomplish this.
This causes some weird behaviors in the demo app and will eventually break the iOS demo into an unusable state.
Same vein as #32 but for Android
Goal: be able to simulate progress along a route for testing purposes, both "live" in the UI and "speed running" for unit tests.
For reusability, the main logic should live in the Rust core. The interface will consist of a family of constructors and public methods that facilitate transitioning to the next step. The interface should be pure where possible to allow for composability, easy unit testing, and custom behavior.
route
A route (fully specified including all the maneuvers and metadata; can be captured from a Valhalla instance)speed_multplier
or speed
(optional) A multiplier to apply to the route replay. TBD. We could probably use a fixed speed to start, but ideally it might be nice to mimic real-world estimated speeds or something. @Archdoog has probably some prev experience to contribute here.locations
or polyline
The path that the user should actually visit; defaults to the route, but we want to be able to simulate things like off-route detection.The primary non-constructor function will be a tick()
which returns a new state given a location. It seems useful to have both a parameter-less version as well as one which takes a location as a parameter. In this way, developer end users may follow the route up to a point and then decide to follow a diverging path in order to test off-route handling without having to specify a full alternate set of locations upfront / decode a polyline and edit by hand, and that sort of thing.
There are a number of cases where I have added these lazily to speed up the early development, but this needs to be rectified. Also consider adding linter checks of some sort if possible to ensure the only ones that we accept are clearly marked.
Currently we have some documentation spread across the README, examples, and the ARCHITECTURE document. As things mature over the next few months, we should consolidate this into an mdBook and publish it on GitHub pages to make onboarding new devs easier.
Especially important for lower quality phones, but this can quite dramatically reduce noise even in fairly open skies on an iPhone! See https://mastodon.social/@_Davidsmith/111409340172869799.
This can be added to the NavigationController
and be applied transparently. Probably should be configurable in case someone wants to disable it.
Right now it looks like the default for rustc is to bake debug symbols into the framework. We can extract this into a dSYM file and strip the binaries.
It will require a bit more research to figure out how/if these should be distributed though, as it would ideally be possible for users of the framework to have these symbols uploaded with their apps to Apple. This post has some potentially useful info: https://pspdfkit.com/blog/2020/supporting-xcframeworks/.
After some discussion, we have elected to go the "framework" route for recalculation. While users of Ferrostar will always be able to access state updates, including whether the user has deviated from the route, recalculation is common enough and annoying enough that we see value in offering a "full-service framework" approach to recalculation for the common case.
What this means:
FerrostarCore
at the imperative shell level (iOS / Android) will watch for route deviations.getRoutes
and then invoke a delegate method informing the application of the available alternative routes.This will programmatically enforce standards and make code reviews a lot easier.
Rust:
Apple:
Android:
This would support certain types of indoor positioning that deal in other coordinate systems like XYZ.
What the title says. It should be pretty straightforward to provide some basic components that make it easy to use with MapKit for SwiftUI.
At some point we'll want to explore support for no_std platforms. Our initial focus is on major mobile operating systems, which have support for the full standard library, but certain embedded platforms we may target in the future may not.
This issue tracks known steps we'll need to take to support no_std:
std::sync::Arc
in constructors, and types like std::time::SystemTime
for mapping timestamps.std::collections::HashMap
a few places; could probably replace with alloc::collections::BTreeMap
; we can also use Vec
from alloc
NavigationController
currently uses a Mutex
for internal state mutation. This is arguably an antipattern and breaks the clean "functional core / imperative shell" pattern. We should probably rearchitect this to return a new controller state (which must be saved by the caller) before v1.0.I've just had a call with the developers at https://www.naurt.com/, who have a pretty compelling solution to fused location updates on Android (and they also manage some improvements on iOS too!).
I have a test API key from them and will work on an integration for their SDK.
Ex: offline routing, having your own custom fallback layers, etc.
This looks promising: https://github.com/fzyzcjy/flutter_rust_bridge
Android is an unmitigated disaster when it comes to location services. We can hack things up with the core Android Location
APIs, but will need a better long-term solution.
Here is the shortest summary I can muster to describe the state of Android location services:
I am not currently aware of any open-source projects within the Android developer community which attempts to mitigate the above issues via a common "better fused location client" that has wide adoption. I hope that I'm just missing something, but I've confirmed most of the above with others in the Android community.
If this is indeed the case, we may simply have to push this down a layer and suggest that developers building apps with Ferrostar will have to consider this for their use cases individually. The Organic Maps issue doesn't inspire much confidence as they seem to have implemented workarounds specific to F-Droid.
We need to figure out an easy way to share test data across the core, Apple, and Android. This data comes in several forms:
Route
s and iOS/Android UI state from a predefined routeThese will be useful for creating demonstration applications, SwiftUI/Compose previews, and unit testing.
Currently some of this info exists, but it is spread out and (in some cases) duplicated.
navigation_controller.rs
and osrm/mod.rs
FerrostarCoreTests/Fixtures.swift
(used in ValhallaCoreTestsl
)FerrostarCore/ObservableState.swift
(waypoints as Swift code for a UI demo fixture)ValhallaCoreTest.kt
I don't think we necessarily need to dedupe all of this, but it probably would be useful to have an easy way to load up routes from Valhalla JSON and simulate location by replaying GPX files on both iOS and Android, and we'll need some helper code for that, and maybe a shared directory (+ symlinks? I think both Xcode and Gradle might not like looking too far afield for files.)
When we detect that the user is off route(#8), initiate recalculation of the route.
Stub tracking issue for the task; can flesh out later.
The core should expose remaining waypoints in some way eventually. This will enable users to recalculate more easily in the case of multi-leg trips. The MVP might ship without this, depending on whether @Archdoog finds out this is a hard requirement during integration.
This is necessary for things like route previews and selection views.
Similar to #36, we need to be able to select a live location provider and have a mechanism for entering waypoints (for live testing at a minimum).
In some scenarios, it would be nice to have a fallback to (or maybe even selectively prefer?) offline routing, such as when the user is in a poor network zone.
Off the top of my head, I'd say maybe we can have a fallback order or something and allow multiple route adapters in sequence. The offline routing would of course be the responsibility of the library end user, as routing itself is out of scope in Ferrostar, but it should support this use case.
While not all apps will necessarily need this, I expect that most will want a predesigned view that the user can activate to get a list of the upcoming maneuvers along the route.
Also add cargo-semver-checks to CI
Context from initial discussion:
Yeah, my thought was use the settings page to easily configure this as well as waypoints. Thinking:
This stuff can all easily be done with some SwiftUI and the @AppStorage decorator.
I agree though, that should be its own ticket after this is merged.
Originally posted by @Archdoog in #34 (comment)
Optionally, we should support configurable refresh behavior, whereby the SDK will periodically check that the current route is still the optimal one. This is useful in areas subject to traffic jams when combined with a routing solution that is aware of live traffic.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.