HeavyVehicleFlagEncoder.java
/* This file is part of Openrouteservice.
*
* Openrouteservice is free software; you can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License along with this library;
* if not, see <https://www.gnu.org/licenses/>.
*/
package org.heigit.ors.routing.graphhopper.extensions.flagencoders;
import com.graphhopper.reader.ReaderWay;
import com.graphhopper.routing.ev.DecimalEncodedValue;
import com.graphhopper.routing.ev.EncodedValue;
import com.graphhopper.routing.ev.UnsignedDecimalEncodedValue;
import com.graphhopper.routing.util.EncodingManager;
import com.graphhopper.routing.util.TransportationMode;
import com.graphhopper.routing.util.parsers.helpers.OSMValueExtractor;
import com.graphhopper.routing.weighting.PriorityWeighting;
import com.graphhopper.storage.IntsRef;
import com.graphhopper.util.Helper;
import com.graphhopper.util.PMap;
import org.heigit.ors.routing.graphhopper.extensions.util.PriorityCode;
import java.util.*;
import static com.graphhopper.routing.util.EncodingManager.getKey;
public class HeavyVehicleFlagEncoder extends VehicleFlagEncoder {
public static final String VAL_DESIGNATED = "designated";
public static final String VAL_AGRICULTURAL = "agricultural";
public static final String VAL_FORESTRY = "forestry";
public static final String VAL_GOODS = "goods";
public static final String KEY_HIGHWAY = "highway";
public static final String VAL_TRACK = "track";
public static final String KEY_IMPASSABLE = "impassable";
protected final HashSet<String> forwardKeys = new HashSet<>(5);
protected final HashSet<String> backwardKeys = new HashSet<>(5);
protected final List<String> hgvAccess = new ArrayList<>(5);
private static final int MEAN_SPEED = 70;
// Encoder for storing whether the edge is on a preferred way
private DecimalEncodedValue priorityWayEncoder;
/**
* Should be only instantied via EncodingManager
*/
public HeavyVehicleFlagEncoder() {
this(5, 5, 0);
}
public HeavyVehicleFlagEncoder(PMap properties) {
this(properties.getInt("speed_bits", 5),
properties.getDouble("speed_factor", 5),
properties.getBool("turn_costs", false) ? 3 : 0);
setProperties(properties);
maxTrackGradeLevel = properties.getInt("maximum_grade_level", 1);
}
public HeavyVehicleFlagEncoder(int speedBits, double speedFactor, int maxTurnCosts) {
super(speedBits, speedFactor, maxTurnCosts);
maxPossibleSpeed = 90;
intendedValues.add(VAL_DESIGNATED);
intendedValues.add(VAL_AGRICULTURAL);
intendedValues.add(VAL_FORESTRY);
intendedValues.add("delivery");
intendedValues.add("bus");
intendedValues.add("hgv");
intendedValues.add(VAL_GOODS);
hgvAccess.addAll(Arrays.asList("hgv", VAL_GOODS, "bus", VAL_AGRICULTURAL, VAL_FORESTRY, "delivery"));
// Override default speeds with lower values
trackTypeSpeedMap.put("grade1", 40); // paved
trackTypeSpeedMap.put("grade2", 30); // now unpaved - gravel mixed with ...
trackTypeSpeedMap.put("grade3", 20); // ... hard and soft materials
trackTypeSpeedMap.put("grade4", 15); // ... some hard or compressed materials
trackTypeSpeedMap.put("grade5", 10); // ... no hard materials. soil/sand/grass
// autobahn
defaultSpeedMap.put("motorway", 80);
defaultSpeedMap.put("motorway_link", 50);
defaultSpeedMap.put("motorroad", 80);
// bundesstraße
defaultSpeedMap.put("trunk", 80);
defaultSpeedMap.put("trunk_link", 50);
// linking bigger town
defaultSpeedMap.put("primary", 60);
initSpeedLimitHandler(this.toString());
forwardKeys.add("goods:forward");
forwardKeys.add("hgv:forward");
forwardKeys.add("bus:forward");
forwardKeys.add("agricultural:forward");
forwardKeys.add("forestry:forward");
forwardKeys.add("delivery:forward");
backwardKeys.add("goods:backward");
backwardKeys.add("hgv:backward");
backwardKeys.add("bus:backward");
backwardKeys.add("agricultural:backward");
backwardKeys.add("forestry:backward");
backwardKeys.add("delivery:backward");
}
@Override
public void createEncodedValues(List<EncodedValue> registerNewEncodedValue, String prefix, int index) {
super.createEncodedValues(registerNewEncodedValue, prefix, index);
priorityWayEncoder = new UnsignedDecimalEncodedValue(getKey(prefix, "priority"), 4, PriorityCode.getFactor(1), false);
registerNewEncodedValue.add(priorityWayEncoder);
}
@Override
public double getMaxSpeed(ReaderWay way) {
double maxSpeed = OSMValueExtractor.stringToKmh(way.getTag("maxspeed:hgv"));
double fwdSpeed = OSMValueExtractor.stringToKmh(way.getTag("maxspeed:hgv:forward"));
if (isValidSpeed(fwdSpeed) && (!isValidSpeed(maxSpeed) || fwdSpeed < maxSpeed)) {
maxSpeed = fwdSpeed;
}
double backSpeed = OSMValueExtractor.stringToKmh(way.getTag("maxspeed:hgv:backward"));
if (isValidSpeed(backSpeed) && (!isValidSpeed(maxSpeed) || backSpeed < maxSpeed)) {
maxSpeed = backSpeed;
}
if (!isValidSpeed(maxSpeed)) {
maxSpeed = super.getMaxSpeed(way);
if (isValidSpeed(maxSpeed)) {
String highway = way.getTag(KEY_HIGHWAY);
if (!Helper.isEmpty(highway)) {
double defaultSpeed = speedLimitHandler.getSpeed(highway);
if (defaultSpeed < maxSpeed)
maxSpeed = defaultSpeed;
}
}
}
return maxSpeed;
}
@Override
protected String getHighway(ReaderWay way) {
return way.getTag(KEY_HIGHWAY);
}
@Override
public EncodingManager.Access getAccess(ReaderWay way) {
String highwayValue = way.getTag(KEY_HIGHWAY);
String[] restrictionValues = way.getFirstPriorityTagValues(restrictions);
if (highwayValue == null) {
if (way.hasTag("route", ferries)) {
for (String restrictionValue : restrictionValues) {
if (restrictedValues.contains(restrictionValue))
return EncodingManager.Access.CAN_SKIP;
if (intendedValues.contains(restrictionValue) ||
// implied default is allowed only if foot and bicycle is not specified:
restrictionValue.isEmpty() && !way.hasTag("foot") && !way.hasTag("bicycle"))
return EncodingManager.Access.FERRY;
}
}
return EncodingManager.Access.CAN_SKIP;
}
if (VAL_TRACK.equals(highwayValue)) {
String tt = way.getTag("tracktype");
int grade = getTrackGradeLevel(tt);
if (grade > maxTrackGradeLevel)
return EncodingManager.Access.CAN_SKIP;
}
if (!speedLimitHandler.hasSpeedValue(highwayValue))
return EncodingManager.Access.CAN_SKIP;
if (way.hasTag(KEY_IMPASSABLE, "yes") || way.hasTag("status", KEY_IMPASSABLE) || way.hasTag("smoothness", KEY_IMPASSABLE))
return EncodingManager.Access.CAN_SKIP;
// multiple restrictions needs special handling compared to foot and bike, see also motorcycle
for (String restrictionValue : restrictionValues) {
if (!restrictionValue.isEmpty()) {
if (restrictedValues.contains(restrictionValue) && !getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way))
return EncodingManager.Access.CAN_SKIP;
if (intendedValues.contains(restrictionValue))
return EncodingManager.Access.WAY;
}
}
// do not drive street cars into fords
boolean carsAllowed = way.hasTag(restrictions, intendedValues);
if (isBlockFords() && ("ford".equals(highwayValue) || way.hasTag("ford")) && !carsAllowed)
return EncodingManager.Access.CAN_SKIP;
// check access restrictions
// filter special type of access for hgv
if (way.hasTag(restrictions, restrictedValues) && !carsAllowed && !way.hasTag(hgvAccess, intendedValues)) {
return EncodingManager.Access.CAN_SKIP;
}
String maxwidth = way.getTag("maxwidth"); // Runge added on 23.02.2016
if (maxwidth != null) {
try {
double mwv = Double.parseDouble(maxwidth);
if (mwv < 2.0)
return EncodingManager.Access.CAN_SKIP;
} catch (Exception ex) {
// do nothing
}
}
if (getConditionalTagInspector().isPermittedWayConditionallyRestricted(way))
return EncodingManager.Access.CAN_SKIP;
else
return EncodingManager.Access.WAY;
}
@Override
public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, EncodingManager.Access access, long relationFlags) {
super.handleWayTags(edgeFlags, way, access, relationFlags);
priorityWayEncoder.setDecimal(false, edgeFlags, PriorityCode.getFactor(handlePriority(way)));
return edgeFlags;
}
protected int handlePriority(ReaderWay way) {
TreeMap<Double, Integer> weightToPrioMap = new TreeMap<>();
collect(way, weightToPrioMap);
// pick priority with biggest order value
return weightToPrioMap.lastEntry().getValue();
}
/**
* @param weightToPrioMap associate a weight with every priority. This sorted map allows
* subclasses to 'insert' more important priorities as well as
* overwrite determined priorities.
*/
protected void collect(ReaderWay way, TreeMap<Double, Integer> weightToPrioMap) { // Runge
if (way.hasTag("hgv", VAL_DESIGNATED) || (way.hasTag("access", VAL_DESIGNATED) && (way.hasTag(VAL_GOODS, "yes") || way.hasTag("hgv", "yes") || way.hasTag("bus", "yes") || way.hasTag(VAL_AGRICULTURAL, "yes") || way.hasTag(VAL_FORESTRY, "yes"))))
weightToPrioMap.put(100d, PriorityCode.BEST.getValue());
else {
String highway = way.getTag(KEY_HIGHWAY);
double maxSpeed = getMaxSpeed(way);
if (!Helper.isEmpty(highway)) {
switch (highway) {
case "motorway":
case "motorway_link":
case "trunk":
case "trunk_link":
weightToPrioMap.put(100d, PriorityCode.BEST.getValue());
break;
case "primary":
case "primary_link":
case "secondary":
case "secondary_link":
weightToPrioMap.put(100d, PriorityCode.PREFER.getValue());
break;
case "tertiary":
case "tertiary_link":
weightToPrioMap.put(100d, PriorityCode.UNCHANGED.getValue());
break;
case "residential":
case "service":
case "road":
case "unclassified":
if (isValidSpeed(maxSpeed) && maxSpeed <= 30) {
weightToPrioMap.put(120d, PriorityCode.REACH_DEST.getValue());
} else {
weightToPrioMap.put(100d, PriorityCode.AVOID_IF_POSSIBLE.getValue());
}
break;
case "living_street":
weightToPrioMap.put(100d, PriorityCode.AVOID_IF_POSSIBLE.getValue());
break;
case VAL_TRACK:
weightToPrioMap.put(100d, PriorityCode.REACH_DEST.getValue());
break;
default:
weightToPrioMap.put(40d, PriorityCode.AVOID_IF_POSSIBLE.getValue());
break;
}
} else {
weightToPrioMap.put(100d, PriorityCode.UNCHANGED.getValue());
}
if (isValidSpeed(maxSpeed)) {
// We assume that the given road segment goes through a settlement.
if (maxSpeed <= 40)
weightToPrioMap.put(110d, PriorityCode.AVOID_IF_POSSIBLE.getValue());
else if (maxSpeed <= 50)
weightToPrioMap.put(110d, PriorityCode.UNCHANGED.getValue());
}
}
}
@Override
public boolean supports(Class<?> feature) {
if (super.supports(feature))
return true;
return PriorityWeighting.class.isAssignableFrom(feature);
}
public double getMeanSpeed() {
return MEAN_SPEED;
}
@Override
public String toString() {
return FlagEncoderNames.HEAVYVEHICLE;
}
@Override
public TransportationMode getTransportationMode() {
return TransportationMode.HGV;
}
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final HeavyVehicleFlagEncoder other = (HeavyVehicleFlagEncoder) obj;
return toString().equals(other.toString());
}
@Override
public int hashCode() {
return ("HeavyVehicleFlagEncoder" + this).hashCode();
}
}