diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmDatabase.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmDatabase.java index 88b1fe9f7da..e6727190b54 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmDatabase.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmDatabase.java @@ -253,11 +253,7 @@ public void addWay(OsmWay way) { applyLevelsForWay(way); - /* An area can be specified as such, or be one by default as an amenity */ - if ( - (way.isArea() || way.isParkAndRide() || way.isBikeParking() || way.isBoardingArea()) && - way.getNodeRefs().size() > 2 - ) { + if (way.isRoutableArea()) { // this is an area that's a simple polygon. So we can just add it straight // to the areas, if it's not part of a relation. if (!areaWayIds.contains(wayId)) { diff --git a/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java b/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java index 7f102b27c5a..7b5fbe56748 100644 --- a/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java +++ b/application/src/main/java/org/opentripplanner/osm/model/OsmWay.java @@ -14,6 +14,7 @@ public class OsmWay extends OsmWithTags { "backward", "reversible" ); + private final TLongList nodes = new TLongArrayList(); public void addNodeRef(long nodeRef) { @@ -137,8 +138,23 @@ public boolean isBackwardEscalator() { return isEscalator() && "backward".equals(this.getTag("conveying")); } - public boolean isArea() { - return isTag("area", "yes"); + /** + * Returns true if the way is considered an area. + * + * An area can be specified as such, or be one by default as an amenity. + */ + public boolean isRoutableArea() { + return ( + !isTag("area", "no") && + ( + isTag("area", "yes") || + isParking() || + isBikeParking() || + isBoardingArea() || + isIndoorRoutable() + ) && + getNodeRefs().size() > 2 + ); } /** diff --git a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java index 1cd08d9fcd5..cbaf4652b34 100644 --- a/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java +++ b/application/src/main/java/org/opentripplanner/osm/model/OsmWithTags.java @@ -45,6 +45,8 @@ public class OsmWithTags { "escape" ); + private static final Set INDOOR_ROUTABLE_VALUES = Set.of("corridor", "area"); + private static final Set LEVEL_TAGS = Set.of("level", "layer"); private static final Set DEFAULT_LEVEL = Set.of("0"); @@ -413,6 +415,13 @@ public boolean isPedestrianExplicitlyAllowed() { return doesTagAllowAccess("foot"); } + /** + * @return True if this node / area is a parking. + */ + public boolean isParking() { + return isTag("amenity", "parking"); + } + /** * @return True if this node / area is a park and ride. */ @@ -420,7 +429,7 @@ public boolean isParkAndRide() { String parkingType = getTag("parking"); String parkAndRide = getTag("park_ride"); return ( - isTag("amenity", "parking") && + isParking() && ( (parkingType != null && parkingType.contains("park_and_ride")) || (parkAndRide != null && !parkAndRide.equalsIgnoreCase("no")) @@ -532,7 +541,7 @@ public void setOsmProvider(OsmProvider provider) { public boolean isRoutable() { if (isOneOfTags("highway", NON_ROUTABLE_HIGHWAYS)) { return false; - } else if (hasTag("highway") || isPlatform()) { + } else if (hasTag("highway") || isPlatform() || isIndoorRoutable()) { if (isGeneralAccessDenied()) { // There are exceptions. return ( @@ -549,6 +558,10 @@ public boolean isRoutable() { return false; } + public boolean isIndoorRoutable() { + return isOneOfTags("indoor", INDOOR_ROUTABLE_VALUES); + } + /** * Is this a link to another road, like a highway ramp. */ diff --git a/application/src/main/java/org/opentripplanner/osm/tagmapping/UKMapper.java b/application/src/main/java/org/opentripplanner/osm/tagmapping/UKMapper.java index bef0be4101b..08531ce051d 100644 --- a/application/src/main/java/org/opentripplanner/osm/tagmapping/UKMapper.java +++ b/application/src/main/java/org/opentripplanner/osm/tagmapping/UKMapper.java @@ -2,7 +2,9 @@ import static org.opentripplanner.osm.wayproperty.WayPropertiesBuilder.withModes; import static org.opentripplanner.street.model.StreetTraversalPermission.ALL; +import static org.opentripplanner.street.model.StreetTraversalPermission.PEDESTRIAN; +import org.opentripplanner.osm.wayproperty.WayProperties; import org.opentripplanner.osm.wayproperty.WayPropertySet; /** @@ -73,6 +75,10 @@ public void populateProperties(WayPropertySet props) { props.setCarSpeed("highway=secondary_link", 13.4f); // ~= 30mph props.setCarSpeed("highway=tertiary", 15.7f); // ~= 35mph + WayProperties pedestrianWayProperties = withModes(PEDESTRIAN).build(); + props.setProperties("indoor=area", pedestrianWayProperties); + props.setProperties("indoor=corridor", pedestrianWayProperties); + // Read the rest from the default set new DefaultMapper().populateProperties(props); } diff --git a/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java b/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java index 424f7247415..9ac9457a9ec 100644 --- a/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java +++ b/application/src/test/java/org/opentripplanner/osm/model/OsmWayTest.java @@ -17,6 +17,56 @@ void testIsBicycleDismountForced() { assertTrue(way.isBicycleDismountForced()); } + @Test + void testAreaMustContain3Nodes() { + OsmWay way = new OsmWay(); + way.addTag("area", "yes"); + assertFalse(way.isRoutableArea()); + way.addNodeRef(1); + assertFalse(way.isRoutableArea()); + way.addNodeRef(2); + assertFalse(way.isRoutableArea()); + way.addNodeRef(3); + assertTrue(way.isRoutableArea()); + way.addNodeRef(4); + assertTrue(way.isRoutableArea()); + } + + @Test + void testAreaTags() { + OsmWay platform = getClosedPolygon(); + platform.addTag("public_transport", "platform"); + assertTrue(platform.isRoutableArea()); + platform.addTag("area", "no"); + assertFalse(platform.isRoutableArea()); + + OsmWay roundabout = getClosedPolygon(); + roundabout.addTag("highway", "roundabout"); + assertFalse(roundabout.isRoutableArea()); + + OsmWay pedestrian = getClosedPolygon(); + pedestrian.addTag("highway", "pedestrian"); + assertFalse(pedestrian.isRoutableArea()); + pedestrian.addTag("area", "yes"); + assertTrue(pedestrian.isRoutableArea()); + + OsmWay indoorArea = getClosedPolygon(); + indoorArea.addTag("indoor", "area"); + assertTrue(indoorArea.isRoutableArea()); + + OsmWay bikeParking = getClosedPolygon(); + bikeParking.addTag("amenity", "bicycle_parking"); + assertTrue(bikeParking.isRoutableArea()); + + OsmWay corridor = getClosedPolygon(); + corridor.addTag("indoor", "corridor"); + assertTrue(corridor.isRoutableArea()); + + OsmWay door = getClosedPolygon(); + door.addTag("indoor", "door"); + assertFalse(door.isRoutableArea()); + } + @Test void testIsSteps() { OsmWay way = new OsmWay(); @@ -125,4 +175,13 @@ void escalator() { escalator.addTag("conveying", "whoknows?"); assertFalse(escalator.isEscalator()); } + + private OsmWay getClosedPolygon() { + var way = new OsmWay(); + way.addNodeRef(1); + way.addNodeRef(2); + way.addNodeRef(3); + way.addNodeRef(1); + return way; + } } diff --git a/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java b/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java index f2e26d74313..2f81cc55e3f 100644 --- a/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java +++ b/application/src/test/java/org/opentripplanner/osm/model/OsmWithTagsTest.java @@ -212,6 +212,8 @@ void isWheelchairAccessible() { @Test void isRoutable() { assertFalse(WayTestData.zooPlatform().isRoutable()); + assertTrue(WayTestData.indoor("area").isRoutable()); + assertFalse(WayTestData.indoor("room").isRoutable()); } @Test diff --git a/application/src/test/java/org/opentripplanner/osm/tagmapping/UKMapperTest.java b/application/src/test/java/org/opentripplanner/osm/tagmapping/UKMapperTest.java new file mode 100644 index 00000000000..3e9e46618fd --- /dev/null +++ b/application/src/test/java/org/opentripplanner/osm/tagmapping/UKMapperTest.java @@ -0,0 +1,26 @@ +package org.opentripplanner.osm.tagmapping; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.street.model.StreetTraversalPermission.PEDESTRIAN; + +import org.junit.jupiter.api.Test; +import org.opentripplanner.osm.wayproperty.WayPropertySet; +import org.opentripplanner.osm.wayproperty.specifier.WayTestData; + +public class UKMapperTest { + + static WayPropertySet wps = new WayPropertySet(); + + static { + var source = new UKMapper(); + source.populateProperties(wps); + } + + @Test + void indoor() { + var corridor = wps.getDataForWay(WayTestData.indoor("corridor")); + assertEquals(PEDESTRIAN, corridor.getPermission()); + var area = wps.getDataForWay(WayTestData.indoor("area")); + assertEquals(PEDESTRIAN, area.getPermission()); + } +} diff --git a/application/src/test/java/org/opentripplanner/osm/wayproperty/specifier/WayTestData.java b/application/src/test/java/org/opentripplanner/osm/wayproperty/specifier/WayTestData.java index 4206b4de091..e075955f6c4 100644 --- a/application/src/test/java/org/opentripplanner/osm/wayproperty/specifier/WayTestData.java +++ b/application/src/test/java/org/opentripplanner/osm/wayproperty/specifier/WayTestData.java @@ -218,4 +218,10 @@ public static OsmWithTags zooPlatform() { way.addTag("usage", "tourism"); return way; } + + public static OsmWithTags indoor(String value) { + var way = new OsmWithTags(); + way.addTag("indoor", value); + return way; + } } diff --git a/doc/user/osm/UK.md b/doc/user/osm/UK.md index 4a640caf95c..34c4d1c1778 100644 --- a/doc/user/osm/UK.md +++ b/doc/user/osm/UK.md @@ -38,6 +38,8 @@ Lower safety values make an OSM way more desirable and higher values less desira | `highway=trunk_link; cycleway=opposite_track` | `ALL` | forward: 2.06
back: 0.85 | | | `highway=trunk; bicycle=designated` | `ALL` | 7.25 | | | `highway=trunk_link; bicycle=designated` | `ALL` | 2.0 | | +| `indoor=area` | `PEDESTRIAN` | | | +| `indoor=corridor` | `PEDESTRIAN` | | | | `mtb:scale=3` | `NONE` | | | | `mtb:scale=4` | `NONE` | | | | `mtb:scale=5` | `NONE` | | |