This guides will give you an idea, what ffwdme.js is, what you can do with it and how you can get started.
If you see a mistake or have any other feedback, how to make it better, please open an issue on Github.
ffwdme.js is a JavaScript toolkit that aims to bring interactive GPS driving directions to mobile browsers. It provides you a programming interface to create and debug your own turn by turn navigation systems and ships with a collection of handy UI components.
It is split up in three packages:
ffwdme is a clientside JavaScript toolkit. It does not come with its own geo data or routing/search algorithms.
Most likely you will need to connect to external services, like a routing server, a map client or a geocoding service.
But don’t despair! These guides will give you advice, how to get everything running.
Ok, let’s take a moment to think about the different parts that are needed to assemble a navigation system. In this guide, we will split it up into four different areas:
We will try to give a short overview over these four areas and then we’ll proceed to setup a test project.
At the moment, ffwdme.js relies on you to pass a destination coordinate in order to start the navigation.
It is up to you where you get this coordinate from. It could be from your database, hardcoded into the application or you could provide a UI element, so the user can enter a textual adress and generate the coordinate using an external geocoding service.
ffwdme is not a routing service. This means it can not calculate routes from a given start to a given destination. You need an external service to do this.
Luckily, we got you covered, as the open source routing service GraphHopper provides you with a free tryout key to get started.
ffwdme provides adapters so you can utilize the routing service of your choice and it will convert the response into a data format that it can work with internally. Basically, if you use a supported routing service, you don’t have to worry about anything.
This is the job of the ffwdme.js core. Once you configured your routing service, you can calculate a new route. ffwdme.js will watch the position of the client and will compare it to the calculated route. It will try to find out, if and where the user is on this route. The rest of your app can listen to ffwdme.js events that will let you know when something changes and provide useful metadata.
ffwdme.js ships with a collection of UI elements that are useful to provide the user information about the ongoing navigation. You can use them or just create your own. Most interesting information is already provided by the ffwdme.js core events and the UI components are basically only a thin view layer to visualize the meta data of these events.
It is important to know that ffwdme.js is not a provider of map data. If you want to show a map in your navigation system, you have to get the map client somewhere. However, ffwdme.js provides you with a map component based on Leaflet and Mapbox. To use it, you have to sign up for a free Mapbox account to get an API key for their map tiling service.
You made it this far! Thank you for your patience. :)
The easiest way to get started is to clone the Github repository and follow the instructions in the README.md. It will set you up with a small local server, a configured build tool (gulp) and a demo page (including debugging tools).
Below we will outline a couple of core concepts of the ffwdme.js code, that may help you to get better insights how everything works.
To initialize a ffwdme.js application, just call the ffwdme.initialize
method. It takes an options object as the only argument.
In most cases, you just need to specify your routing service adapter and depending on the adapter, you may need to pass additional options.
If you’re using the default routing service “GraphHopper”, the initalize call would look like this:
ffwdme.initialize({
routing: 'GraphHopper',
graphHopper: {
apiKey: 'GRAPHHOPPER-API-KEY'
}
});
ffwdme.js provides the following events:
Event | Description |
---|---|
geoposition:init |
The geoposition watch of the client GPS gets initialized. Useful for showing something like a “waiting for geoposition” loader. |
geoposition:ready |
The geoposition was found/allowed. |
geoposition:update |
The geoposition was updated. Depending on the client device, this can be fired multiple times per second. |
geoposition:error |
There was an error while obtaining the geoposition. Either a technical or the user didn’t allow it. |
routecalculation:start |
A route calculation has been started. |
routecalculation:success |
Route calculation was successful. |
routecalculation:error |
Route calculation was not successful. Either no route could be calculated or there was an network error. |
navigation:onroute |
The client is on the current route. This will pass a NavInfo object |
navigation:offroute |
The client is not on the route anymore. |
reroutecalculation:start |
Same as routecalculation:start but it tries to recalculate the existing route because e.g. the user left the route already there. |
reroutecalculation:success |
Same as routecalculation:success but it did recalculate the existing route because e.g. the user left the route already there. |
reroutecalculation:error |
Same as routecalculation:error but it tried to recalculate the existing route because e.g. the user left the route already there. |
You can register to this events with ffwdme.on(eventName, callback)
, example:
ffwdme.on('navigation:onroute', function(e) {
console.info(e.navInfo.nextDirection.street); // prints next street
});
There is also ffwdme.off(eventName, registeredCallback)
to unregister from events and ffwdme.once(eventName, callback)
to register to an events only once.
You determined the configuration of the routing service to use when you initialized ffwdme. This automatically created a convenience method for you, so now, you just have to call something like…
new ffwdme.routingService({
dest: { lat: 49.9, lng: 8.9 }
}).fetch();
… to calculate a new route.
To react to the results of this call you can register to the routecalculation
events listed above.
Important: Make sure you are already watching the geoposition on the client, as this is needed to
Once you obtained a route, you can pass it to ffwdme.navigation
. This tells ffwdme to compare the movements of the client to this route and it will fire the navigation:onroute
and navigation:offroute
events.
The easiest way to start the navigation is to register for the routecalculation:success
event, that will contain the calculated route.
ffwdme.on('routecalculation:success', function(response) {
ffwdme.navigation.setRoute(response.route).start();
});
Ok, so now you initialized you app, you calculated a route and you started the navigation. But how do you create the UI for your turn by turn navigation system?
The answer is the NavigationInfo
object. It gets passed to the navigation:onroute
event that is triggered when the client updates its geoposition and ffwdme can locate it on the current route.
ffwdme.on('navigation:onroute', function(e) {
var navInfo = e.navInfo; // <- there it is!
});
The NavigationInfo
object contains a lot of interesting information, that you can use to display it in your UI.
Attribute | Description |
---|---|
arrived |
Indicates if the user arrived at his destination. |
currentDirection |
The current direction of the calculated route. Includes information of the routing service, like street name, turn angle, etc. |
distanceToDestination |
The distance to the destination in meters. |
distanceToNextDirection |
The distance to the next turn in meters. |
finalDirection |
Indicates if this is the final direction of the route. |
navigation |
Points to the current `ffwdme.Navigation` instance. |
nearest |
Gives information about the point on the route mapped to the raw geoposition of the client. |
nextDirection |
The next direction of the calculated route. Includes information of the routing service, like street name, turn angle, etc. |
position |
The position of the client mapped to the route. |
positionRaw |
The raw geoposition of the client. |
ratioCompletedDirection |
Value between 0.1 and 1.0 that represents the progress on the current direction of the route. |
ratioCompletedRoute |
Value between 0.1 and 1.0 that represents the progress on the current route. |
route |
The current route. |
timeToDestination |
The estimated time to the destination in seconds. |
timeToNextDirection |
The estimated time to the next direction in seconds. |
// first initialize ffwdme
ffwdme.initialize(/*config*/);
// start the navigation once the route is calculated
ffwdme.on('routecalculation:success', function(response) {
ffwdme.navigation.setRoute(response.route).start();
});
// wait for the geoposition to be ready,
// then start route calculation
ffwdme.on('geoposition:ready', function() {
var route = new ffwdme.routingService({
dest: { lat: 49.9, lng: 8.9 }
}).fetch();
});
// register to navigation events
ffwdme.on('navigation:onroute', function(e) {
var distance = e.navInfo.distanceToDestination;
console.info('You have ' + distance + ' meters to go!');
});
ffwdme.js comes with an optional components package. You can use it right away to build your own navigation UI or just use it as an example to create your own.
The components basically work by registering to the events described above and reacting to them. Everything you see in the ffwdme.js UI is a component.
There are visible components like the map or the panels with information to the current directions. But there are also invisible components like AutoRerouting
that don’t provide a UI for the user, but help to control the navigation process for the user (in this case by calculation a new route, after the user has left the calculated route for some time).
Because debugging is hard and can be very annoying especially when you try to create a turn by turn navigation system, ffwdme.js comes with some handy tools.
The GeoProvider gives you two tools: A recorder interface and a player. Using the recorder, you can record GPS tracks with a mobile device and store it as a fixture. With the player, you can replay these tracks in your local development environment to debug and test your navigation system.
We provide some debugging components for you, so you can control the GeoProvider replay, the routing service and you get a good overview for the generated NavigationInfo
data. Try out the tabs on the demo.html
page in the code repository!
If you still have questions after reading these guides, please let us know by opening a Github issue.