for my app, for map rendering, i use vtm-mapsforge
and as you see on the screenshot, i have some of the roads with green-ish colors and some with red-ish.
the data is taken from the openstreetmap, and if in osm there is toll=yes, than the app shows red-ish color, like AP-66 on the image, if toll=no, or no toll field/key at all, than the map shows green-ish, as A-66 or A-60 on the image.
when it comes to routing, than my case is a bit different.
because the app is fully offline and therefore it uses graphhopper v 1.0 as the latest.
and in my app i have separation of simple toll roads (italy, france, …), and also vignette roads (cz, ch, a), that in osm are simply marked as toll roads.
i often drive through switzerland, and because their vignette is cheap (40 euro / annum), i buy the vignette after 4th december almost every year.
but when i drive, for example, from dunkerque to brig i don’t want to go through the french toll roads because they are very expensive, but i want to go through swiss motorways because i have vignette.
if i chose the simple avoid tolls setup - i would have to go through non-toll roads in france or through belgium, but also through the local roads in switzerland - no good.
so in my app i took the idea from my outdated e-class (ntg4.7) satnav that allows me to choose to avoid tolls but use vignette.
to atchieve this with gh i had to do changes in gh 1.0 source, and then during graph preparation, which covers 14 countries/regions, i simply replace toll=yes with toll=vignette for austr. switz., and czechia.
so in the gh source i did changes to:
OSMTollParser.java, where i have:
@Override
public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, boolean ferry, IntsRef relationFlags) {
if (readerWay.hasTag("toll", "vignette"))
tollEnc.setEnum(false, edgeFlags, Toll.VIGNETTE);
if (readerWay.hasTag("toll", "yes"))
tollEnc.setEnum(false, edgeFlags, Toll.ALL);
else if (readerWay.hasTag("toll:hgv", "yes"))
tollEnc.setEnum(false, edgeFlags, Toll.HGV);
else if (readerWay.hasTag("toll:N2", "yes"))
tollEnc.setEnum(false, edgeFlags, Toll.HGV);
else if (readerWay.hasTag("toll:N3", "yes"))
tollEnc.setEnum(false, edgeFlags, Toll.HGV);
return edgeFlags;
}
}
and i created
FastestNoTollsWithVignetteWeighting.java, as
package com.graphhopper.routing.weighting;
import com.graphhopper.routing.ev.EnumEncodedValue;
import com.graphhopper.routing.ev.Toll;
import com.graphhopper.routing.util.FlagEncoder;
import com.graphhopper.util.EdgeIteratorState;
import com.graphhopper.util.PMap;
public class FastestNoTollsWithVignetteWeighting extends PriorityWeighting {
private final EnumEncodedValue<Toll> tollEnc;
private static final double UNIVERSAL_TURN_PENALTY_SECONDS = 3.0;
public FastestNoTollsWithVignetteWeighting(FlagEncoder encoder, PMap map, TurnCostProvider turnCostProvider, EnumEncodedValue<Toll> tollEnc) {
super(encoder, map, turnCostProvider);
this.tollEnc = tollEnc;
}
@Override
public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
// Check if the edge is a vignette road AND vignette avoidance is enabled.
if (edgeState.get(tollEnc) == Toll.ALL) {
// Avoid vignette roads by returning an infinite weight.
return Double.POSITIVE_INFINITY;
}
// For all other roads (including regular tolls), use the standard weighting.
return super.calcEdgeWeight(edgeState, reverse);
}
@Override
public double calcTurnWeight(int inEdge, int viaNode, int outEdge) {
// 1. Ask the "Rulebook" (DefaultTurnCostProvider) for the official cost.
double basePenalty = super.calcTurnWeight(inEdge, viaNode, outEdge);
// 2. The Rulebook will report back:
// - Infinity: if the Database had a "no turn" rule.
// - A high number: for U-turns.
// - 0: if the Database had no rule for this turn.
if (Double.isInfinite(basePenalty)) {
// If the turn is forbidden, our decision is easy: it's forbidden.
return basePenalty;
}
// 3. This is our custom logic. If the turn was not forbidden,
// we add our own universal penalty to it.
// - A normal turn (basePenalty=0) becomes 20.0 seconds.
// - A U-turn (basePenalty=40) becomes 60.0 seconds.
return basePenalty + UNIVERSAL_TURN_PENALTY_SECONDS;
}
}
and
FastestNoTollsWeighting.java - similar
FastestWeighting.java
and then in GraphHopper.java i have:
if (CustomWeighting.NAME.equalsIgnoreCase(weightingStr)) {
if (!(profile instanceof CustomProfile))
throw new IllegalArgumentException("custom weighting requires a CustomProfile but was profile=" + profile.getName());
CustomModel queryCustomModel = requestHints.getObject(CustomModel.KEY, null);
CustomProfile customProfile = (CustomProfile) profile;
queryCustomModel = queryCustomModel == null ?
customProfile.getCustomModel() : CustomModel.merge(customProfile.getCustomModel(), queryCustomModel);
weighting = new CustomWeighting(encoder, encodingManager, turnCostProvider, queryCustomModel);
} else if ("shortest".equalsIgnoreCase(weightingStr)) {
weighting = new ShortestWeighting(encoder, turnCostProvider);
} else if ("fastest_no_tolls".equalsIgnoreCase(weightingStr)) {
EnumEncodedValue<Toll> tollEnc = this.encodingManager.getEnumEncodedValue(Toll.KEY, Toll.class);
weighting = new FastestNoTollsWeighting(encoder, hints, turnCostProvider, tollEnc);
} else if ("fastest_no_tolls_use_vignette".equalsIgnoreCase(weightingStr)) {
// block for weighting no tolls but use vignette
EnumEncodedValue<Toll> tollEnc = this.encodingManager.getEnumEncodedValue(Toll.KEY, Toll.class);
weighting = new FastestNoTollsWithVignetteWeighting(encoder, hints, turnCostProvider, tollEnc);
} else if ("fastest".equalsIgnoreCase(weightingStr)) {
if (encoder.supports(PriorityWeighting.class))
weighting = new PriorityWeighting(encoder, hints, turnCostProvider);
else
weighting = new FastestWeighting(encoder, hints, turnCostProvider);
} else if ("curvature".equalsIgnoreCase(weightingStr)) {
if (encoder.supports(CurvatureWeighting.class))
weighting = new CurvatureWeighting(encoder, hints, turnCostProvider);
} else if ("short_fastest".equalsIgnoreCase(weightingStr)) {
weighting = new ShortFastestWeighting(encoder, hints, turnCostProvider);
}
if (weighting == null)
throw new IllegalArgumentException("Weighting '" + weightingStr + "' not supported");
return weighting;
}
}
and in graph preparation i run the script, some part of it loops though my countries as:
osmium tags-filter -v "osmpbf/${COUNTRYS[$i]}-latest.osm.pbf" \
nw/route=ferry \
w/operator \
nw/highway=motorway,motorway_link,trunk,trunk_link,primary,primary_link,secondary,secondary_link,tertiary,tertiary_link,unclassified,residential,residential_link,living_street,escape,road,construction,give_way,mini_roundabout,motorway_junction,passing_place,parking,service,crossing,emergency_bay,phone,motorroad,mountain_pass,traffic_island,rest_area,services,traffic_signals,turning_circle,turning_loop,priority,junction,minor,unspecified,unclassified_link \
nw/tunnel w/maxspeed w/motorroad w/turn w/access w/oneway w/traffic_calming,mountain_pass r/type=restriction --overwrite -o "osmpbf-gh/${COUNTRYS[$i]}.osm.pbf"
and then replace toll=yes with toll=vignette, as:
$HOME/AndroidStudioProjects/mapfiles/.venv/bin/python3 021-002-toll-to-vignette.py osmpbf-gh/austria-cc.osm.pbf osmpbf-gh/austria-cc-v.osm.pbf
$HOME/AndroidStudioProjects/mapfiles/.venv/bin/python3 021-002-toll-to-vignette.py osmpbf-gh/czech-republic-cc.osm.pbf osmpbf-gh/czechia-cc-v.osm.pbf
$HOME/AndroidStudioProjects/mapfiles/.venv/bin/python3 021-002-toll-to-vignette.py osmpbf-gh/switzerland-cc.osm.pbf osmpbf-gh/switzerland-cc-v.osm.pbf
and then merge all the country files with osmium and then create graph with the merged file as:
graphhopper:
datareader.file: my-europe-gh.osm.pbf
graph.location: aio
# graph.dataaccess.default_type: DISK_STORE
graph.dataaccess: mmap_byte_buffer
# car : will use all toll roads and vignette roads
# car_no_tolls_use_vignette : will avoid tolls but will use vignette roads (ch + at)
# car_no_tolls_no_vignette : will avoid tolls and will avoid vignette roads
profiles:
- name: car
vehicle: car
turn_costs: true
weighting: fastest
- name: car_no_tolls_use_vignette
vehicle: car
turn_costs: true
weighting: fastest_no_tolls_use_vignette
- name: car_no_tolls_no_vignette
vehicle: car
turn_costs: true
weighting: fastest_no_tolls
- name: car_shortest
vehicle: car
turn_costs: true
weighting: shortest
graph.encoded_values: toll,max_speed
graph.flag_encoders: car|turn_costs=true
profiles_ch:
- profile: car
- profile: car_no_tolls_use_vignette
- profile: car_no_tolls_no_vignette
- profile: car_shortest
# profiles_lm: []
prepare.ch.threads: 4
prepare.min_network_size: 600
prepare.min_one_way_network_size: 600
# routing.ch.disabling_allowed: false
import.osm.ignored_highways: footway,construction,cycleway,path,pedestrian,steps,elevator,street_lamp,bus_stop,speed_camera
# graph.do_sort: true
# Dropwizard server configuration
server:
application_connectors:
- type: http
port: 8989
# for security reasons bind to localhost
bind_host: localhost
request_log:
appenders: []
admin_connectors:
- type: http
port: 8990
bind_host: localhost
and it works very well