Help

Get Started With Customizable Routing

The upcoming version 1.0 of the GraphHopper routing engine has a new customizable routing feature.

Disclaimer: We updated the format for custom models. See the updated blog post with several examples.

To install GraphHopper with this feature on your own server download the JDK, the routing server, the configuration file and the OpenStreetMap data:

# if you have no JDK installed get it from: https://adoptopenjdk.net/
wget https://oss.sonatype.org/content/groups/public/com/graphhopper/graphhopper-web/1.0/graphhopper-web-1.0.jar
wget https://raw.githubusercontent.com/graphhopper/graphhopper/1.0/config-example.yml
wget http://download.geofabrik.de/europe/germany/berlin-latest.osm.pbf

Now edit the downloaded config-example.yml and add the following car profile, please note that indentation is important in YAML:

graphhopper:
  ...

  profiles:
    - name: car
      vehicle: car
      weighting: custom
      custom_model_file: empty

  # also add the configuration:
  routing.ch.disabling_allowed: true

Then you can start the java server:

java -Ddw.graphhopper.datareader.file=berlin-latest.osm.pbf -jar graphhopper-web-1.0.jar server config-example.yml

which should give you a similar log output:

...
 io.dropwizard.server.ServerFactory - Starting GraphHopperApplication
...
c.g.reader.osm.GraphHopperOSM - version 1.0|2020-05-01T21:00:45Z (5,15,4,3,5,6)
c.g.reader.osm.GraphHopperOSM - start creating graph from berlin-latest.osm.pbf
...
c.g.routing.ch.CHPreparationHandler - 1/1 calling CH prepare.doWork for profile 'car' NODE_BASED
...
c.g.reader.osm.GraphHopperOSM - flushing graph CH|car|RAM_STORE|2D|no_turn_cost|5,15,4,3,5, details:edges:133 725...
...
org.eclipse.jetty.server.Server - Started @23477ms

When you start the server the first time it will take a bit longer as it creates the road network (the part after “start creating graph”) and helper files (the part of CHPreparationHandler) inside the graph-cache folder.

Now open the GraphHopper Maps demo UI that is available on your local machine: http://localhost:8989/maps/?point=52.48947%2C13.346329&point=52.565917%2C13.450699&profile=car

Click on the small “flex” button near the “Search” button and a text input will open. Include the following:

priority:
  road_class: { primary: 0.5 }

to e.g. avoid primary roads (see below for “prefer”). The route will be evaluated on-the-fly and the query speed is already perfect for most real world scenarios.

An animated difference that transitions to the user-defined route

Increasing the priority of primary roads works a bit different: decrease the priority of “all other roads”, which can be done using the so called “catch-all” key and set the priority of primary to 1.0:

priority:
  road_class: { primary: 1.0, "*": 0.5 }

Learn More About CustomWeighting

To learn more about the possibilities read the documentation. Also you’ll notice that when you place the cursor in the text input on e.g. “road_class” it will list all available values like motorway or footpath, or place it on “priority” this will list all available so called “encoded values” like road_class, but also road_environment (bridge, tunnel, …). By default several useful “encoded values” are already available. For example click on the top right button and select “Omniscale Dev” and “Local MVT” then you can hover your mouse and see the “encoded values” road_environment, road_class and max_speed on the left panel for every road segment:

Debug view of the road network. The secondary road Heinersdorfer Straße is highlighted.

You can add more encoded values like surface or toll when you add the following in the config-example.yml:

graph.encoded_values: surface,toll,max_width,max_weight,max_height,hazmat,toll,track_type

To make them available in the graph you have to:

  1. stop the server
  2. remove graph-cache and
  3. start the server again.

This procedure is always required when you want to make changes to the graph itself, so more or less every time you edit the config-example.yml or when you want to update the OSM data.

Fast Response Times for CustomWeighting

To have a fast response also for longer routes like across Europe you put the customization in a YAML (or JSON) file and provide this at import time. I.e. create a local file profile1.yml with the following content:

priority:
  road_class: { primary: 0.5 }

In the config-example.yml file replace:

 custom_model_file: empty

with

 custom_model_file: profile1.yml

Now again remove the graph-cache folder and restart the server again.

When you now open the UI the default vehicle profile will already avoid the primary road_class without the need to specify this in the request. But the big difference is the query speed. You won’t notice this for the Berlin example, still you try a larger area, e.g. download a OSM pbf file for a bigger area (and remove the graph-cache to force a re-import) and try this again.

Contraction Hierarchy and Landmark Preparation

Technically we enabled a Contraction Hierarchy (CH) preparation for the custom car profile, this is called the speed mode, see the profiles_ch section in config-example.yml. This step introduces shortcut edges and uses a special query to significantly improve query speed for long routes. The downside is that currently you cannot further customize a “CH prepared profile” per-request.

To get faster response times and still being able to customize the profile per-request you can enable the hybrid mode which uses the landmark (LM) preparation under the hood:

graphhopper:
  ...
  profiles_lm:
  - profile: car

To make this change available you again need to remove graph-cache and restart the server.

Get a more sophisticated profile in this truck example file.

More Playground

Let’s add a bike profile and highly prefer roads that are part of the official bike network. To do this first add the bike flag encoder and also change the profiles section:

graphhopper:
  ...
  graph.flag_encoders: car,bike

  profiles:
    - name: car
      vehicle: car
      weighting: custom
      custom_model_file: empty
    - name: bike
      vehicle: bike
      weighting: custom
      custom_model_file: empty

Remove the graph-cache folder and restart the server again. You’ll now see also bike in the logs:

c.g.reader.osm.GraphHopperOSM - using CH|car,bike|RAM_STORE|2D|no_turn_cost

When the server is ready you’ll again being able to open the previous link: http://localhost:8989/maps/?point=52.48947%2C13.346329&point=52.565917%2C13.450699&profile=car

The only tiny difference is that a bike icon is additionally displayed. Click on it. You’ll get an error which reads:

Cannot find CH preparation for the requested profile: 'bike' You can try disabling CH using ch.disable=true available CH profiles: [car]

This error tells us that we cannot use speed mode for our new bike profile yet. We have two options: 1) We can keep using the profile using the hybrid mode and further adjust it to our needs and 2) we can enable speed mode once we are fully satisfied with our settings. Let’s see how this works.

Option 1

Disable the speed mode (CH) in the request, i.e. append a special parameter to the URL which will be forwarded to the routing server: http://localhost:8989/maps/?point=52.48947%2C13.346329&point=52.565917%2C13.450699&profile=bike&ch.disable=true

This is sufficient to try out the custom weighting with bike. Click again on the “flex” icon near the “Search” button and enter the following text:

priority:
  bike_network: { other: 0.4 }

When you click again on the button in the top right corner and select “TF Cycle” some nice bike maps will appear. The blue, purple or red marked roads are official recommended routes from different bike networks. And with the steps above we avoid all roads that are not part of this network. You can reduce the number to e.g. 0.1 and see that the route will “snap” to these official bike network even if that means a huge detour:

“Snap” to official bike routes.

Get a more sophisticated “cargo bike” profile in this example file.

Option 2

Now we can enable the speed mode also for bike. To do that we only have to add “bike” to the profiles_ch section in the config-example.yml:

graphhopper:
  ...
  profiles_ch:
    - profile: car
    - profile: bike

Then once again remove the graph-cache folder and restart the server again. You’ll now notice the following two entries in the logs:

c.g.routing.ch.CHPreparationHandler - 1/2 calling CH prepare.doWork for profile 'car' NODE_BASED
c.g.routing.ch.CHPreparationHandler - 2/2 calling CH prepare.doWork for profile 'bike' NODE_BASED

Which means that it will use a bit more RAM (and space on disk) but also take a bit longer. After that you’ll be able to click on the bike icon:
http://localhost:8989/maps/?point=52.48947%2C13.346329&point=52.565917%2C13.450699 or follow this link to get the bike icon pre-selected. Now with this additional “CH preparation” also the bike profile responds fast even for long-distance routes.

If you have questions or feedback for this tutorial, you can comment in this topic.