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

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

Go to download

GraphHopper is a fast and memory efficient Java road routing engine working seamlessly with OpenStreetMap data.

There is a newer version: 10.0
Show newest version
/*
 *  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.weighting.TurnCostProvider;
import com.graphhopper.routing.weighting.Weighting;
import com.graphhopper.util.CustomModel;
import com.graphhopper.util.EdgeIteratorState;

import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER;

/**
 * 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 implements Weighting { 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 double distanceInfluence; private final double headingPenaltySeconds; private final EdgeToDoubleMapping edgeToSpeedMapping; private final EdgeToDoubleMapping edgeToPriorityMapping; private final TurnCostProvider turnCostProvider; private final MaxCalc maxPrioCalc; private final MaxCalc maxSpeedCalc; public CustomWeighting(TurnCostProvider turnCostProvider, Parameters parameters) { if (!Weighting.isValidName(getName())) throw new IllegalStateException("Not a valid name for a Weighting: " + getName()); this.turnCostProvider = turnCostProvider; this.edgeToSpeedMapping = parameters.getEdgeToSpeedMapping(); this.maxSpeedCalc = parameters.getMaxSpeedCalc(); this.edgeToPriorityMapping = parameters.getEdgeToPriorityMapping(); this.maxPrioCalc = parameters.getMaxPrioCalc(); this.headingPenaltySeconds = parameters.getHeadingPenaltySeconds(); // 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 calcMinWeightPerDistance() { return 1d / (maxSpeedCalc.calcMax() / SPEED_CONV) / maxPrioCalc.calcMax() + distanceInfluence; } @Override public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { double priority = edgeToPriorityMapping.get(edgeState, reverse); if (priority == 0) return Double.POSITIVE_INFINITY; final double distance = edgeState.getDistance(); double seconds = calcSeconds(distance, edgeState, reverse); if (Double.isInfinite(seconds)) return Double.POSITIVE_INFINITY; // add penalty at start/stop/via points if (edgeState.get(EdgeIteratorState.UNFAVORED_EDGE)) seconds += headingPenaltySeconds; double distanceCosts = distance * distanceInfluence; if (Double.isInfinite(distanceCosts)) return Double.POSITIVE_INFINITY; return seconds / priority + distanceCosts; } double calcSeconds(double distance, EdgeIteratorState edgeState, boolean reverse) { double speed = edgeToSpeedMapping.get(edgeState, reverse); if (speed == 0) return Double.POSITIVE_INFINITY; if (speed < 0) throw new IllegalArgumentException("Speed cannot be negative"); return distance / speed * SPEED_CONV; } @Override public long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) { return Math.round(calcSeconds(edgeState.getDistance(), edgeState, reverse) * 1000); } @Override public double calcTurnWeight(int inEdge, int viaNode, int outEdge) { return turnCostProvider.calcTurnWeight(inEdge, viaNode, outEdge); } @Override public long calcTurnMillis(int inEdge, int viaNode, int outEdge) { return turnCostProvider.calcTurnMillis(inEdge, viaNode, outEdge); } @Override public boolean hasTurnCosts() { return turnCostProvider != NO_TURN_COST_PROVIDER; } @Override public String getName() { return NAME; } @FunctionalInterface public interface EdgeToDoubleMapping { double get(EdgeIteratorState edge, boolean reverse); } @FunctionalInterface public interface MaxCalc { double calcMax(); } public static class Parameters { private final EdgeToDoubleMapping edgeToSpeedMapping; private final EdgeToDoubleMapping edgeToPriorityMapping; private final MaxCalc maxSpeedCalc; private final MaxCalc maxPrioCalc; private final double distanceInfluence; private final double headingPenaltySeconds; public Parameters(EdgeToDoubleMapping edgeToSpeedMapping, MaxCalc maxSpeedCalc, EdgeToDoubleMapping edgeToPriorityMapping, MaxCalc maxPrioCalc, double distanceInfluence, double headingPenaltySeconds) { this.edgeToSpeedMapping = edgeToSpeedMapping; this.maxSpeedCalc = maxSpeedCalc; this.edgeToPriorityMapping = edgeToPriorityMapping; this.maxPrioCalc = maxPrioCalc; this.distanceInfluence = distanceInfluence; this.headingPenaltySeconds = headingPenaltySeconds; } public EdgeToDoubleMapping getEdgeToSpeedMapping() { return edgeToSpeedMapping; } public EdgeToDoubleMapping getEdgeToPriorityMapping() { return edgeToPriorityMapping; } public MaxCalc getMaxSpeedCalc() { return maxSpeedCalc; } public MaxCalc getMaxPrioCalc() { return maxPrioCalc; } public double getDistanceInfluence() { return distanceInfluence; } public double getHeadingPenaltySeconds() { return headingPenaltySeconds; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy