Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,7 @@ public void populateProperties(WayPropertySet props) {
withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(0.8)
);
// Default was 2.5, we want to favor using mixed footways somewhat
props.setProperties(
"footway=sidewalk;highway=footway;bicycle=yes",
withModes(PEDESTRIAN_AND_BICYCLE).bicycleSafety(1.2)
);
props.setMixinProperties("footway=sidewalk;highway=footway;bicycle=yes", ofBicycleSafety(0.6));

props.setMixinProperties("highway=tertiary", ofBicycleSafety(1.2));
props.setMixinProperties("maxspeed=70", ofBicycleSafety(1.5));
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -32,62 +32,6 @@ public void populateProperties(WayPropertySet props) {
// reduce trunk safety compared to default mapper
props.setProperties("highway=trunk", withModes(ALL).walkSafety(2.5).bicycleSafety(2.5));
props.setProperties("highway=trunk_link", withModes(ALL).walkSafety(2.5).bicycleSafety(2.06));
props.setProperties(
"highway=trunk;cycleway=lane",
withModes(ALL).walkSafety(2.5).bicycleSafety(1.5)
);
props.setProperties(
"highway=trunk_link;cycleway=lane",
withModes(ALL).walkSafety(2.5).bicycleSafety(1.15)
);
props.setProperties(
"highway=trunk;cycleway=share_busway",
withModes(ALL).walkSafety(2.5).bicycleSafety(1.75)
);
props.setProperties(
"highway=trunk_link;cycleway=share_busway",
withModes(ALL).walkSafety(2.5).bicycleSafety(1.25)
);
props.setProperties(
"highway=trunk;cycleway=opposite_lane",
withModes(ALL).walkSafety(2.5).bicycleSafety(2.5),
withModes(ALL).walkSafety(2.5).bicycleSafety(2.5),
withModes(ALL).walkSafety(2.5).bicycleSafety(1.5)
);
props.setProperties(
"highway=trunk_link;cycleway=opposite_lane",
withModes(ALL).walkSafety(2.5).bicycleSafety(2.06),
withModes(ALL).walkSafety(2.5).bicycleSafety(2.06),
withModes(ALL).walkSafety(2.5).bicycleSafety(1.15)
);
props.setProperties(
"highway=trunk;cycleway=track",
withModes(ALL).walkSafety(2.5).bicycleSafety(0.95)
);
props.setProperties(
"highway=trunk_link;cycleway=track",
withModes(ALL).walkSafety(2.5).bicycleSafety(0.85)
);
props.setProperties(
"highway=trunk;cycleway=opposite_track",
withModes(ALL).walkSafety(2.5).bicycleSafety(2.5),
withModes(ALL).walkSafety(2.5).bicycleSafety(2.5),
withModes(ALL).walkSafety(2.5).bicycleSafety(0.95)
);
props.setProperties(
"highway=trunk_link;cycleway=opposite_track",
withModes(ALL).walkSafety(2.5).bicycleSafety(2.5),
withModes(ALL).walkSafety(2.5).bicycleSafety(2.5),
withModes(ALL).walkSafety(2.5).bicycleSafety(0.85)
);
props.setProperties(
"highway=trunk;bicycle=designated",
withModes(ALL).walkSafety(2.5).bicycleSafety(1.75)
);
props.setProperties(
"highway=trunk_link;bicycle=designated",
withModes(ALL).walkSafety(2.5).bicycleSafety(1.75)
);

props.setMixinProperties(
"expressway=yes",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
package org.opentripplanner.osm.wayproperty;

public record MixinDirectionalProperties(double walkSafety, double bicycleSafety) {}
import org.opentripplanner.street.model.StreetTraversalPermission;

public record MixinDirectionalProperties(
StreetTraversalPermission addedPermission,
StreetTraversalPermission removedPermission,
double walkSafety,
double bicycleSafety
) {}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.opentripplanner.osm.wayproperty;

import org.opentripplanner.street.model.StreetTraversalPermission;

/**
* Builder for {@link MixinDirectionalProperties}. If you don't set the safety features they will have a default
* value of 1, which means no change.
Expand All @@ -8,6 +10,8 @@ public class MixinDirectionalPropertiesBuilder {

private double walkSafety = 1;
private double bicycleSafety = 1;
private StreetTraversalPermission addedPermission = StreetTraversalPermission.NONE;
private StreetTraversalPermission removedPermission = StreetTraversalPermission.NONE;

public static MixinDirectionalPropertiesBuilder ofWalkSafety(double safety) {
return new MixinDirectionalPropertiesBuilder().withWalkSafety(safety);
Expand Down Expand Up @@ -39,7 +43,24 @@ public MixinDirectionalPropertiesBuilder withWalkSafety(double walkSafety) {
return this;
}

public MixinDirectionalPropertiesBuilder addPermission(StreetTraversalPermission permission) {
removedPermission = removedPermission.remove(permission);
addedPermission = addedPermission.add(permission);
return this;
}

public MixinDirectionalPropertiesBuilder removePermission(StreetTraversalPermission permission) {
removedPermission = removedPermission.add(permission);
addedPermission = addedPermission.remove(permission);
return this;
}

public MixinDirectionalProperties build() {
return new MixinDirectionalProperties(walkSafety, bicycleSafety);
return new MixinDirectionalProperties(
addedPermission,
removedPermission,
walkSafety,
bicycleSafety
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
import org.opentripplanner.osm.wayproperty.specifier.OsmSpecifier;

/**
* Mixins are like {@link WayProperties} but they only contain walk and bicycle safety features (not
* modes).
* Mixins are like {@link WayProperties} but they are applied on top of the way properties.
* <p>
* They don't override other properties but their safety values are multiplied with the existing
* values. As opposed to way properties, more than one mixins can apply to a single way.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package org.opentripplanner.osm.wayproperty;

import java.util.function.Consumer;
import org.opentripplanner.osm.model.TraverseDirection;
import org.opentripplanner.osm.wayproperty.specifier.OsmSpecifier;
import org.opentripplanner.street.model.StreetTraversalPermission;

/**
* Builder for {@link MixinProperties}. If you don't set the safety features they will have a default
Expand Down Expand Up @@ -50,6 +53,40 @@ public MixinPropertiesBuilder walkSafety(double walkSafety) {
return this;
}

/**
* Add the same permission to all directions
*/
public MixinPropertiesBuilder addPermission(StreetTraversalPermission permission) {
this.defaultBuilder.addPermission(permission);
this.forwardBuilder.addPermission(permission);
this.backwardBuilder.addPermission(permission);
return this;
}

/**
* Remove the same permission to all directions
*/
public MixinPropertiesBuilder removePermission(StreetTraversalPermission permission) {
this.defaultBuilder.removePermission(permission);
this.forwardBuilder.removePermission(permission);
this.backwardBuilder.removePermission(permission);
return this;
}

public MixinPropertiesBuilder directional(
TraverseDirection direction,
Consumer<MixinDirectionalPropertiesBuilder> action
) {
var builder =
switch (direction) {
case DIRECTIONLESS -> defaultBuilder;
case FORWARD -> forwardBuilder;
case BACKWARD -> backwardBuilder;
};
action.accept(builder);
return this;
}

public MixinProperties build(OsmSpecifier spec) {
return new MixinProperties(
spec,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -547,11 +547,20 @@ private WayProperties applyMixins(
) {
double bicycle = result.bicycleSafety();
double walk = result.walkSafety();
StreetTraversalPermission permission = result.getPermission();
for (var mixin : mixins) {
var properties = mixin.getDirectionalProperties(direction);
bicycle *= properties.bicycleSafety();
walk *= properties.walkSafety();
permission = permission
.add(properties.addedPermission())
.remove(properties.removedPermission());
}
return result.mutate().bicycleSafety(bicycle).walkSafety(walk).build();
return result
.mutate()
.bicycleSafety(bicycle)
.walkSafety(walk)
.withPermission(permission)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,26 @@ enum MatchResult {
NONE,
}

/**
* Negates the condition
*/
record Not(Condition condition) implements Condition {
@Override
public String key() {
return condition.key();
}

@Override
public boolean isExtendedKeyMatch(OsmEntity way, String exKey) {
return !condition.isExtendedKeyMatch(way, exKey);
}

@Override
public String toString() {
return "not(%s)".formatted(condition.toString());
}
}

/**
* Selects tags where a given key/value matches.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import static org.opentripplanner.osm.tagmapping.OsmTagMapperSource.HAMBURG;
import static org.opentripplanner.osm.tagmapping.OsmTagMapperSource.HOUSTON;
import static org.opentripplanner.osm.tagmapping.OsmTagMapperSource.PORTLAND;
import static org.opentripplanner.street.model.StreetTraversalPermission.NONE;

import java.io.File;
import java.util.Arrays;
Expand All @@ -22,6 +23,7 @@
import org.opentripplanner.osm.tagmapping.OsmTagMapper;
import org.opentripplanner.osm.tagmapping.OsmTagMapperSource;
import org.opentripplanner.osm.wayproperty.WayPropertySet;
import org.opentripplanner.street.model.StreetTraversalPermission;
import org.opentripplanner.utils.text.Table;
import org.opentripplanner.utils.text.TableBuilder;

Expand Down Expand Up @@ -96,11 +98,27 @@ private static Table propTable(WayPropertySet wps) {

private static Table mixinTable(WayPropertySet wps) {
var propTable = new TableBuilder();
propTable.withHeaders("matcher", "bicycle safety", "walk safety");
propTable.withHeaders(
"matcher",
"add permission",
"remove permission",
"bicycle safety",
"walk safety"
);

for (var prop : wps.getMixins()) {
propTable.addRow(
"`%s`".formatted(prop.specifier().toDocString()),
tableValues(
prop.directionlessProperties().addedPermission(),
prop.forwardProperties().addedPermission(),
prop.backwardProperties().addedPermission()
),
tableValues(
prop.directionlessProperties().removedPermission(),
prop.forwardProperties().removedPermission(),
prop.backwardProperties().removedPermission()
),
tableValues(
prop.directionlessProperties().bicycleSafety(),
prop.forwardProperties().bicycleSafety(),
Expand All @@ -116,6 +134,36 @@ private static Table mixinTable(WayPropertySet wps) {
return propTable.build();
}

private static String tableValues(
StreetTraversalPermission value,
StreetTraversalPermission forward,
StreetTraversalPermission backward
) {
if (value == NONE && forward == NONE && backward == NONE) {
return "";
} else if (value == forward && value == backward) {
return value.toString();
} else {
Comment on lines +144 to +146
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to use the elses when you always have a return in the condition.

StringBuilder result = new StringBuilder();
if (value != NONE) {
result.append("no direction: ").append(value);
}
if (forward != NONE) {
if (!result.isEmpty()) {
result.append("<br>");
}
result.append("forward: ").append(forward);
}
if (backward != NONE) {
if (!result.isEmpty()) {
result.append("<br>");
}
result.append("backward: ").append(backward);
}
return result.toString();
}
}

private static String tableValues(double value, double forward, double backward) {
if (value == 1.0 && forward == 1.0 && backward == 1.0) {
return "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ public void testStopsLinkedIdentically() {
assertEquals(v1.getLon(), v2.getLon(), 1e-10);
}
}
assertEquals(153, unlinkedStopsCounter);
assertEquals(155, unlinkedStopsCounter);
}

/** Build a graph in Columbus, OH with no transit */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ void testPermissions() {
way.addTag("highway", "track");
way.addTag("tracktype", "grade1");
assertEquals(
wps.getDataForEntity(way).getPermission(),
StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE
StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE,
wps.getDataForEntity(way).getPermission()
);

// https://www.openstreetmap.org/way/5155805
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ void testAccessPrivate() {

@Test
void testFootway() {
assertEquals(PEDESTRIAN, wps.getDataForEntity(WayTestData.footway()).getPermission());
OsmWay footway = WayTestData.footway();
assertEquals(PEDESTRIAN, wps.getDataForEntity(footway).getPermission());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.directBike=[
"details" : { },
"fare" : { }
},
"generalizedCost" : 1705,
"generalizedCost" : 1680,
"legs" : [
{
"agencyTimeZoneOffset" : -25200000,
Expand All @@ -25,7 +25,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.directBike=[
"name" : "SW Johnson St. & NW 24th Ave. (P1)",
"vertexType" : "NORMAL"
},
"generalizedCost" : 1704,
"generalizedCost" : 1680,
"interlineWithPreviousLeg" : false,
"legElevation" : "0,63.7,1,63.7,11,63.4,21,63.2,31,63.3,41,63.5,51,63.3,61,62.9,71,62.5,81,62.2,91,62.0,101,61.7,111,61.5,121,61.2,131,60.9,141,60.6,151,60.3,160,60.2,240,60.2,1032,60.2,1190,55.5,1430,55.5,1506,40.4,1849,40.4,1919,37.3,1931,37.3,2078,55.5,2503,55.5,2503,NaN,2635,NaN",
"legGeometry" : { },
Expand Down
Loading
Loading