Replicate maxPossibleSpeed through custom model file


we are using the GH routing engine as a component in one of our java projects. We are using GraphHopper version 2.3.

In our setup we need routing for Vespas, i.e. essentially small, slow motorcycles. We initially hard-coded these with a custom flag encoder like this:

public class VespaFlagEncoder extends MotorcycleFlagEncoder {

    public VespaFlagEncoder() {
        // motorcycles are capped at 120 km/h, vespas should be noticeably slower
        maxPossibleSpeed = 50;

    public String toString() {
        return "vespa";

The hopper is then initialised like this (simplified):

Flagencoder = new VespaFlagEncoder();
String vehicle = "vespa";

EncodingManager em = EncodingManager.create(encoder);

hopper = new GraphHopperOSM().forDesktop();
hopper.setProfiles(new Profile(vehicle).setVehicle(vehicle).setWeighting("fastest"));
hopper.getCHPreparationHandler().setCHProfiles(new CHProfile(vehicle));

… and queried like this:

GHRequest request = new GHRequest(origin.y, origin.x, dest.y, dest.x);

GHResponse response = hopper.route(request);

We currently try to use the new GH config file interface to handle the whole router initialisation to be more flexible and rely less on hard-coding custom vehicles. We successfully managed to parse a config object from the following YAML file:

datareader.file: <PATH-TO-OSM>
graph.location: <CACHE-DIR>
graph.flag_encoders: motorcycle
  - name: vespa
    vehicle: motorcycle
    weighting: custom
    custom_model_file: vespa_model.yml
  - profile: vespa
prepare.min_network_size: 200
graph.dataaccess: RAM_STORE

and associated custom vespa_model.yml:

    motorway: 0
    trunk: 50
    primary: 50
    secondary: 50
    tertiary: 50
max_speed_fallback: 50

Printing the resulting GraphHopperConfig object yields:

name=vespa|vehicle=motorcycle|weighting=custom|turnCosts=false|hints={custom_model_file=empty, custom_model=distanceInfluence=70.0|speedFactor={}|maxSpeed={road_class={motorway=0, trunk=50, primary=50, secondary=50, tertiary=50}}|maxSpeedFallback=50.0|priorityMap={}|areas={}}
datareader.file: <PATH-TO-OSM>
graph.location: <CACHE-DIR>
graph.flag_encoders: motorcycle
prepare.min_network_size: 200
graph.dataaccess: RAM_STORE

We can use this to initialise our hopper simply as follows:

hopper = new GraphHopperOSM().forDesktop();

and query it as before.

Problem: The two hoppers route differently. In particular, if my custom model does not contain the motorway:0 setting, it actually chooses a route which contains a significant portion of road_class motorway, despite the overall speed-cap of 50 km/h (which I don’t want and which the initial VespaFlagEncoder did not do). If on the other hand I set motorway:0, as above, I still get a deviation, this time with a shortcut along a poor surface-quality unhewn cobble street (the blue route corresponds to the VespaFlagEncoder, the red route with the suboptimal short-cut is produced from the custom model as shown above) …

Question: Now that we have most of the relevant context … What is the correct way to control, via GH yaml configs and custom model files, the exact behaviour of the VespaFlagEncoder. In other words: how do I control the maxPossibleSpeed attribute of the encoder through the model configuration? Contrary to the available documentation, the config key max_speed_fallback does not appear to achieve this, I think … or am I missing some other aspect where the two solutions deviate and which could be the cause of the differing routing behaviour …

I am aware that config files are still a beta feature in 2.3 but we would like to refrain from upgrading to 3.0 until there is a proper release.

In this case it is best to use the unreleased version 3 (e.g. 3.0-pre3). There won’t be any changes to the JSON format in 3.0 anymore and it is also better documented and tested in production. In fact, the only problem that hinders a 3.0 release is related to the custom model that we are heavily working on.

See the updated blog post with many examples and a demo server here:

Thanks you for the quick response and the reference to the blog post :slight_smile:

Is there an overview of breaking changes when going from 2.3 to the (pre-releases of) 3.0, so that we can assess what impact the migration would have elsewhere?

Otherwise we will probably simply wait until the proper release is out :man_shrugging:

We list all breaking changes that we are aware of here. The format for customizable routing is completely different now and the recommended way for server-side profiles and custom profiles per request is JSON now. Still YAML is supported for server-side profiles (without explicit docs but should be easy to translate the JSON format)

Powered by Discourse