EmergencyFlagEncoder.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.util.EncodingManager;
import com.graphhopper.routing.util.TransportationMode;
import com.graphhopper.routing.util.parsers.helpers.OSMValueExtractor;
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.*;
public class EmergencyFlagEncoder extends VehicleFlagEncoder {
private static final double MEAN_SPEED = 80;
public static final String KEY_AGRICULTURAL = "agricultural";
public static final String KEY_MOTORWAY_LINK = "motorway_link";
public static final String KEY_FORESTRY = "forestry";
public static final String KEY_MOTORWAY = "motorway";
public static final String KEY_MOTORROAD = "motorroad";
public static final String KEY_SERVICE = "service";
public static final String KEY_TRACK = "track";
public static final String KEY_HIGHWAY = "highway";
protected final HashSet<String> forwardKeys = new HashSet<>(5);
protected final HashSet<String> backwardKeys = new HashSet<>(5);
protected final HashSet<String> noValues = new HashSet<>(5);
protected final HashSet<String> yesValues = new HashSet<>(5);
protected final List<String> hgvAccess = new ArrayList<>(5);
public double getMeanSpeed() {
return MEAN_SPEED;
}
public EmergencyFlagEncoder(PMap properties) {
this(properties.getInt("speed_bits", 5),
properties.getDouble("speed_factor", 5),
properties.getBool("turn_costs", false) ? 3 : 0);
blockFords(false);
}
public EmergencyFlagEncoder(int speedBits, double speedFactor, int maxTurnCosts) {
super(speedBits, speedFactor, maxTurnCosts);
restrictions.addAll(Arrays.asList("motorcar", "motor_vehicle", "vehicle", "access"));
restrictedValues.add("private");
restrictedValues.add(KEY_AGRICULTURAL);
restrictedValues.add(KEY_FORESTRY);
restrictedValues.add("no");
restrictedValues.add("restricted");
restrictedValues.add("delivery");
intendedValues.add("yes");
intendedValues.add("permissive");
hgvAccess.addAll(Arrays.asList("hgv", "goods", "bus", KEY_AGRICULTURAL, KEY_FORESTRY, "delivery"));
passByDefaultBarriers.add("gate");
passByDefaultBarriers.add("lift_gate");
passByDefaultBarriers.add("kissing_gate");
passByDefaultBarriers.add("swing_gate");
blockByDefaultBarriers.add("bollard");
blockByDefaultBarriers.add("stile");
blockByDefaultBarriers.add("turnstile");
blockByDefaultBarriers.add("cycle_barrier");
blockByDefaultBarriers.add("motorcycle_barrier");
blockByDefaultBarriers.add("block");
Map<String, Integer> trackTypeSpeedMap = new HashMap<>();
trackTypeSpeedMap.put("grade1", 25); // paved
trackTypeSpeedMap.put("grade2", 15); // now unpaved - gravel mixed with ...
trackTypeSpeedMap.put("grade3", 15); // ... hard and soft materials
trackTypeSpeedMap.put("grade4", 10); // ... some hard or compressed materials
trackTypeSpeedMap.put("grade5", 5); // ... no hard materials. soil/sand/grass
Map<String, Integer> badSurfaceSpeedMap = new HashMap<>();
badSurfaceSpeedMap.put("asphalt", -1);
badSurfaceSpeedMap.put("concrete", -1);
badSurfaceSpeedMap.put("concrete:plates", -1);
badSurfaceSpeedMap.put("concrete:lanes", -1);
badSurfaceSpeedMap.put("paved", -1);
badSurfaceSpeedMap.put("cement", 80);
badSurfaceSpeedMap.put("compacted", 80);
badSurfaceSpeedMap.put("fine_gravel", 60);
badSurfaceSpeedMap.put("paving_stones", 40);
badSurfaceSpeedMap.put("metal", 40);
badSurfaceSpeedMap.put("bricks", 40);
badSurfaceSpeedMap.put("grass", 30);
badSurfaceSpeedMap.put("wood", 30);
badSurfaceSpeedMap.put("sett", 30);
badSurfaceSpeedMap.put("grass_paver", 30);
badSurfaceSpeedMap.put("gravel", 30);
badSurfaceSpeedMap.put("unpaved", 30);
badSurfaceSpeedMap.put("ground", 30);
badSurfaceSpeedMap.put("dirt", 30);
badSurfaceSpeedMap.put("pebblestone", 30);
badSurfaceSpeedMap.put("tartan", 30);
badSurfaceSpeedMap.put("cobblestone", 20);
badSurfaceSpeedMap.put("clay", 20);
badSurfaceSpeedMap.put("earth", 15);
badSurfaceSpeedMap.put("stone", 15);
badSurfaceSpeedMap.put("rocky", 15);
badSurfaceSpeedMap.put("sand", 15);
badSurfaceSpeedMap.put("mud", 10);
badSurfaceSpeedMap.put("unknown", 30);
// limit speed on bad surfaces to 30 km/h
badSurfaceSpeed = 30;
maxPossibleSpeed = 140;
Map<String, Integer> defaultSpeedMap = new HashMap<>();
// autobahn
defaultSpeedMap.put(KEY_MOTORWAY, 130);
defaultSpeedMap.put(KEY_MOTORWAY_LINK, 50);
defaultSpeedMap.put(KEY_MOTORROAD, 130);
// bundesstraße
defaultSpeedMap.put("trunk", 120);
defaultSpeedMap.put("trunk_link", 50);
// linking bigger town
defaultSpeedMap.put("primary", 120);
defaultSpeedMap.put("primary_link", 50);
// linking towns + villages
defaultSpeedMap.put("secondary", 120);
defaultSpeedMap.put("secondary_link", 50);
// streets without middle line separation
defaultSpeedMap.put("tertiary", 110);
defaultSpeedMap.put("tertiary_link", 50);
defaultSpeedMap.put("unclassified", 60);
defaultSpeedMap.put("residential", 50);
// spielstraße
defaultSpeedMap.put("living_street", 20);
defaultSpeedMap.put(KEY_SERVICE, 20);
// unknown road
defaultSpeedMap.put("road", 20);
// forestry stuff
defaultSpeedMap.put(KEY_TRACK, 15);
// additional available for emergency
defaultSpeedMap.put("raceway", 100);
defaultSpeedMap.put("cycleway", 10);
// how to declare this ?
defaultSpeedMap.put("aeroway=runway", 100);
defaultSpeedMap.put("aeroway=taxilane", 100);
// FIXME: allow highway=footway, pedestrian
speedLimitHandler = new SpeedLimitHandler(this.toString(), defaultSpeedMap, badSurfaceSpeedMap, trackTypeSpeedMap);
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");
noValues.add("no");
noValues.add("-1");
yesValues.add("yes");
yesValues.add("1");
}
@Override
protected double applyMaxSpeed(ReaderWay way, double speed) {
double maxSpeed = getMaxSpeed(way);
// We obay speed limits
if (maxSpeed >= 0) {
return maxSpeed;
}
return speed;
}
@Override
public double getMaxSpeed(ReaderWay way) { // runge
String maxspeedTag = way.getTag("maxspeed:hgv");
if (Helper.isEmpty(maxspeedTag))
maxspeedTag = way.getTag("maxspeed");
double maxSpeed = OSMValueExtractor.stringToKmh(maxspeedTag);
String highway = way.getTag(KEY_HIGHWAY);
double defaultSpeed = speedLimitHandler.getSpeed(highway);
if (defaultSpeed < maxSpeed) // TODO
maxSpeed = defaultSpeed;
return maxSpeed;
}
@Override
protected double getSpeed(ReaderWay way) {
String highwayValue = way.getTag(KEY_HIGHWAY);
if (!Helper.isEmpty(highwayValue) && way.hasTag(KEY_MOTORROAD, "yes")
&& !highwayValue.equals(KEY_MOTORWAY) && !highwayValue.equals(KEY_MOTORWAY_LINK)) {
highwayValue = KEY_MOTORROAD;
}
Integer speed = speedLimitHandler.getSpeed(highwayValue);
if (speed == null)
throw new IllegalStateException(this + ", no speed found for: " + highwayValue + ", tags: " + way);
if (highwayValue.equals(KEY_TRACK)) {
String tt = way.getTag("tracktype");
if (!Helper.isEmpty(tt)) {
Integer tInt = speedLimitHandler.getTrackTypeSpeed(tt); // FIXME
if (tInt != null && tInt != -1)
speed = tInt;
}
}
return speed;
}
@Override
public EncodingManager.Access getAccess(ReaderWay way) {
String highwayValue = way.getTag(KEY_HIGHWAY);
if (highwayValue == null) {
if (way.hasTag("route", ferries)) {
String motorcarTag = way.getTag("motorcar");
if (motorcarTag == null)
motorcarTag = way.getTag("motor_vehicle");
if (motorcarTag == null && !way.hasTag("foot") && !way.hasTag("bicycle") || "yes".equals(motorcarTag))
return EncodingManager.Access.FERRY;
}
return EncodingManager.Access.CAN_SKIP;
}
if (!speedLimitHandler.hasSpeedValue(highwayValue))
return EncodingManager.Access.CAN_SKIP;
if (way.hasTag("impassable", "yes") || way.hasTag("status", "impassable"))
return EncodingManager.Access.CAN_SKIP;
// do not drive street cars into fords
if (isBlockFords() && ("ford".equals(highwayValue) || way.hasTag("ford")))
return EncodingManager.Access.CAN_SKIP;
// check access restrictions
// Amandus
if (way.hasTag("lanes:psv") || way.hasTag("lanes:bus") || way.hasTag("lanes:taxi") || way.hasTag("busway, lane") || way.hasTag("busway:left, lane") || way.hasTag("busway:right, lane"))
return EncodingManager.Access.WAY; // TODO: this result is equal to the final return; can the if be removed?
return EncodingManager.Access.WAY;
}
@Override
public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, EncodingManager.Access access, long relationFlags) {
if (access.canSkip())
return edgeFlags;
if (!access.isFerry()) {
// get assumed speed from highway type
double speed = getSpeed(way);
speed = applyMaxSpeed(way, speed);
speed = getSurfaceSpeed(way, speed);
boolean isRoundabout = way.hasTag("junction", "roundabout");
if (isRoundabout)
roundaboutEnc.setBool(false, edgeFlags, true);
setSpeed(false, edgeFlags, speed);
setSpeed(true, edgeFlags, speed);
if ((isOneway(way) || isRoundabout) && speed > 80) {
if (isForwardOneway(way))
accessEnc.setBool(false, edgeFlags, true);
if (isBackwardOneway(way))
accessEnc.setBool(true, edgeFlags, true);
} else {
accessEnc.setBool(false, edgeFlags, true);
accessEnc.setBool(true, edgeFlags, true);
}
} else {
double ferrySpeed = ferrySpeedCalc.getSpeed(way);
accessEnc.setBool(false, edgeFlags, true);
accessEnc.setBool(true, edgeFlags, true);
setSpeed(false, edgeFlags, ferrySpeed);
setSpeed(true, edgeFlags, ferrySpeed);
}
return edgeFlags;
}
/**
* @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", "designated") || (way.hasTag("access", "designated") && (way.hasTag("goods", "yes") || way.hasTag("hgv", "yes") || way.hasTag("bus", "yes") || way.hasTag(KEY_AGRICULTURAL, "yes") || way.hasTag(KEY_FORESTRY, "yes"))))
weightToPrioMap.put(100d, PriorityCode.BEST.getValue());
// Amandus
else if (way.hasTag(KEY_HIGHWAY, KEY_SERVICE) && way.hasTag(KEY_SERVICE, "emergency_access"))
weightToPrioMap.put(100d, PriorityCode.BEST.getValue());
else {
// Amandus
String busway = way.getTag("busway");// FIXME || way.getTag("busway:right") || way.getTag("busway:left")
if (!Helper.isEmpty(busway) && "lane".equals(busway))
weightToPrioMap.put(10d, PriorityCode.PREFER.getValue());
String highway = way.getTag(KEY_HIGHWAY);
double maxSpeed = getMaxSpeed(way);
if (!Helper.isEmpty(highway)) {
switch (highway) {
case KEY_MOTORWAY:
case KEY_MOTORWAY_LINK:
case "trunk":
case "trunk_link":
weightToPrioMap.put(100d, PriorityCode.BEST.getValue());
break;
case "primary":
case "primary_link":
weightToPrioMap.put(100d, PriorityCode.PREFER.getValue());
break;
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 KEY_SERVICE:
case "road":
case "unclassified":
if (maxSpeed > 0 && 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 KEY_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 (maxSpeed > 0) {
// 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 equals(Object obj) {
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final EmergencyFlagEncoder other = (EmergencyFlagEncoder) obj;
return toString().equals(other.toString());
}
@Override
public int hashCode() {
return ("EmergencyFlagEncoder" + this).hashCode();
}
@Override
public String toString() {
return FlagEncoderNames.EMERGENCY;
}
@Override
public TransportationMode getTransportationMode() {
return TransportationMode.PSV;
}
}