ffwdme.js Guides

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.

What is ffwdme.js?

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:

  • Core - the core navigation functionality
  • Components - UI components to use in navigation system (optional)
  • Debug - Debugging tools for development (optional)

Features of ffwdme.js include:

  • It’s open source :)
  • No dependencies for the core framework.
  • Small footprint (core framework is only 16kb minified/gzipped)
  • Provides a ready-to-go collection of UI elements.
  • Easy to be used/extended with other routing services.
  • Can be integrated with popular frameworks, like Backbone.js, AngularJS or Ember.
  • Custom events provide a simple way to hook your own UI into ffwdme.js and react to changes during navigation.
  • Targets iOS and Android, works as pure webpage as well as embedded web view for native apps.
  • Custom debugging tools for a better development workflow.

For the navigation itself it features amongst others:

  • Geoposition of the client
  • Check if the client is on the route or not
  • Automatic recalculation of the route, if client leaves the current route for some time
  • Direction for the next turn
  • Distance and time to next turn
  • Distance and time to destination
  • Rotation and speed-dependent zooming of the map
  • Voice instructions (experimental)

What is ffwdme.js not?

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.

What do I need to create my own turn by turn navigation system?

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:

  • Getting the destination from the user
  • Calculating a route to the destination
  • Control the position of the user and generate instructions to follow
  • Provide an UI to present the generated instructions to the user

We will try to give a short overview over these four areas and then we’ll proceed to setup a test project.

Getting the destination from the user

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.

Calculating a route to the destination

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.

Control the position of the user and generate instructions to follow

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.

Provide an UI to present the generated instructions to the user

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.

TL;DR - Show me the code!

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.

Initializing a ffwdme.js Application

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 Events

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.

Calculating a Route

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

Starting the Navigation

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();
});

The NavigationInfo Object

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.

Putting it all together

// 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!');
});

UI Components

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).

Debugging Tools

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.

GeoProvider

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.

Components

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!

Questions?

If you still have questions after reading these guides, please let us know by opening a Github issue.