All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.graphhopper.routing.weighting.custom.CustomWeighting Maven / Gradle / Ivy

/*
 *  Licensed to GraphHopper GmbH under one or more contributor
 *  license agreements. See the NOTICE file distributed with this work for
 *  additional information regarding copyright ownership.
 *
 *  GraphHopper GmbH licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except in
 *  compliance with the License. You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.graphhopper.routing.weighting.custom;

import com.graphhopper.routing.ev.BooleanEncodedValue;
import com.graphhopper.routing.util.FlagEncoder;
import com.graphhopper.routing.weighting.AbstractWeighting;
import com.graphhopper.routing.weighting.TurnCostProvider;
import com.graphhopper.util.CustomModel;
import com.graphhopper.util.EdgeIteratorState;

/**
 * The CustomWeighting allows adjusting the edge weights relative to those we'd obtain for a given base flag encoder.
 * For example a car flag encoder already provides speeds and access flags for every edge depending on certain edge
 * properties. By default the CustomWeighting simply makes use of these values, but it is possible to adjust them by
 * setting up rules that apply changes depending on the edges' encoded values.
 * 

* The formula for the edge weights is as follows: *

* weight = distance/speed + distance_costs + stress_costs *

* The first term simply corresponds to the time it takes to travel along the edge. * The second term adds a fixed per-distance cost that is proportional to the distance but *independent* of the edge * properties, i.e. it reads *

* distance_costs = distance * distance_influence *

* The third term is also proportional to the distance but compared to the second it describes additional costs that *do* * depend on the edge properties. It can represent any kind of costs that depend on the edge (like inconvenience or * dangers encountered on 'high-stress' roads for bikes, toll roads (because they cost money), stairs (because they are * awkward when going by bike) etc.). This 'stress' term reads *

* stress_costs = distance * stress_per_meter *

* and just like the distance term it describes costs measured in seconds. When modelling it, one always has to 'convert' * the costs into some time equivalent (e.g. for toll roads one has to think about how much money can be spent to save * a certain amount of time). Note that the distance_costs described by the second term in general cannot be properly * described by the stress costs, because the distance term allows increasing the per-distance costs per-se (regardless * of the type of the road). Also note that both the second and third term are different to the first in that they can * increase the edge costs but do *not* modify the travel *time*. *

* Instead of letting you set the speed directly, `CustomWeighting` allows changing the speed relative to the speed we * get from the base flag encoder. The stress costs can be specified by using a factor between 0 and 1 that is called * 'priority'. *

* Therefore the full edge weight formula reads: *

 * weight = distance / (base_speed * speed_factor * priority)
 *        + distance * distance_influence
 * 
*

* The open parameters that we can adjust are therefore: speed_factor, priority and distance_influence and they are * specified via the `{@link CustomModel}`. The speed can also be restricted to a maximum value, in which case the value * calculated via the speed_factor is simply overwritten. Edges that are not accessible according to the access flags of * the base vehicle always get assigned an infinite weight and this cannot be changed (yet) using this weighting. */ public final class CustomWeighting extends AbstractWeighting { public static final String NAME = "custom"; /** * Converting to seconds is not necessary but makes adding other penalties easier (e.g. turn * costs or traffic light costs etc) */ private final static double SPEED_CONV = 3.6; private final BooleanEncodedValue baseVehicleAccessEnc; private final double maxSpeed; private final double distanceInfluence; private final double headingPenaltySeconds; private final EdgeToDoubleMapping edgeToSpeedMapping; private final EdgeToDoubleMapping edgeToPriorityMapping; public CustomWeighting(FlagEncoder baseFlagEncoder, TurnCostProvider turnCostProvider, Parameters parameters) { super(baseFlagEncoder, turnCostProvider); this.edgeToSpeedMapping = parameters.getEdgeToSpeedMapping(); this.edgeToPriorityMapping = parameters.getEdgeToPriorityMapping(); this.baseVehicleAccessEnc = baseFlagEncoder.getAccessEnc(); this.headingPenaltySeconds = parameters.getHeadingPenaltySeconds(); this.maxSpeed = parameters.getMaxSpeed() / SPEED_CONV; // given unit is s/km -> convert to s/m this.distanceInfluence = parameters.getDistanceInfluence() / 1000.0; if (this.distanceInfluence < 0) throw new IllegalArgumentException("distance_influence cannot be negative " + this.distanceInfluence); } @Override public double getMinWeight(double distance) { return distance / maxSpeed + distance * distanceInfluence; } @Override public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { final double distance = edgeState.getDistance(); double seconds = calcSeconds(distance, edgeState, reverse); if (Double.isInfinite(seconds)) return Double.POSITIVE_INFINITY; double distanceCosts = distance * distanceInfluence; if (Double.isInfinite(distanceCosts)) return Double.POSITIVE_INFINITY; double priority = edgeToPriorityMapping.get(edgeState, reverse); return seconds / priority + distanceCosts; } double calcSeconds(double distance, EdgeIteratorState edgeState, boolean reverse) { // special case for loop edges: since they do not have a meaningful direction we always need to read them in forward direction if (edgeState.getBaseNode() == edgeState.getAdjNode()) reverse = false; // TODO see #1835 if (reverse ? !edgeState.getReverse(baseVehicleAccessEnc) : !edgeState.get(baseVehicleAccessEnc)) return Double.POSITIVE_INFINITY; double speed = edgeToSpeedMapping.get(edgeState, reverse); if (speed > maxSpeed * SPEED_CONV) throw new IllegalStateException("for " + getName() + " speed <= maxSpeed is violated, " + speed + " <= " + maxSpeed * SPEED_CONV); if (speed == 0) return Double.POSITIVE_INFINITY; if (speed < 0) throw new IllegalArgumentException("Speed cannot be negative"); double seconds = distance / speed * SPEED_CONV; // add penalty at start/stop/via points return edgeState.get(EdgeIteratorState.UNFAVORED_EDGE) ? seconds + headingPenaltySeconds : seconds; } @Override public long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) { return Math.round(calcSeconds(edgeState.getDistance(), edgeState, reverse) * 1000); } @Override public String getName() { return NAME; } @FunctionalInterface public interface EdgeToDoubleMapping { double get(EdgeIteratorState edge, boolean reverse); } static class Parameters { private final EdgeToDoubleMapping edgeToSpeedMapping; private final EdgeToDoubleMapping edgeToPriorityMapping; private final double maxSpeed; private final double distanceInfluence; private final double headingPenaltySeconds; Parameters(EdgeToDoubleMapping edgeToSpeedMapping, EdgeToDoubleMapping edgeToPriorityMapping, double maxSpeed, double distanceInfluence, double headingPenaltySeconds) { this.edgeToSpeedMapping = edgeToSpeedMapping; this.edgeToPriorityMapping = edgeToPriorityMapping; this.maxSpeed = maxSpeed; this.distanceInfluence = distanceInfluence; this.headingPenaltySeconds = headingPenaltySeconds; } public EdgeToDoubleMapping getEdgeToSpeedMapping() { return edgeToSpeedMapping; } public EdgeToDoubleMapping getEdgeToPriorityMapping() { return edgeToPriorityMapping; } public double getMaxSpeed() { return maxSpeed; } public double getDistanceInfluence() { return distanceInfluence; } public double getHeadingPenaltySeconds() { return headingPenaltySeconds; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy