Skip to content

feat: improvements to road bike routing #2076

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Releasing is documented in RELEASE.md
### Added

### Changed
- improve road bike average speeds to be faster on cycleways but slower on bad road surface or certain road types such as footways ([#2076](https://github.com/GIScience/openrouteservice/pull/2076))

### Deprecated

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,7 @@ public abstract class CommonBikeFlagEncoder extends BikeCommonFlagEncoder {
private static final boolean DEBUG_OUTPUT = false;
FileWriter logWriter;

// MARQ24 MOD START
// MARQ24 ADDON in the case of the RoadBike Encoder we want to skip some
// conditions...
private final boolean isRoadBikeEncoder = this instanceof RoadBikeFlagEncoder; // TODO: design: parent class should not need to know of child
protected static final Logger LOGGER = Logger.getLogger(CommonBikeFlagEncoder.class.getName());
// MARQ24 MOD END

// MARQ24 MOD START
protected CommonBikeFlagEncoder(int speedBits, double speedFactor, int maxTurnCosts) {
Expand Down Expand Up @@ -415,7 +410,7 @@ public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, EncodingManager.A
if (!access.isFerry()) {
wayTypeSpeed = applyMaxSpeed(way, wayTypeSpeed);
handleSpeed(edgeFlags, way, wayTypeSpeed);
handleBikeRelated(edgeFlags, way, priorityFromRelation > UNCHANGED.getValue());
handleBikeRelated(edgeFlags, way, isPartOfCycleRelation(priorityFromRelation));
if (access.isConditional() && conditionalAccessEncoder != null)
conditionalAccessEncoder.setBool(false, edgeFlags, true);
boolean isRoundabout = way.hasTag(KEY_JUNCTION, "roundabout") || way.hasTag(KEY_JUNCTION, "circular");
Expand All @@ -440,6 +435,10 @@ public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, EncodingManager.A
return edgeFlags;
}

protected boolean isPartOfCycleRelation(int priorityFromRelation) {
return priorityFromRelation > UNCHANGED.getValue();
}

int getSpeed(ReaderWay way) {
int speed = Integer.MIN_VALUE;
String highwayTag = way.getTag(KEY_HIGHWAY);
Expand Down Expand Up @@ -639,28 +638,7 @@ private PriorityCode convertClassValueToPriority(String tagvalue) {
void collect(ReaderWay way, double wayTypeSpeed, TreeMap<Double, Integer> weightToPrioMap) {
String service = way.getTag(KEY_SERVICE);
String highway = way.getTag(KEY_HIGHWAY);
// MARQ24 MOD START
if (!isRoadBikeEncoder) {
// MARQ24 MOD END
// MARQ24 MOD START
if (way.hasTag(KEY_BICYCLE, KEY_DESIGNATED) || way.hasTag(KEY_BICYCLE, KEY_OFFICIAL) || way.hasTag(KEY_BICYCLE_ROAD, "yes")) {
// MARQ24 MOD END
if ("path".equals(highway)) {
weightToPrioMap.put(100d, VERY_NICE.getValue());
} else {
weightToPrioMap.put(100d, PREFER.getValue());
}
}
if (KEY_CYCLEWAY.equals(highway)) {
if (way.hasTag("foot", intendedValues) && !way.hasTag(KEY_SEGREGATED, "yes")) {
weightToPrioMap.put(100d, PREFER.getValue());
} else {
weightToPrioMap.put(100d, VERY_NICE.getValue());
}
}
// MARQ24 MOD START
}
// MARQ24 MOD END
handleDesignatedCyclingPriority(way, weightToPrioMap, highway);

double maxSpeed = getMaxSpeed(way);
if (preferHighwayTags.contains(highway) || this.isValidSpeed(maxSpeed) && maxSpeed <= 30) {
Expand All @@ -679,27 +657,7 @@ void collect(ReaderWay way, double wayTypeSpeed, TreeMap<Double, Integer> weight

if (pushingSectionsHighways.contains(highway)
|| "parking_aisle".equals(service)) {
int pushingSectionPrio = AVOID_IF_POSSIBLE.getValue();
// MARQ24 MOD START
if (!isRoadBikeEncoder) {
// MARQ24 MOD END
if (way.hasTag(KEY_BICYCLE, "use_sidepath") || way.hasTag(KEY_BICYCLE, "yes") || way.hasTag(KEY_BICYCLE, "permissive")) {
pushingSectionPrio = PREFER.getValue();
}
if (way.hasTag(KEY_BICYCLE, KEY_DESIGNATED) || way.hasTag(KEY_BICYCLE, KEY_OFFICIAL)) {
pushingSectionPrio = VERY_NICE.getValue();
}
// MARQ24 MOD START
}
// MARQ24 MOD END

if (way.hasTag("foot", "yes")) {
pushingSectionPrio = Math.max(pushingSectionPrio - 1, WORST.getValue());
if (!isRoadBikeEncoder && way.hasTag(KEY_SEGREGATED, "yes")) {
pushingSectionPrio = Math.min(pushingSectionPrio + 1, BEST.getValue());
}
}
weightToPrioMap.put(100d, pushingSectionPrio);
handlePushingSectionPriority(way, weightToPrioMap);
}

if (way.hasTag(KEY_RAILWAY, "tram")) {
Expand All @@ -724,6 +682,40 @@ void collect(ReaderWay way, double wayTypeSpeed, TreeMap<Double, Integer> weight
}
}

protected void handlePushingSectionPriority(ReaderWay way, TreeMap<Double, Integer> weightToPrioMap) {
int pushingSectionPrio = AVOID_IF_POSSIBLE.getValue();
if (way.hasTag(KEY_BICYCLE, "use_sidepath") || way.hasTag(KEY_BICYCLE, "yes") || way.hasTag(KEY_BICYCLE, "permissive")) {
pushingSectionPrio = PREFER.getValue();
}
if (way.hasTag(KEY_BICYCLE, KEY_DESIGNATED) || way.hasTag(KEY_BICYCLE, KEY_OFFICIAL)) {
pushingSectionPrio = VERY_NICE.getValue();
}
if (way.hasTag("foot", "yes")) {
pushingSectionPrio = Math.max(pushingSectionPrio - 1, WORST.getValue());
if (way.hasTag(KEY_SEGREGATED, "yes")) {
pushingSectionPrio = Math.min(pushingSectionPrio + 1, BEST.getValue());
}
}
weightToPrioMap.put(100d, pushingSectionPrio);
}

protected void handleDesignatedCyclingPriority(ReaderWay way, TreeMap<Double, Integer> weightToPrioMap, String highway) {
if (way.hasTag(KEY_BICYCLE, KEY_DESIGNATED) || way.hasTag(KEY_BICYCLE, KEY_OFFICIAL) || way.hasTag(KEY_BICYCLE_ROAD, "yes")) {
if ("path".equals(highway)) {
weightToPrioMap.put(100d, VERY_NICE.getValue());
} else {
weightToPrioMap.put(100d, PREFER.getValue());
}
}
if (KEY_CYCLEWAY.equals(highway)) {
if (way.hasTag("foot", intendedValues) && !way.hasTag(KEY_SEGREGATED, "yes")) {
weightToPrioMap.put(100d, PREFER.getValue());
} else {
weightToPrioMap.put(100d, VERY_NICE.getValue());
}
}
}

/**
* Handle surface and wayType encoding
*/
Expand Down Expand Up @@ -751,7 +743,7 @@ void handleBikeRelated(IntsRef edgeFlags, ReaderWay way, boolean partOfCycleRela
wayType = WayType.PUSHING_SECTION;
} else {
// boost "none identified" partOfCycleRelation
if (!isRoadBikeEncoder && partOfCycleRelation) {
if (partOfCycleRelation) {
wayType = WayType.CYCLEWAY;
}

Expand Down Expand Up @@ -821,6 +813,10 @@ protected void setSurfaceSpeed(String surface, int speed) {
surfaceSpeeds.put(surface, new SpeedValue(speed));
}

protected void setSurfaceSpeed(String surface, int speed, UpdateType type) {
surfaceSpeeds.put(surface, new SpeedValue(speed, type));
}

protected void setSurfaceSpeed(String surface, SpeedValue speed) {
surfaceSpeeds.put(surface, speed);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

import java.util.TreeMap;

import static com.graphhopper.routing.ev.RouteNetwork.LOCAL;
import static org.heigit.ors.routing.graphhopper.extensions.util.PriorityCode.*;

/**
Expand All @@ -35,6 +34,7 @@
*/
public class RoadBikeFlagEncoder extends CommonBikeFlagEncoder {
private static final int MEAN_SPEED = 25;
protected static final int PAVED_WAY_SPEED = 20;
public static final String VAL_SECONDARY = "secondary";
public static final String VAL_SECONDARY_LINK = "secondary_link";
public static final String VAL_TERTIARY = "tertiary";
Expand All @@ -45,6 +45,7 @@ public class RoadBikeFlagEncoder extends CommonBikeFlagEncoder {
public static final String VAL_SERVICE = "service";
public static final String VAL_UNCLASSIFIED = "unclassified";
public static final String VAL_HIGHWAY = "highway";
public static final String VAL_ROAD = "road";

public RoadBikeFlagEncoder() {
// MARQ24 MOD START
Expand Down Expand Up @@ -75,29 +76,30 @@ public RoadBikeFlagEncoder(String propertiesStr) {
public RoadBikeFlagEncoder(int speedBits, double speedFactor, int maxTurnCosts, boolean considerElevation) {
super(speedBits, speedFactor, maxTurnCosts, considerElevation);
// MARQ24 MOD END
preferHighwayTags.add("road");
preferHighwayTags.add(VAL_ROAD);
preferHighwayTags.add(VAL_SECONDARY);
preferHighwayTags.add(VAL_SECONDARY_LINK);
preferHighwayTags.add(VAL_TERTIARY);
preferHighwayTags.add(VAL_TERTIARY_LINK);
preferHighwayTags.add(VAL_RESIDENTIAL);
preferHighwayTags.add(VAL_UNCLASSIFIED);

setTrackTypeSpeed(VAL_GRADE_1, 20); // paved
setTrackTypeSpeed(VAL_GRADE_1, PAVED_WAY_SPEED); // paved
setTrackTypeSpeed("grade2", 10); // now unpaved ...
setTrackTypeSpeed("grade3", PUSHING_SECTION_SPEED);
setTrackTypeSpeed("grade4", PUSHING_SECTION_SPEED);
setTrackTypeSpeed("grade5", PUSHING_SECTION_SPEED);

setSurfaceSpeed("paved", 20);
setSurfaceSpeed("asphalt", 20);
setSurfaceSpeed("cobblestone", 10);
setSurfaceSpeed("cobblestone:flattened", 10);
setSurfaceSpeed("sett", 10);
setSurfaceSpeed("concrete", 20);
setSurfaceSpeed("concrete:lanes", 16);
setSurfaceSpeed("concrete:plates", 16);
setSurfaceSpeed("paving_stones", 10);
setSurfaceSpeed("paving_stones:30", 10);
setSurfaceSpeed("paved", PAVED_WAY_SPEED, UpdateType.UPGRADE_ONLY);
setSurfaceSpeed("asphalt", PAVED_WAY_SPEED, UpdateType.UPGRADE_ONLY);
setSurfaceSpeed("concrete", PAVED_WAY_SPEED, UpdateType.UPGRADE_ONLY);

setSurfaceSpeed("cobblestone", 10, UpdateType.DOWNGRADE_ONLY);
setSurfaceSpeed("cobblestone:flattened", 10, UpdateType.DOWNGRADE_ONLY);
setSurfaceSpeed("sett", 10, UpdateType.DOWNGRADE_ONLY);
setSurfaceSpeed("concrete:lanes", 16, UpdateType.DOWNGRADE_ONLY);
setSurfaceSpeed("concrete:plates", 16, UpdateType.DOWNGRADE_ONLY);
setSurfaceSpeed("paving_stones", 12, UpdateType.DOWNGRADE_ONLY);
setSurfaceSpeed("paving_stones:30", 12, UpdateType.DOWNGRADE_ONLY);
setSurfaceSpeed("unpaved", PUSHING_SECTION_SPEED / 2);
setSurfaceSpeed("compacted", PUSHING_SECTION_SPEED / 2);
setSurfaceSpeed("dirt", PUSHING_SECTION_SPEED / 2);
Expand All @@ -115,88 +117,36 @@ public RoadBikeFlagEncoder(int speedBits, double speedFactor, int maxTurnCosts,
setSurfaceSpeed("sand", PUSHING_SECTION_SPEED / 2);
setSurfaceSpeed("wood", PUSHING_SECTION_SPEED / 2);

setHighwaySpeed("cycleway", 18);
setHighwaySpeed(KEY_CYCLEWAY, new SpeedValue(18, UpdateType.DOWNGRADE_ONLY));
setHighwaySpeed("living_street", 10);
setHighwaySpeed("path", 8);
setHighwaySpeed("footway", 6);
setHighwaySpeed("pedestrian", 6);
setHighwaySpeed("road", 12);
setHighwaySpeed(VAL_ROAD, PAVED_WAY_SPEED);
setHighwaySpeed(VAL_TRACK, PUSHING_SECTION_SPEED / 2); // assume unpaved
setHighwaySpeed(VAL_SERVICE, 12);
setHighwaySpeed(VAL_UNCLASSIFIED, 16);
setHighwaySpeed(VAL_RESIDENTIAL, 16);
setHighwaySpeed(VAL_UNCLASSIFIED, PAVED_WAY_SPEED);
setHighwaySpeed(VAL_RESIDENTIAL, new SpeedValue(18, UpdateType.DOWNGRADE_ONLY));

setHighwaySpeed("trunk", 20);
setHighwaySpeed("trunk_link", 20);
setHighwaySpeed("primary", 20);
setHighwaySpeed("primary_link", 20);
setHighwaySpeed(VAL_SECONDARY, 20);
setHighwaySpeed(VAL_SECONDARY_LINK, 20);
setHighwaySpeed(VAL_TERTIARY, 20);
setHighwaySpeed(VAL_TERTIARY_LINK, 20);
setHighwaySpeed("primary", 22);
setHighwaySpeed("primary_link", 22);
setHighwaySpeed(VAL_SECONDARY, 24);
setHighwaySpeed(VAL_SECONDARY_LINK, 24);
setHighwaySpeed(VAL_TERTIARY, 26);
setHighwaySpeed(VAL_TERTIARY_LINK, 26);

addPushingSection("path");
addPushingSection("footway");
addPushingSection("pedestrian");
addPushingSection("steps");
addPushingSection(KEY_BRIDLEWAY);

routeMap.put(LOCAL, UNCHANGED.getValue());

blockByDefaultBarriers.add("kissing_gate");

setAvoidSpeedLimit(81);
setSpecificClassBicycle("roadcycling");

// MARQ24 MOD START
//**********************************************************************
// REQUIRED ADDON OR OVERWRITE OF Default GH-RoadBikeProfile
// created by MARQ24
//**********************************************************************
preferHighwayTags.remove(VAL_RESIDENTIAL);
preferHighwayTags.add(VAL_UNCLASSIFIED);

// adjusted speeds...
setHighwaySpeed("trunk", 20);
setHighwaySpeed("trunk_link", 20);
setHighwaySpeed("primary", 22);
setHighwaySpeed("primary_link", 22);
setHighwaySpeed(VAL_SECONDARY, 24);
setHighwaySpeed(VAL_SECONDARY_LINK, 24);
setHighwaySpeed(VAL_TERTIARY, 26);
setHighwaySpeed(VAL_TERTIARY_LINK, 26);
setHighwaySpeed("road", 20);
setHighwaySpeed(VAL_UNCLASSIFIED, 20);
setHighwaySpeed(VAL_RESIDENTIAL, new SpeedValue(18, UpdateType.DOWNGRADE_ONLY));

// make sure that we will avoid 'cycleway' & 'service' ways where ever
// it is possible...
setHighwaySpeed("cycleway", new SpeedValue(8, UpdateType.DOWNGRADE_ONLY));
setHighwaySpeed(VAL_SERVICE, new SpeedValue(8, UpdateType.DOWNGRADE_ONLY));

// overwriting also the SurfaceSpeeds... to the "max" of the residential speed
setSurfaceSpeed("paved", new SpeedValue(18, UpdateType.UPGRADE_ONLY));
setSurfaceSpeed("asphalt", new SpeedValue(18, UpdateType.UPGRADE_ONLY));
setSurfaceSpeed("concrete", new SpeedValue(18, UpdateType.UPGRADE_ONLY));

setSurfaceSpeed("concrete:lanes", new SpeedValue(16, UpdateType.UPGRADE_ONLY));
setSurfaceSpeed("concrete:plates", new SpeedValue(16, UpdateType.UPGRADE_ONLY));
setSurfaceSpeed("paving_stones", new SpeedValue(10, UpdateType.UPGRADE_ONLY));
setSurfaceSpeed("paving_stones:30", new SpeedValue(10, UpdateType.UPGRADE_ONLY));
setSurfaceSpeed("cobblestone", new SpeedValue(10, UpdateType.UPGRADE_ONLY));
setSurfaceSpeed("cobblestone:flattened", new SpeedValue(10, UpdateType.UPGRADE_ONLY));
setSurfaceSpeed("sett", new SpeedValue(10, UpdateType.UPGRADE_ONLY));

// overwriting also the trackTypeSpeeds... to the "max" of the residential speed
setTrackTypeSpeed(VAL_GRADE_1, new SpeedValue(18, UpdateType.UPGRADE_ONLY));
setTrackTypeSpeed("grade2", new SpeedValue(10, UpdateType.UPGRADE_ONLY));

// HSW - asphalt cycleway vs asphalt roundabout
// http://localhost:3035/directions?n1=51.965101&n2=8.24595&n3=18&a=51.965555,8.243968,51.964878,8.245057&b=1c&c=0&g1=-1&g2=0&h2=3&k1=en-US&k2=km

// Aschloh roundabout vs cycleway (cycle relation) & service shortcut
// http://localhost:3035/directions?n1=52.064701&n2=8.386386&n3=19&a=52.065407,8.386171,52.064821,8.386833&b=1c&c=0&g1=-1&g2=0&h2=3&k1=en-US&k2=km
LOGGER.info("NextGen RoadBike FlagEncoder is active...");
// MARQ24 MOD END
}

public double getMeanSpeed() {
Expand All @@ -217,6 +167,8 @@ void collect(ReaderWay way, double wayTypeSpeed, TreeMap<Double, Integer> weight
} else if (trackType == null || trackType.startsWith("grade")) {
weightToPrioMap.put(110d, AVOID_AT_ALL_COSTS.getValue());
}
} else if (way.hasTag("foot", intendedValues)) {
weightToPrioMap.put(110d, AVOID_IF_POSSIBLE.getValue());
}
}

Expand Down Expand Up @@ -245,4 +197,24 @@ public String toString() {
protected double getDownhillMaxSpeed() {
return 60;
}

@Override
protected void handlePushingSectionPriority(ReaderWay way, TreeMap<Double, Integer> weightToPrioMap) {
int pushingSectionPrio = AVOID_IF_POSSIBLE.getValue();
if (way.hasTag("foot", "yes")) {
pushingSectionPrio = Math.max(pushingSectionPrio - 1, WORST.getValue());
}
weightToPrioMap.put(100d, pushingSectionPrio);
}

@Override
protected void handleDesignatedCyclingPriority(ReaderWay way, TreeMap<Double, Integer> weightToPrioMap, String highway) {
// do nothing
}

@Override
protected boolean isPartOfCycleRelation(int priorityFromRelation) {
return false;
}

}
Loading