From ffd8828b93b4fb552b1c4770fa2ab4e26ebfbeb9 Mon Sep 17 00:00:00 2001 From: Andrew Byrd Date: Sat, 10 Feb 2024 20:14:15 +0800 Subject: [PATCH 01/85] update overview and accessibility docs --- docs/Accessibility.md | 21 +++++++++++++++++---- docs/Product-Overview.md | 7 ++++--- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/docs/Accessibility.md b/docs/Accessibility.md index cc45a56d776..eed127bbe58 100644 --- a/docs/Accessibility.md +++ b/docs/Accessibility.md @@ -2,10 +2,23 @@ ## Preamble -GTFS and Netex define accessibility primarily in terms of binary access for wheelchair users: it's -either on or off. Whilst it is the desire of the OTP developers to broaden the scope of -accessibility the lack of data limits us to use this definition in the implementation and in this -document. +In this document and in OTP, the term "accessibility" is used with what has become its most common +meaning: design of products, devices, services, vehicles, or environments to ensure they are usable by +people with disabilities. It has other meanings in other contexts (see below). +While accessibility is a complex subject, at this point GTFS and Netex mostly represent it very +simply as a yes/no possibility of wheelchair use. While OTP developers hope to broaden the +scope and nuance of accessibility support in OTP, the lack of detailed data from data producers +currently limits implementation and discussion in this document to this binary +"wheelchair accessible" definition. + +The term "accessibility" has a completely separate, unrelated definition in the fields of +spatial analysis, urban transportation planning, and associated social sciences, where it refers to +quantitative indicators of how well-connected a particular location is to people or opportunities. +OpenTripPlanner has been widely used in research settings for the calculation of such accessibility +indicators. If this is what you're looking for, see the [documentation section on isochrones](sandbox/TravelTime.md). +Although this meaning of the term dates back many decades, it is less well known and has become a +source of confusion, so the academic and planning communities are gradually shifting to the +expression "access to opportunities", often shortened to "access". ## Unknown data diff --git a/docs/Product-Overview.md b/docs/Product-Overview.md index cbf9dc3a1b8..dc0cfcc25a1 100644 --- a/docs/Product-Overview.md +++ b/docs/Product-Overview.md @@ -3,8 +3,9 @@ ## OpenTripPlanner project OpenTripPlanner is a group of open source software applications that help individuals and organizations -calculate and deliver multimodal trip plans based on OpenStreetMap (OSM) and other standardized data -sources (e.g. GTFS, GBFS, NeTEx). +calculate and deliver multimodal trip plans based on a combination of open-standard data sources. +These include public transit services and schedules (GTFS and NeTEx) and OpenStreetMap (OSM), as +well as sources describing bicycle sharing or rental, ride hailing, and other services (e.g. GBFS). A community of dozens of individuals and organizations work on OpenTripPlanner collaboratively to improve multimodal trip planning best practices and to make it easier for public transit agencies and @@ -12,7 +13,7 @@ public transit riders to publish and access information about transit services. OpenTripPlanner deployments are locally managed in many different ways by many different types of organizations. OpenTripPlanner consistently and dependably delivers multimodal trip plans to millions of riders -everyday in dozens of countries around the globe. The project is actively maintained by the community, +every day in dozens of countries around the globe. The project is actively maintained by the community, with more than 50 commits most weeks during 2022, and 20 different developers having made 50 or more commits during the life of the project. From 28dd158615056b47160593122f56e2e4f1d5d0dc Mon Sep 17 00:00:00 2001 From: Andrew Byrd Date: Sat, 10 Feb 2024 20:35:13 +0800 Subject: [PATCH 02/85] add link to analysis section --- docs/Accessibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Accessibility.md b/docs/Accessibility.md index eed127bbe58..c5820facc40 100644 --- a/docs/Accessibility.md +++ b/docs/Accessibility.md @@ -15,7 +15,7 @@ The term "accessibility" has a completely separate, unrelated definition in the spatial analysis, urban transportation planning, and associated social sciences, where it refers to quantitative indicators of how well-connected a particular location is to people or opportunities. OpenTripPlanner has been widely used in research settings for the calculation of such accessibility -indicators. If this is what you're looking for, see the [documentation section on isochrones](sandbox/TravelTime.md). +indicators. If this is what you're looking for, see the documentation sections [on isochrones](sandbox/TravelTime.md) and [analysis](Version-Comparison.md/#analysis). Although this meaning of the term dates back many decades, it is less well known and has become a source of confusion, so the academic and planning communities are gradually shifting to the expression "access to opportunities", often shortened to "access". From 7fe1f3ee7486b1bcdcecc8cfa777d80e6a585f66 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 19 Mar 2024 23:07:42 +0100 Subject: [PATCH 03/85] Make more classes package private --- .../opentripplanner/graph_builder/module/osm/DisjointSet.java | 2 +- .../java/org/opentripplanner/graph_builder/module/osm/Ring.java | 2 +- .../graph_builder/module/osm/WalkableAreaBuilder.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/DisjointSet.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/DisjointSet.java index a5b9179f696..15bfc3605b5 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/DisjointSet.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/DisjointSet.java @@ -12,7 +12,7 @@ import org.opentripplanner.framework.collection.MapUtils; /** Basic union-find data structure with path compression */ -public class DisjointSet { +class DisjointSet { TIntList sets = new TIntArrayList(); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/Ring.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/Ring.java index 9804c2d646f..754bdbc36b2 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/Ring.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/Ring.java @@ -19,7 +19,7 @@ import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.openstreetmap.model.OSMNode; -public class Ring { +class Ring { private final LinearRing shell; private final List holes = new ArrayList<>(); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java index 5562f1df1a3..b83673c239b 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java @@ -68,7 +68,7 @@ * number of edges for an area wouldn't be determined by the nodes. The current approach can lead * to an excessive number of edges, or to no edges at all if maxAreaNodes is surpassed. */ -public class WalkableAreaBuilder { +class WalkableAreaBuilder { private final DataImportIssueStore issueStore; From debb6eb2a9370064b037d19146ee87f0bc07aa2e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 20 Mar 2024 14:53:23 +0100 Subject: [PATCH 04/85] Use instance method for figuring out bogus name --- .../graph_builder/module/osm/OsmModule.java | 7 ++----- .../module/osm/WalkableAreaBuilder.java | 18 ++++++------------ .../openstreetmap/model/OSMWithTags.java | 7 +++++++ .../openstreetmap/model/OSMWithTagsTest.java | 9 +++++++++ 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index e831d2ada47..0cd53b70fc6 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -541,11 +541,8 @@ private StreetEdge getEdgeForStreet( .withRoundabout(way.isRoundabout()) .withSlopeOverride(way.getOsmProvider().getWayPropertySet().getSlopeOverride(way)) .withStairs(way.isSteps()) - .withWheelchairAccessible(way.isWheelchairAccessible()); - - if (!way.hasTag("name") && !way.hasTag("ref")) { - seb.withBogusName(true); - } + .withWheelchairAccessible(way.isWheelchairAccessible()) + .withBogusName(way.needsFallbackName()); StreetEdge street = seb.buildAndConnect(); params.edgeNamer().recordEdge(way, street); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java index b83673c239b..5a828f2b689 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java @@ -400,7 +400,7 @@ private void pruneAreaEdges( Set edges, Set edgesToKeep ) { - if (edges.size() == 0) return; + if (edges.isEmpty()) return; StreetMode mode; StreetEdge firstEdge = (StreetEdge) edges.iterator().next(); @@ -496,7 +496,7 @@ private Set createSegments( } // do we need to recurse? if (intersects.size() == 1) { - Area area = intersects.get(0); + Area area = intersects.getFirst(); OSMWithTags areaEntity = area.parent; StreetTraversalPermission areaPermissions = areaEntity.overridePermissions( @@ -531,11 +531,8 @@ private Set createSegments( .withPermission(areaPermissions) .withBack(false) .withArea(edgeList) - .withCarSpeed(carSpeed); - - if (!areaEntity.hasTag("name") && !areaEntity.hasTag("ref")) { - streetEdgeBuilder.withBogusName(true); - } + .withCarSpeed(carSpeed) + .withBogusName(areaEntity.needsFallbackName()); streetEdgeBuilder.withWheelchairAccessible(areaEntity.isWheelchairAccessible()); @@ -559,11 +556,8 @@ private Set createSegments( .withPermission(areaPermissions) .withBack(true) .withArea(edgeList) - .withCarSpeed(carSpeed); - - if (!areaEntity.hasTag("name") && !areaEntity.hasTag("ref")) { - backStreetEdgeBuilder.withBogusName(true); - } + .withCarSpeed(carSpeed) + .withBogusName(areaEntity.needsFallbackName()); backStreetEdgeBuilder.withWheelchairAccessible(areaEntity.isWheelchairAccessible()); diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java index 37757823362..5128fb49d3f 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java @@ -576,6 +576,13 @@ public boolean isWheelchairAccessible() { return !isTagFalse("wheelchair"); } + /** + * Does this entity has a name of its own or if it needs to have a fallback one assigned? + */ + public boolean needsFallbackName() { + return !hasTag("name") && !hasTag("ref"); + } + /** * Returns true if this tag is explicitly access to this entity. */ diff --git a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java index ca5db77df12..18d92a5eec8 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java @@ -262,4 +262,13 @@ void testGenerateI18NForPattern() { osmTags.generateI18NForPattern("Note: {note}, {wheelchair:description}, {foobar:description}") ); } + + @Test + void fallbackName() { + var nameless = WayTestData.cycleway(); + assertTrue(nameless.needsFallbackName()); + + var namedTunnel = WayTestData.carTunnel(); + assertFalse(namedTunnel.needsFallbackName()); + } } From f2d5e4a5bd7aebdf0a520d5b5fd98228571ff8fc Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 20 Mar 2024 15:37:35 +0100 Subject: [PATCH 05/85] Improve debugging of edge names --- .../vector/edge/EdgePropertyMapper.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java index fb65d0b5d3b..704bf961dfa 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java +++ b/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java @@ -3,6 +3,7 @@ import static org.opentripplanner.framework.lang.DoubleUtils.roundTo2Decimals; import static org.opentripplanner.inspector.vector.KeyValue.kv; +import com.google.common.collect.Lists; import java.util.Collection; import java.util.List; import org.opentripplanner.apis.support.mapping.PropertyMapper; @@ -16,16 +17,26 @@ public class EdgePropertyMapper extends PropertyMapper { @Override protected Collection map(Edge input) { - List baseProps = List.of(kv("class", input.getClass().getSimpleName())); + var baseProps = List.of(kv("class", input.getClass().getSimpleName())); List properties = switch (input) { - case StreetEdge e -> List.of( - kv("permission", e.getPermission().toString()), - kv("bicycleSafetyFactor", roundTo2Decimals(e.getBicycleSafetyFactor())) - ); + case StreetEdge e -> mapStreetEdge(e); case EscalatorEdge e -> List.of(kv("distance", e.getDistanceMeters())); default -> List.of(); }; return ListUtils.combine(baseProps, properties); } + + private static List mapStreetEdge(StreetEdge se) { + var props = Lists.newArrayList( + kv("permission", se.getPermission().toString()), + kv("bicycleSafetyFactor", roundTo2Decimals(se.getBicycleSafetyFactor())) + ); + if (se.hasBogusName()) { + props.addFirst(kv("generated name", "%s".formatted(se.getName().toString()))); + } else { + props.addFirst(kv("name", se.getName().toString())); + } + return props; + } } From ea94f83770cf7c6970a0157aec13b0504db2c936 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 20 Mar 2024 15:43:44 +0100 Subject: [PATCH 06/85] Simplify builder chains --- .../module/osm/WalkableAreaBuilder.java | 16 ++++++---------- .../openstreetmap/model/OSMWay.java | 4 ++++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java index 5a828f2b689..6244a50321f 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java @@ -532,11 +532,9 @@ private Set createSegments( .withBack(false) .withArea(edgeList) .withCarSpeed(carSpeed) - .withBogusName(areaEntity.needsFallbackName()); - - streetEdgeBuilder.withWheelchairAccessible(areaEntity.isWheelchairAccessible()); - - streetEdgeBuilder.withLink(areaEntity.isLink()); + .withBogusName(areaEntity.needsFallbackName()) + .withWheelchairAccessible(areaEntity.isWheelchairAccessible()) + .withLink(areaEntity.isLink()); label = "way (area) " + @@ -557,11 +555,9 @@ private Set createSegments( .withBack(true) .withArea(edgeList) .withCarSpeed(carSpeed) - .withBogusName(areaEntity.needsFallbackName()); - - backStreetEdgeBuilder.withWheelchairAccessible(areaEntity.isWheelchairAccessible()); - - backStreetEdgeBuilder.withLink(areaEntity.isLink()); + .withBogusName(areaEntity.needsFallbackName()) + .withWheelchairAccessible(areaEntity.isWheelchairAccessible()) + .withLink(areaEntity.isLink()); if (!wayPropertiesCache.containsKey(areaEntity)) { WayProperties wayData = areaEntity diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java index 1ce6c698f22..c0cd049bd83 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java @@ -60,6 +60,10 @@ public boolean isSteps() { return isTag("highway", "steps"); } + /** + * Checks the wheelchair-accessibility of this way. Stairs are by default inaccessible but + * can be made accessible if they explicitly set wheelchair=true. + */ public boolean isWheelchairAccessible() { if (isSteps()) { return isTagTrue("wheelchair"); From c25c12ab69da21a7ca45255530e6c412210caf0e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 21 Mar 2024 17:19:33 +0100 Subject: [PATCH 07/85] Add first draft of edge naming --- .../apis/vectortiles/DebugStyleSpec.java | 27 +++++-- .../apis/vectortiles/model/StyleBuilder.java | 20 +++++ .../apis/vectortiles/model/StyleSpec.java | 5 ++ .../module/osm/naming/SidewalkNamer.java | 77 +++++++++++++++++++ .../graph_builder/services/osm/EdgeNamer.java | 4 +- .../vector/edge/EdgePropertyMapper.java | 2 +- .../openstreetmap/model/OSMWithTags.java | 20 ++++- 7 files changed, 142 insertions(+), 13 deletions(-) create mode 100644 src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 8350a10d670..1558346493a 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -13,6 +13,7 @@ import org.opentripplanner.service.vehiclerental.street.StreetVehicleRentalLink; import org.opentripplanner.street.model.edge.AreaEdge; import org.opentripplanner.street.model.edge.BoardingLocationToStopLink; +import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.edge.ElevatorHopEdge; import org.opentripplanner.street.model.edge.EscalatorEdge; import org.opentripplanner.street.model.edge.PathwayEdge; @@ -49,6 +50,13 @@ public class DebugStyleSpec { 1, List.of(new ZoomStop(15, 0.2f), new ZoomStop(MAX_ZOOM, 3)) ); + private static final Class[] ALL_EDGES = new Class[]{StreetEdge.class, + AreaEdge.class, + EscalatorEdge.class, + PathwayEdge.class, + ElevatorHopEdge.class, + TemporaryPartialStreetEdge.class, + TemporaryFreeEdge.class}; static StyleSpec build( VectorSourceLayer regularStops, @@ -74,18 +82,23 @@ static StyleSpec build( .vectorSourceLayer(edges) .lineColor(MAGENTA) .edgeFilter( - StreetEdge.class, - AreaEdge.class, - EscalatorEdge.class, - PathwayEdge.class, - ElevatorHopEdge.class, - TemporaryPartialStreetEdge.class, - TemporaryFreeEdge.class + ALL_EDGES ) .lineWidth(LINE_WIDTH) .minZoom(13) .maxZoom(MAX_ZOOM) .intiallyHidden(), + StyleBuilder + .ofId("edge-name") + .typeSymbol() + .lineText("name") + .vectorSourceLayer(edges) + .edgeFilter( + ALL_EDGES + ) + .minZoom(15) + .maxZoom(MAX_ZOOM) + .intiallyHidden(), StyleBuilder .ofId("link") .typeLine() diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java index 07efe376968..c7f259b0c9b 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java @@ -8,6 +8,7 @@ import java.util.Map; import java.util.Objects; import java.util.stream.Stream; +import org.opentripplanner.apis.vectortiles.model.ZoomDependentNumber.ZoomStop; import org.opentripplanner.framework.collection.ListUtils; import org.opentripplanner.framework.json.ObjectMappers; import org.opentripplanner.street.model.edge.Edge; @@ -41,6 +42,7 @@ public enum LayerType { Line, Raster, Fill, + Symbol } private StyleBuilder(String id) { @@ -94,11 +96,29 @@ public StyleBuilder typeFill() { return this; } + public StyleBuilder typeSymbol() { + type(LayerType.Symbol); + return this; + } + private StyleBuilder type(LayerType type) { props.put(TYPE, type.name().toLowerCase()); return this; } + public StyleBuilder lineText(String name){ + layout.put("symbol-placement", "line"); + layout.put("text-field", "{%s}".formatted(name)); + layout.put("text-font", List.of("KlokanTech Noto Sans Regular")); + layout.put("text-size", new ZoomDependentNumber(14, List.of(new ZoomStop(14, 12), new ZoomStop(20, 14))).toJson()); + layout.put("text-offset", List.of(0, 0.9)); + paint.put("text-color", "#000"); + paint.put("text-halo-color", "#fff"); + paint.put("text-halo-blur", 4); + paint.put("text-halo-width", 3); + return this; + } + public StyleBuilder circleColor(String color) { paint.put("circle-color", validateColor(color)); return this; diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleSpec.java index 64f680ed202..71e5df9d335 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleSpec.java @@ -46,4 +46,9 @@ public Map sources() { public List layers() { return layers; } + + @JsonSerialize + public String glyphs() { + return "https://cdn.jsdelivr.net/gh/klokantech/klokantech-gl-fonts@master/{fontstack}/{range}.pbf"; + } } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java new file mode 100644 index 00000000000..e32df244530 --- /dev/null +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -0,0 +1,77 @@ +package org.opentripplanner.graph_builder.module.osm.naming; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import org.locationtech.jts.algorithm.distance.DiscreteHausdorffDistance; +import org.locationtech.jts.index.SpatialIndex; +import org.locationtech.jts.index.quadtree.Quadtree; +import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.framework.logging.ProgressTracker; +import org.opentripplanner.graph_builder.services.osm.EdgeNamer; +import org.opentripplanner.openstreetmap.model.OSMWithTags; +import org.opentripplanner.street.model.edge.StreetEdge; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SidewalkNamer implements EdgeNamer { + + private static final Logger LOG = LoggerFactory.getLogger(SidewalkNamer.class); + + private SpatialIndex streetEdges = new Quadtree(); + private Collection unnamedSidewalks = new ArrayList<>(); + + @Override + public I18NString name(OSMWithTags way) { + return way.getAssumedName(); + } + + @Override + public void recordEdge(OSMWithTags way, StreetEdge edge) { + if (way.isSidewalk() && way.needsFallbackName()) { + unnamedSidewalks.add(edge); + } + if (way.isNamed()) { + streetEdges.insert(edge.getGeometry().getEnvelopeInternal(), edge); + } + } + + @Override + public void postprocess() { + ProgressTracker progress = ProgressTracker.track( + "Assigning names to sidewalks", + 500, + unnamedSidewalks.size() + ); + unnamedSidewalks + .parallelStream() + .forEach(sidewalk -> { + var envelope = sidewalk.getGeometry().getEnvelopeInternal(); + envelope.expandBy(0.0002); + var candidates = (List) streetEdges.query(envelope); + + candidates + .stream() + .min(lowestHausdorffDistance(sidewalk)) + .ifPresent(named -> { + sidewalk.setName(named.getName()); + }); + + //Keep lambda! A method-ref would cause incorrect class and line number to be logged + //noinspection Convert2MethodRef + progress.step(m -> LOG.info(m)); + }); + LOG.info(progress.completeMessage()); + + // set the indices to null so they can be garbage-collected + streetEdges = null; + unnamedSidewalks = null; + } + + private static Comparator lowestHausdorffDistance(StreetEdge sidewalk) { + return Comparator.comparingDouble(candidate -> + DiscreteHausdorffDistance.distance(sidewalk.getGeometry(), candidate.getGeometry()) + ); + } +} diff --git a/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java b/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java index d9e52a18465..2733e705c70 100644 --- a/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java @@ -5,6 +5,7 @@ import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.graph_builder.module.osm.naming.DefaultNamer; import org.opentripplanner.graph_builder.module.osm.naming.PortlandCustomNamer; +import org.opentripplanner.graph_builder.module.osm.naming.SidewalkNamer; import org.opentripplanner.openstreetmap.model.OSMWithTags; import org.opentripplanner.standalone.config.framework.json.NodeAdapter; import org.opentripplanner.standalone.config.framework.json.OtpVersion; @@ -50,7 +51,7 @@ public static EdgeNamer fromConfig(NodeAdapter root, String parameterName) { var osmNaming = root .of(parameterName) .summary("A custom OSM namer to use.") - .since(OtpVersion.V2_0) + .since(OtpVersion.V1_5) .asString(null); return fromConfig(osmNaming); } @@ -65,6 +66,7 @@ public static EdgeNamer fromConfig(String type) { return switch (type) { case "portland" -> new PortlandCustomNamer(); + case "sidewalks" -> new SidewalkNamer(); default -> throw new IllegalArgumentException( String.format("Unknown osmNaming type: '%s'", type) ); diff --git a/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java b/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java index 704bf961dfa..d43a91d384d 100644 --- a/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java +++ b/src/main/java/org/opentripplanner/inspector/vector/edge/EdgePropertyMapper.java @@ -33,7 +33,7 @@ private static List mapStreetEdge(StreetEdge se) { kv("bicycleSafetyFactor", roundTo2Decimals(se.getBicycleSafetyFactor())) ); if (se.hasBogusName()) { - props.addFirst(kv("generated name", "%s".formatted(se.getName().toString()))); + props.addFirst(kv("name", "%s (generated)".formatted(se.getName().toString()))); } else { props.addFirst(kv("name", se.getName().toString())); } diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java index 5128fb49d3f..a4922400b04 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java @@ -30,7 +30,7 @@ public class OSMWithTags { /** * highway=* values that we don't want to even consider when building the graph. */ - public static final Set NON_ROUTABLE_HIGHWAYS = Set.of( + private static final Set NON_ROUTABLE_HIGHWAYS = Set.of( "proposed", "planned", "construction", @@ -46,7 +46,8 @@ public class OSMWithTags { "escape" ); - static final Set LEVEL_TAGS = Set.of("level", "layer"); + private static final Set LEVEL_TAGS = Set.of("level", "layer"); + private static final Set DEFAULT_LEVEL = Set.of("0"); /* To save memory this is only created when an entity actually has tags. */ private Map tags; @@ -163,6 +164,10 @@ public boolean isBicycleDismountForced() { return isTag("bicycle", "dismount"); } + public boolean isSidewalk() { + return isTag("footway", "sidewalk") && isTag("highway", "footway"); + } + protected boolean doesTagAllowAccess(String tag) { if (tags == null) { return false; @@ -580,7 +585,14 @@ public boolean isWheelchairAccessible() { * Does this entity has a name of its own or if it needs to have a fallback one assigned? */ public boolean needsFallbackName() { - return !hasTag("name") && !hasTag("ref"); + return !isNamed(); + } + + /** + * Does this entity have tags that allow extracting a name? + */ + public boolean isNamed() { + return hasTag("name") || hasTag("ref"); } /** @@ -600,7 +612,7 @@ public Set getLevels() { var levels = getMultiTagValues(LEVEL_TAGS); if (levels.isEmpty()) { // default - return Set.of("0"); + return DEFAULT_LEVEL; } return levels; } From bac74c97ccfba54360636d49d0ce6175b043e0e4 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 25 Mar 2024 09:58:25 +0100 Subject: [PATCH 08/85] Finetune sidewalk naming --- .../apis/vectortiles/DebugStyleSpec.java | 16 ++-- .../apis/vectortiles/model/StyleBuilder.java | 14 +++- .../module/osm/naming/SidewalkNamer.java | 73 +++++++++++++++++-- .../openstreetmap/model/OSMWithTags.java | 7 ++ 4 files changed, 91 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 1558346493a..16e3b247bc7 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -50,13 +50,15 @@ public class DebugStyleSpec { 1, List.of(new ZoomStop(15, 0.2f), new ZoomStop(MAX_ZOOM, 3)) ); - private static final Class[] ALL_EDGES = new Class[]{StreetEdge.class, + private static final Class[] ALL_EDGES = new Class[] { + StreetEdge.class, AreaEdge.class, EscalatorEdge.class, PathwayEdge.class, ElevatorHopEdge.class, TemporaryPartialStreetEdge.class, - TemporaryFreeEdge.class}; + TemporaryFreeEdge.class, + }; static StyleSpec build( VectorSourceLayer regularStops, @@ -81,9 +83,7 @@ static StyleSpec build( .typeLine() .vectorSourceLayer(edges) .lineColor(MAGENTA) - .edgeFilter( - ALL_EDGES - ) + .edgeFilter(ALL_EDGES) .lineWidth(LINE_WIDTH) .minZoom(13) .maxZoom(MAX_ZOOM) @@ -93,10 +93,8 @@ static StyleSpec build( .typeSymbol() .lineText("name") .vectorSourceLayer(edges) - .edgeFilter( - ALL_EDGES - ) - .minZoom(15) + .edgeFilter(ALL_EDGES) + .minZoom(17) .maxZoom(MAX_ZOOM) .intiallyHidden(), StyleBuilder diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java index c7f259b0c9b..28c1e792fd1 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java @@ -42,7 +42,7 @@ public enum LayerType { Line, Raster, Fill, - Symbol + Symbol, } private StyleBuilder(String id) { @@ -106,12 +106,18 @@ private StyleBuilder type(LayerType type) { return this; } - public StyleBuilder lineText(String name){ + public StyleBuilder lineText(String name) { layout.put("symbol-placement", "line"); + layout.put("symbol-spacing", 500); layout.put("text-field", "{%s}".formatted(name)); layout.put("text-font", List.of("KlokanTech Noto Sans Regular")); - layout.put("text-size", new ZoomDependentNumber(14, List.of(new ZoomStop(14, 12), new ZoomStop(20, 14))).toJson()); - layout.put("text-offset", List.of(0, 0.9)); + layout.put( + "text-size", + new ZoomDependentNumber(14, List.of(new ZoomStop(14, 12), new ZoomStop(20, 14))).toJson() + ); + layout.put("text-max-width", 5); + layout.put("text-keep-upright", true); + layout.put("text-rotation-alignment", "map"); paint.put("text-color", "#000"); paint.put("text-halo-color", "#fff"); paint.put("text-halo-blur", 4); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index e32df244530..fa988df6032 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -4,9 +4,13 @@ import java.util.Collection; import java.util.Comparator; import java.util.List; +import java.util.Objects; import org.locationtech.jts.algorithm.distance.DiscreteHausdorffDistance; import org.locationtech.jts.index.SpatialIndex; -import org.locationtech.jts.index.quadtree.Quadtree; +import org.locationtech.jts.operation.distance.DistanceOp; +import org.opentripplanner.framework.geometry.HashGridSpatialIndex; +import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; +import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.logging.ProgressTracker; import org.opentripplanner.graph_builder.services.osm.EdgeNamer; @@ -18,8 +22,9 @@ public class SidewalkNamer implements EdgeNamer { private static final Logger LOG = LoggerFactory.getLogger(SidewalkNamer.class); + private static final int MAX_DISTANCE_TO_SIDEWALK = 50; - private SpatialIndex streetEdges = new Quadtree(); + private SpatialIndex streetEdges = new HashGridSpatialIndex(); private Collection unnamedSidewalks = new ArrayList<>(); @Override @@ -29,7 +34,7 @@ public I18NString name(OSMWithTags way) { @Override public void recordEdge(OSMWithTags way, StreetEdge edge) { - if (way.isSidewalk() && way.needsFallbackName()) { + if (way.isSidewalk() && way.needsFallbackName() && !way.isExplicitlyUnnamed()) { unnamedSidewalks.add(edge); } if (way.isNamed()) { @@ -44,24 +49,45 @@ public void postprocess() { 500, unnamedSidewalks.size() ); + + List edges = new ArrayList<>(); unnamedSidewalks .parallelStream() .forEach(sidewalk -> { var envelope = sidewalk.getGeometry().getEnvelopeInternal(); - envelope.expandBy(0.0002); + envelope.expandBy(0.000002); var candidates = (List) streetEdges.query(envelope); candidates .stream() - .min(lowestHausdorffDistance(sidewalk)) + .map(c -> { + var hausdorff = DiscreteHausdorffDistance.distance( + sidewalk.getGeometry(), + c.getGeometry(),0.5 + ); + + var points = DistanceOp.nearestPoints( c.getGeometry(),sidewalk.getGeometry()); + double fastDistance = SphericalDistanceLibrary.fastDistance(points[0], points[1]); + + return new EdgeWithDistance(hausdorff, fastDistance, candidates.size(), c, sidewalk); + }) + .filter(e -> e.distance < MAX_DISTANCE_TO_SIDEWALK) + .min(Comparator.comparingDouble(EdgeWithDistance::hausdorff)) .ifPresent(named -> { - sidewalk.setName(named.getName()); + edges.add(named); + sidewalk.setName(Objects.requireNonNull(named.namedEdge.getName())); }); //Keep lambda! A method-ref would cause incorrect class and line number to be logged //noinspection Convert2MethodRef progress.step(m -> LOG.info(m)); }); + + edges + .stream() + .sorted(Comparator.comparingDouble(EdgeWithDistance::hausdorff).reversed()) + .limit(100) + .forEach(EdgeWithDistance::logDebugString); LOG.info(progress.completeMessage()); // set the indices to null so they can be garbage-collected @@ -74,4 +100,39 @@ private static Comparator lowestHausdorffDistance(StreetEdge sidewal DiscreteHausdorffDistance.distance(sidewalk.getGeometry(), candidate.getGeometry()) ); } + + record EdgeWithDistance( + double hausdorff, + double distance, + int numberOfCandidates, + StreetEdge namedEdge, + StreetEdge sidewalk + ) { + void logDebugString() { + LOG.info("Name '{}' applied with low Hausdorff distance ", namedEdge.getName()); + LOG.info("Hausdorff: {}", hausdorff); + LOG.info("Distance: {}m", distance); + LOG.info("OSM: {}", osmUrl()); + LOG.info("Debug client: {}", debugClientUrl()); + LOG.info("<-------------------------------------------------------------------------------->"); + } + + String debugClientUrl() { + var c = new WgsCoordinate(sidewalk.getFromVertex().getCoordinate()); + return "http://localhost:8080/debug-client-preview/#19/%s/%s".formatted( + c.latitude(), + c.longitude() + ); + } + + String osmUrl() { + var c = new WgsCoordinate(sidewalk.getFromVertex().getCoordinate()); + return "https://www.openstreetmap.org/?mlat=%s&mlon=%s#map=17/%s/%s".formatted( + c.latitude(), + c.longitude(), + c.latitude(), + c.longitude() + ); + } + } } diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java index a4922400b04..056acafb5d6 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java @@ -168,6 +168,13 @@ public boolean isSidewalk() { return isTag("footway", "sidewalk") && isTag("highway", "footway"); } + /** + * Whether this entity explicity doesn't have a name. + */ + public boolean isExplicitlyUnnamed() { + return isTagTrue("noname"); + } + protected boolean doesTagAllowAccess(String tag) { if (tags == null) { return false; From 22e102a77928283d994c2d0e3d3616e4bf77eb4a Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 25 Mar 2024 10:04:32 +0100 Subject: [PATCH 09/85] Finetune sidewalk naming --- .../graph_builder/module/osm/naming/SidewalkNamer.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index fa988df6032..30696a548f7 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -63,10 +63,11 @@ public void postprocess() { .map(c -> { var hausdorff = DiscreteHausdorffDistance.distance( sidewalk.getGeometry(), - c.getGeometry(),0.5 + c.getGeometry(), + 0.5 ); - var points = DistanceOp.nearestPoints( c.getGeometry(),sidewalk.getGeometry()); + var points = DistanceOp.nearestPoints(c.getGeometry(), sidewalk.getGeometry()); double fastDistance = SphericalDistanceLibrary.fastDistance(points[0], points[1]); return new EdgeWithDistance(hausdorff, fastDistance, candidates.size(), c, sidewalk); @@ -114,7 +115,9 @@ void logDebugString() { LOG.info("Distance: {}m", distance); LOG.info("OSM: {}", osmUrl()); LOG.info("Debug client: {}", debugClientUrl()); - LOG.info("<-------------------------------------------------------------------------------->"); + LOG.info( + "<-------------------------------------------------------------------------------->" + ); } String debugClientUrl() { From 1f106b14e5e4e7d7dc318b89a9ba1b9b2e13ad89 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 26 Mar 2024 17:20:50 +0100 Subject: [PATCH 10/85] Add documentation --- .../geometry/SphericalDistanceLibrary.java | 14 ++++++++++---- .../module/osm/naming/SidewalkNamer.java | 13 +++++++++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/opentripplanner/framework/geometry/SphericalDistanceLibrary.java b/src/main/java/org/opentripplanner/framework/geometry/SphericalDistanceLibrary.java index 479163aefd1..38c80e86d79 100644 --- a/src/main/java/org/opentripplanner/framework/geometry/SphericalDistanceLibrary.java +++ b/src/main/java/org/opentripplanner/framework/geometry/SphericalDistanceLibrary.java @@ -30,10 +30,16 @@ public static double distance(Coordinate from, Coordinate to) { return distance(from.y, from.x, to.y, to.x); } + /** + * @see SphericalDistanceLibrary#fastDistance(double, double, double, double) + */ public static double fastDistance(Coordinate from, Coordinate to) { return fastDistance(from.y, from.x, to.y, to.x); } + /** + * @see SphericalDistanceLibrary#fastDistance(double, double, double, double) + */ public static double fastDistance(Coordinate from, Coordinate to, double cosLat) { double dLat = toRadians(from.y - to.y); double dLon = toRadians(from.x - to.x) * cosLat; @@ -105,8 +111,8 @@ public static double distance(double lat1, double lon1, double lat2, double lon2 } /** - * Compute an (approximated) distance between two points, with a known cos(lat). Be careful, this - * is approximated and never check for the validity of input cos(lat). + * Compute an (approximated) distance in meters between two points, with a known cos(lat). + * Be careful, this is approximated and never checks for the validity of input cos(lat). */ public static double fastDistance(double lat1, double lon1, double lat2, double lon2) { return fastDistance(lat1, lon1, lat2, lon2, RADIUS_OF_EARTH_IN_M); @@ -131,8 +137,8 @@ public static double distance(double lat1, double lon1, double lat2, double lon2 } /** - * Approximated, fast and under-estimated equirectangular distance between two points. Works only - * for small delta lat/lon, fall-back on exact distance if not the case. See: + * Approximated, fast and under-estimated equirectangular distance in meters between two points. + * Works only for small delta lat/lon, fall-back on exact distance if not the case. See: * http://www.movable-type.co.uk/scripts/latlong.html */ public static double fastDistance( diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index 30696a548f7..495409a62a3 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -68,9 +68,9 @@ public void postprocess() { ); var points = DistanceOp.nearestPoints(c.getGeometry(), sidewalk.getGeometry()); - double fastDistance = SphericalDistanceLibrary.fastDistance(points[0], points[1]); + double distance = SphericalDistanceLibrary.fastDistance(points[0], points[1]); - return new EdgeWithDistance(hausdorff, fastDistance, candidates.size(), c, sidewalk); + return new EdgeWithDistance(hausdorff, distance, candidates.size(), c, sidewalk); }) .filter(e -> e.distance < MAX_DISTANCE_TO_SIDEWALK) .min(Comparator.comparingDouble(EdgeWithDistance::hausdorff)) @@ -84,11 +84,12 @@ public void postprocess() { progress.step(m -> LOG.info(m)); }); - edges + var worst = edges .stream() .sorted(Comparator.comparingDouble(EdgeWithDistance::hausdorff).reversed()) .limit(100) - .forEach(EdgeWithDistance::logDebugString); + .toList(); + worst.forEach(EdgeWithDistance::logDebugString); LOG.info(progress.completeMessage()); // set the indices to null so they can be garbage-collected @@ -109,6 +110,10 @@ record EdgeWithDistance( StreetEdge namedEdge, StreetEdge sidewalk ) { + EdgeWithDistance { + Objects.requireNonNull(namedEdge); + Objects.requireNonNull(sidewalk); + } void logDebugString() { LOG.info("Name '{}' applied with low Hausdorff distance ", namedEdge.getName()); LOG.info("Hausdorff: {}", hausdorff); From 1d83a07ccc6439d4cf4230c11bfe228cade66aa0 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Mar 2024 15:37:03 +0100 Subject: [PATCH 11/85] Fine-tune sidewalk naming algorithm --- .../framework/geometry/GeometryUtils.java | 2 +- .../module/osm/naming/SidewalkNamer.java | 202 ++++++++++++------ .../openstreetmap/model/OSMWithTags.java | 5 +- 3 files changed, 144 insertions(+), 65 deletions(-) diff --git a/src/main/java/org/opentripplanner/framework/geometry/GeometryUtils.java b/src/main/java/org/opentripplanner/framework/geometry/GeometryUtils.java index cf634ad0e23..87a225fc0b3 100644 --- a/src/main/java/org/opentripplanner/framework/geometry/GeometryUtils.java +++ b/src/main/java/org/opentripplanner/framework/geometry/GeometryUtils.java @@ -264,7 +264,7 @@ public static Geometry convertGeoJsonToJtsGeometry(GeoJsonObject geoJsonGeom) } /** - * Extract individual line string from a mult-line string. + * Extract individual line strings from a multi-line string. */ public static List getLineStrings(MultiLineString mls) { var ret = new ArrayList(); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index 495409a62a3..8c03eacbd67 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -5,13 +5,27 @@ import java.util.Comparator; import java.util.List; import java.util.Objects; -import org.locationtech.jts.algorithm.distance.DiscreteHausdorffDistance; -import org.locationtech.jts.index.SpatialIndex; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import org.geotools.api.referencing.FactoryException; +import org.geotools.api.referencing.crs.CoordinateReferenceSystem; +import org.geotools.api.referencing.operation.MathTransform; +import org.geotools.api.referencing.operation.TransformException; +import org.geotools.geometry.jts.JTS; +import org.geotools.referencing.CRS; +import org.geotools.referencing.crs.DefaultGeographicCRS; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.MultiLineString; +import org.locationtech.jts.geom.Point; +import org.locationtech.jts.operation.buffer.BufferParameters; import org.locationtech.jts.operation.distance.DistanceOp; +import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.geometry.HashGridSpatialIndex; import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; -import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.framework.lang.DoubleUtils; import org.opentripplanner.framework.logging.ProgressTracker; import org.opentripplanner.graph_builder.services.osm.EdgeNamer; import org.opentripplanner.openstreetmap.model.OSMWithTags; @@ -23,9 +37,10 @@ public class SidewalkNamer implements EdgeNamer { private static final Logger LOG = LoggerFactory.getLogger(SidewalkNamer.class); private static final int MAX_DISTANCE_TO_SIDEWALK = 50; + private static final double MIN_PERCENT_IN_BUFFER = .85; - private SpatialIndex streetEdges = new HashGridSpatialIndex(); - private Collection unnamedSidewalks = new ArrayList<>(); + private HashGridSpatialIndex streetEdges = new HashGridSpatialIndex<>(); + private Collection unnamedSidewalks = new ArrayList<>(); @Override public I18NString name(OSMWithTags way) { @@ -35,10 +50,19 @@ public I18NString name(OSMWithTags way) { @Override public void recordEdge(OSMWithTags way, StreetEdge edge) { if (way.isSidewalk() && way.needsFallbackName() && !way.isExplicitlyUnnamed()) { - unnamedSidewalks.add(edge); + unnamedSidewalks.add(new EdgeOnLevel(edge, way.getLevels())); } - if (way.isNamed()) { - streetEdges.insert(edge.getGeometry().getEnvelopeInternal(), edge); + if (way.isNamed() && !way.isLink()) { + var containsReverse = streetEdges + .query(edge.getGeometry().getEnvelopeInternal()) + .stream() + .anyMatch(candidate -> candidate.edge.isReverseOf(edge)); + if (!containsReverse) { + streetEdges.insert( + edge.getGeometry().getEnvelopeInternal(), + new EdgeOnLevel(edge, way.getLevels()) + ); + } } } @@ -50,33 +74,58 @@ public void postprocess() { unnamedSidewalks.size() ); - List edges = new ArrayList<>(); + final AtomicInteger namesApplied = new AtomicInteger(0); unnamedSidewalks .parallelStream() - .forEach(sidewalk -> { + .forEach(sidewalkOnLevel -> { + var sidewalk = sidewalkOnLevel.edge; var envelope = sidewalk.getGeometry().getEnvelopeInternal(); envelope.expandBy(0.000002); - var candidates = (List) streetEdges.query(envelope); + var candidates = streetEdges.query(envelope); - candidates + var groups = candidates + .stream() + .collect(Collectors.groupingBy(e -> e.edge.getName())) + .entrySet() .stream() - .map(c -> { - var hausdorff = DiscreteHausdorffDistance.distance( - sidewalk.getGeometry(), - c.getGeometry(), - 0.5 + .map(entry -> { + var levels = entry + .getValue() + .stream() + .flatMap(e -> e.levels.stream()) + .collect(Collectors.toSet()); + return new CandidateGroup( + entry.getKey(), + entry.getValue().stream().map(e -> e.edge).toList(), + levels ); + }); - var points = DistanceOp.nearestPoints(c.getGeometry(), sidewalk.getGeometry()); - double distance = SphericalDistanceLibrary.fastDistance(points[0], points[1]); + var buffer = preciseBuffer(sidewalk.getGeometry(), 25); + var sidewalkLength = SphericalDistanceLibrary.length(sidewalk.getGeometry()); - return new EdgeWithDistance(hausdorff, distance, candidates.size(), c, sidewalk); + groups + .filter(g -> g.nearestDistanceTo(sidewalk.getGeometry()) < MAX_DISTANCE_TO_SIDEWALK) + .filter(g -> g.levels.equals(sidewalkOnLevel.levels)) + .map(g -> { + var lengthInsideBuffer = g.intersectionLength(buffer); + double percentInBuffer = lengthInsideBuffer / sidewalkLength; + return new NamedEdgeGroup(percentInBuffer, candidates.size(), g.name, sidewalk); }) - .filter(e -> e.distance < MAX_DISTANCE_TO_SIDEWALK) - .min(Comparator.comparingDouble(EdgeWithDistance::hausdorff)) - .ifPresent(named -> { - edges.add(named); - sidewalk.setName(Objects.requireNonNull(named.namedEdge.getName())); + // remove those groups where less than a certain percentage is inside the buffer around + // the sidewalk. this safety mechanism for sidewalks that snake around the + // like https://www.openstreetmap.org/way/1059101564 + .filter(group -> group.percentInBuffer > MIN_PERCENT_IN_BUFFER) + .max(Comparator.comparingDouble(NamedEdgeGroup::percentInBuffer)) + .ifPresent(group -> { + namesApplied.incrementAndGet(); + var name = group.name.toString(); + + sidewalk.setName( + Objects.requireNonNull( + I18NString.of("%s, Percent in buffer: %s".formatted(name, group.percentInBuffer)) + ) + ); }); //Keep lambda! A method-ref would cause incorrect class and line number to be logged @@ -84,12 +133,13 @@ public void postprocess() { progress.step(m -> LOG.info(m)); }); - var worst = edges - .stream() - .sorted(Comparator.comparingDouble(EdgeWithDistance::hausdorff).reversed()) - .limit(100) - .toList(); - worst.forEach(EdgeWithDistance::logDebugString); + LOG.info( + "Assigned names to {} of {} of sidewalks ({})", + namesApplied.get(), + unnamedSidewalks.size(), + DoubleUtils.roundTo2Decimals((double) namesApplied.get() / unnamedSidewalks.size() * 100) + ); + LOG.info(progress.completeMessage()); // set the indices to null so they can be garbage-collected @@ -97,50 +147,76 @@ public void postprocess() { unnamedSidewalks = null; } - private static Comparator lowestHausdorffDistance(StreetEdge sidewalk) { - return Comparator.comparingDouble(candidate -> - DiscreteHausdorffDistance.distance(sidewalk.getGeometry(), candidate.getGeometry()) - ); + /** + * Taken from https://stackoverflow.com/questions/36455020 + */ + private Geometry preciseBuffer(Geometry geometry, double distanceInMeters) { + try { + var coordinate = geometry.getCentroid().getCoordinate(); + String code = "AUTO:42001,%s,%s".formatted(coordinate.x, coordinate.y); + CoordinateReferenceSystem auto = CRS.decode(code); + + MathTransform toTransform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, auto); + MathTransform fromTransform = CRS.findMathTransform(auto, DefaultGeographicCRS.WGS84); + + Geometry pGeom = JTS.transform(geometry, toTransform); + + Geometry pBufferedGeom = pGeom.buffer(distanceInMeters, 4, BufferParameters.CAP_FLAT); + return JTS.transform(pBufferedGeom, fromTransform); + } catch (TransformException | FactoryException e) { + throw new RuntimeException(e); + } } - record EdgeWithDistance( - double hausdorff, - double distance, + record NamedEdgeGroup( + double percentInBuffer, int numberOfCandidates, - StreetEdge namedEdge, + I18NString name, StreetEdge sidewalk ) { - EdgeWithDistance { - Objects.requireNonNull(namedEdge); + NamedEdgeGroup { + Objects.requireNonNull(name); Objects.requireNonNull(sidewalk); } - void logDebugString() { - LOG.info("Name '{}' applied with low Hausdorff distance ", namedEdge.getName()); - LOG.info("Hausdorff: {}", hausdorff); - LOG.info("Distance: {}m", distance); - LOG.info("OSM: {}", osmUrl()); - LOG.info("Debug client: {}", debugClientUrl()); - LOG.info( - "<-------------------------------------------------------------------------------->" - ); + } + + record CandidateGroup(I18NString name, List edges, Set levels) { + double intersectionLength(Geometry polygon) { + return edges + .stream() + .mapToDouble(edge -> { + var intersection = polygon.intersection(edge.getGeometry()); + return length(intersection); + }) + .sum(); } - String debugClientUrl() { - var c = new WgsCoordinate(sidewalk.getFromVertex().getCoordinate()); - return "http://localhost:8080/debug-client-preview/#19/%s/%s".formatted( - c.latitude(), - c.longitude() + private double length(Geometry intersection) { + return switch (intersection) { + case LineString ls -> SphericalDistanceLibrary.length(ls); + case MultiLineString mls -> GeometryUtils + .getLineStrings(mls) + .stream() + .mapToDouble(this::intersectionLength) + .sum(); + case Point ignored -> 0; + case Geometry g -> throw new IllegalStateException( + "Didn't expect geometry %s".formatted(g.getClass()) ); + }; } - String osmUrl() { - var c = new WgsCoordinate(sidewalk.getFromVertex().getCoordinate()); - return "https://www.openstreetmap.org/?mlat=%s&mlon=%s#map=17/%s/%s".formatted( - c.latitude(), - c.longitude(), - c.latitude(), - c.longitude() - ); + double nearestDistanceTo(Geometry g) { + return edges + .stream() + .mapToDouble(e -> { + var points = DistanceOp.nearestPoints(e.getGeometry(), g); + return SphericalDistanceLibrary.fastDistance(points[0], points[1]); + }) + .min() + .orElse(Double.MAX_VALUE); } } + + record EdgeOnLevel(StreetEdge edge, Set levels) {} } diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java index 056acafb5d6..5cf5ce52a2f 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java @@ -533,7 +533,7 @@ public Set getMultiTagValues(Set refTags) { .flatMap(v -> Arrays.stream(v.split(";"))) .map(String::strip) .filter(v -> !v.isBlank()) - .collect(Collectors.toUnmodifiableSet()); + .collect(Collectors.toSet()); } public OsmProvider getOsmProvider() { @@ -570,6 +570,9 @@ public boolean isRoutable() { return false; } + /** + * Is this a link to another road, like a highway ramp. + */ public boolean isLink() { String highway = getTag("highway"); return highway != null && highway.endsWith(("_link")); From ea1c1697f8467da822940e138604c712b4502f8f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Mar 2024 15:47:07 +0100 Subject: [PATCH 12/85] Update tests and docs --- docs/BuildConfiguration.md | 2 +- .../apis/vectortiles/style.json | 55 ++++++++++++++++++- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/docs/BuildConfiguration.md b/docs/BuildConfiguration.md index 3a3a6ff7ee4..c993016160f 100644 --- a/docs/BuildConfiguration.md +++ b/docs/BuildConfiguration.md @@ -35,7 +35,7 @@ Sections follow that describe particular settings in more depth. | maxTransferDuration | `duration` | Transfers up to this duration with the default walk speed value will be pre-calculated and included in the Graph. | *Optional* | `"PT30M"` | 2.1 | | [multiThreadElevationCalculations](#multiThreadElevationCalculations) | `boolean` | Configuring multi-threading during elevation calculations. | *Optional* | `false` | 2.0 | | [osmCacheDataInMem](#osmCacheDataInMem) | `boolean` | If OSM data should be cached in memory during processing. | *Optional* | `false` | 2.0 | -| osmNaming | `string` | A custom OSM namer to use. | *Optional* | | 2.0 | +| osmNaming | `string` | A custom OSM namer to use. | *Optional* | | 1.5 | | platformEntriesLinking | `boolean` | Link unconnected entries to public transport platforms. | *Optional* | `false` | 2.0 | | [readCachedElevations](#readCachedElevations) | `boolean` | Whether to read cached elevation data. | *Optional* | `true` | 2.0 | | staticBikeParkAndRide | `boolean` | Whether we should create bike P+R stations from OSM data. | *Optional* | `false` | 1.5 | diff --git a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index 7e611171409..f8a6a80e44c 100644 --- a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -63,6 +63,56 @@ "visibility" : "none" } }, + { + "id" : "edge-name", + "type" : "symbol", + "source" : "vectorSource", + "source-layer" : "edges", + "minzoom" : 17, + "maxzoom" : 23, + "paint" : { + "text-color" : "#000", + "text-halo-color" : "#fff", + "text-halo-blur" : 4, + "text-halo-width" : 3 + }, + "filter" : [ + "in", + "class", + "StreetEdge", + "AreaEdge", + "EscalatorEdge", + "PathwayEdge", + "ElevatorHopEdge", + "TemporaryPartialStreetEdge", + "TemporaryFreeEdge" + ], + "layout" : { + "symbol-placement" : "line", + "symbol-spacing" : 500, + "text-field" : "{name}", + "text-font" : [ + "KlokanTech Noto Sans Regular" + ], + "text-size" : { + "base" : 14.0, + "stops" : [ + [ + 14, + 12.0 + ], + [ + 20, + 14.0 + ] + ] + }, + "text-max-width" : 5, + "text-keep-upright" : true, + "text-rotation-alignment" : "map", + "visibility" : "none" + } + }, { "id" : "link", "type" : "line", @@ -194,5 +244,6 @@ } } ], - "version" : 8 -} + "version" : 8, + "glyphs" : "https://cdn.jsdelivr.net/gh/klokantech/klokantech-gl-fonts@master/{fontstack}/{range}.pbf" +} \ No newline at end of file From daac6006f9e0cae79fb9b7a7bf5a0b75673aa727 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Mar 2024 16:48:14 +0100 Subject: [PATCH 13/85] Improve documentation --- docs/BuildConfiguration.md | 10 ++++++++- .../module/osm/naming/SidewalkNamer.java | 8 +------ .../graph_builder/services/osm/EdgeNamer.java | 21 +++++++++++-------- .../services/osm/EdgeNamerTest.java | 12 +++++++++++ 4 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 src/test/java/org/opentripplanner/graph_builder/services/osm/EdgeNamerTest.java diff --git a/docs/BuildConfiguration.md b/docs/BuildConfiguration.md index c993016160f..a95421e47f7 100644 --- a/docs/BuildConfiguration.md +++ b/docs/BuildConfiguration.md @@ -35,7 +35,7 @@ Sections follow that describe particular settings in more depth. | maxTransferDuration | `duration` | Transfers up to this duration with the default walk speed value will be pre-calculated and included in the Graph. | *Optional* | `"PT30M"` | 2.1 | | [multiThreadElevationCalculations](#multiThreadElevationCalculations) | `boolean` | Configuring multi-threading during elevation calculations. | *Optional* | `false` | 2.0 | | [osmCacheDataInMem](#osmCacheDataInMem) | `boolean` | If OSM data should be cached in memory during processing. | *Optional* | `false` | 2.0 | -| osmNaming | `string` | A custom OSM namer to use. | *Optional* | | 1.5 | +| [osmNaming](#osmNaming) | `enum` | A custom OSM namer to use. | *Optional* | `"none"` | 1.5 | | platformEntriesLinking | `boolean` | Link unconnected entries to public transport platforms. | *Optional* | `false` | 2.0 | | [readCachedElevations](#readCachedElevations) | `boolean` | Whether to read cached elevation data. | *Optional* | `true` | 2.0 | | staticBikeParkAndRide | `boolean` | Whether we should create bike P+R stations from OSM data. | *Optional* | `false` | 1.5 | @@ -536,6 +536,14 @@ deployment depending on your infrastructure. Set the parameter to `true` to cach data, and to `false` to read the stream from the source each time. +

osmNaming

+ +**Since version:** `1.5` ∙ **Type:** `enum` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"none"` +**Path:** / +**Enum values:** `none` | `portland` | `sidewalks` + +A custom OSM namer to use. +

readCachedElevations

**Since version:** `2.0` ∙ **Type:** `boolean` ∙ **Cardinality:** `Optional` ∙ **Default value:** `true` diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index 8c03eacbd67..41e4fa09482 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -119,13 +119,7 @@ public void postprocess() { .max(Comparator.comparingDouble(NamedEdgeGroup::percentInBuffer)) .ifPresent(group -> { namesApplied.incrementAndGet(); - var name = group.name.toString(); - - sidewalk.setName( - Objects.requireNonNull( - I18NString.of("%s, Percent in buffer: %s".formatted(name, group.percentInBuffer)) - ) - ); + sidewalk.setName(Objects.requireNonNull(group.name)); }); //Keep lambda! A method-ref would cause incorrect class and line number to be logged diff --git a/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java b/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java index 2733e705c70..249557ba1fe 100644 --- a/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java @@ -52,25 +52,28 @@ public static EdgeNamer fromConfig(NodeAdapter root, String parameterName) { .of(parameterName) .summary("A custom OSM namer to use.") .since(OtpVersion.V1_5) - .asString(null); + .asEnum(EdgeNamerType.NONE); return fromConfig(osmNaming); } /** * Create a custom namer if needed, return null if not found / by default. */ - public static EdgeNamer fromConfig(String type) { - if (type == null) { + public static EdgeNamer fromConfig(EdgeNamerType type) { + if(type == null{ return new DefaultNamer(); } - return switch (type) { - case "portland" -> new PortlandCustomNamer(); - case "sidewalks" -> new SidewalkNamer(); - default -> throw new IllegalArgumentException( - String.format("Unknown osmNaming type: '%s'", type) - ); + case PORTLAND -> new PortlandCustomNamer(); + case SIDEWALKS -> new SidewalkNamer(); + case NONE -> new DefaultNamer(); }; } } + + enum EdgeNamerType { + NONE, + PORTLAND, + SIDEWALKS, + } } diff --git a/src/test/java/org/opentripplanner/graph_builder/services/osm/EdgeNamerTest.java b/src/test/java/org/opentripplanner/graph_builder/services/osm/EdgeNamerTest.java new file mode 100644 index 00000000000..70e9dbbc198 --- /dev/null +++ b/src/test/java/org/opentripplanner/graph_builder/services/osm/EdgeNamerTest.java @@ -0,0 +1,12 @@ +package org.opentripplanner.graph_builder.services.osm; + +import org.junit.jupiter.api.Test; + +class EdgeNamerTest { + + @Test + void nullType(){ + var namer = EdgeNamer.EdgeNamerFactory.fromConfig(null); + } + +} \ No newline at end of file From 4d67575df277e8781af10c780d79b252b752e2ac Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 27 Mar 2024 21:40:05 +0100 Subject: [PATCH 14/85] Split up methods, add documentation --- .../module/osm/naming/SidewalkNamer.java | 122 ++++++++++-------- .../graph_builder/services/osm/EdgeNamer.java | 8 +- .../services/osm/EdgeNamerTest.java | 15 ++- 3 files changed, 84 insertions(+), 61 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index 41e4fa09482..a44b4551919 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -8,6 +8,7 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.geotools.api.referencing.FactoryException; import org.geotools.api.referencing.crs.CoordinateReferenceSystem; import org.geotools.api.referencing.operation.MathTransform; @@ -53,6 +54,8 @@ public void recordEdge(OSMWithTags way, StreetEdge edge) { unnamedSidewalks.add(new EdgeOnLevel(edge, way.getLevels())); } if (way.isNamed() && !way.isLink()) { + // we generate two edges for each osm way: one there and one back. since we don't do any routing + // in this class we don't need the two directions and keep only one of them. var containsReverse = streetEdges .query(edge.getGeometry().getEnvelopeInternal()) .stream() @@ -78,49 +81,7 @@ public void postprocess() { unnamedSidewalks .parallelStream() .forEach(sidewalkOnLevel -> { - var sidewalk = sidewalkOnLevel.edge; - var envelope = sidewalk.getGeometry().getEnvelopeInternal(); - envelope.expandBy(0.000002); - var candidates = streetEdges.query(envelope); - - var groups = candidates - .stream() - .collect(Collectors.groupingBy(e -> e.edge.getName())) - .entrySet() - .stream() - .map(entry -> { - var levels = entry - .getValue() - .stream() - .flatMap(e -> e.levels.stream()) - .collect(Collectors.toSet()); - return new CandidateGroup( - entry.getKey(), - entry.getValue().stream().map(e -> e.edge).toList(), - levels - ); - }); - - var buffer = preciseBuffer(sidewalk.getGeometry(), 25); - var sidewalkLength = SphericalDistanceLibrary.length(sidewalk.getGeometry()); - - groups - .filter(g -> g.nearestDistanceTo(sidewalk.getGeometry()) < MAX_DISTANCE_TO_SIDEWALK) - .filter(g -> g.levels.equals(sidewalkOnLevel.levels)) - .map(g -> { - var lengthInsideBuffer = g.intersectionLength(buffer); - double percentInBuffer = lengthInsideBuffer / sidewalkLength; - return new NamedEdgeGroup(percentInBuffer, candidates.size(), g.name, sidewalk); - }) - // remove those groups where less than a certain percentage is inside the buffer around - // the sidewalk. this safety mechanism for sidewalks that snake around the - // like https://www.openstreetmap.org/way/1059101564 - .filter(group -> group.percentInBuffer > MIN_PERCENT_IN_BUFFER) - .max(Comparator.comparingDouble(NamedEdgeGroup::percentInBuffer)) - .ifPresent(group -> { - namesApplied.incrementAndGet(); - sidewalk.setName(Objects.requireNonNull(group.name)); - }); + assignNameToSidewalk(sidewalkOnLevel, namesApplied); //Keep lambda! A method-ref would cause incorrect class and line number to be logged //noinspection Convert2MethodRef @@ -128,7 +89,7 @@ public void postprocess() { }); LOG.info( - "Assigned names to {} of {} of sidewalks ({})", + "Assigned names to {} of {} of sidewalks ({}%)", namesApplied.get(), unnamedSidewalks.size(), DoubleUtils.roundTo2Decimals((double) namesApplied.get() / unnamedSidewalks.size() * 100) @@ -141,6 +102,60 @@ public void postprocess() { unnamedSidewalks = null; } + private void assignNameToSidewalk(EdgeOnLevel sidewalkOnLevel, AtomicInteger namesApplied) { + var sidewalk = sidewalkOnLevel.edge; + var buffer = preciseBuffer(sidewalk.getGeometry(), 25); + var sidewalkLength = SphericalDistanceLibrary.length(sidewalk.getGeometry()); + + var envelope = sidewalk.getGeometry().getEnvelopeInternal(); + envelope.expandBy(0.000002); + var candidates = streetEdges.query(envelope); + + groupEdgesByName(candidates) + .filter(g -> g.nearestDistanceTo(sidewalk.getGeometry()) < MAX_DISTANCE_TO_SIDEWALK) + .filter(g -> g.levels.equals(sidewalkOnLevel.levels)) + .map(g -> computePercentInsideBuffer(g, buffer, sidewalkLength)) + // remove those groups where less than a certain percentage is inside the buffer around + // the sidewalk. this safety mechanism for sidewalks that snake around the corner + // like https://www.openstreetmap.org/way/1059101564 + .filter(group -> group.percentInBuffer > MIN_PERCENT_IN_BUFFER) + .max(Comparator.comparingDouble(NamedEdgeGroup::percentInBuffer)) + .ifPresent(group -> { + namesApplied.incrementAndGet(); + sidewalk.setName(Objects.requireNonNull(group.name)); + }); + } + + private static NamedEdgeGroup computePercentInsideBuffer( + CandidateGroup g, + Geometry buffer, + double sidewalkLength + ) { + var lengthInsideBuffer = g.intersectionLength(buffer); + double percentInBuffer = lengthInsideBuffer / sidewalkLength; + return new NamedEdgeGroup(percentInBuffer, g.name); + } + + private static Stream groupEdgesByName(List candidates) { + return candidates + .stream() + .collect(Collectors.groupingBy(e -> e.edge.getName())) + .entrySet() + .stream() + .map(entry -> { + var levels = entry + .getValue() + .stream() + .flatMap(e -> e.levels.stream()) + .collect(Collectors.toSet()); + return new CandidateGroup( + entry.getKey(), + entry.getValue().stream().map(e -> e.edge).toList(), + levels + ); + }); + } + /** * Taken from https://stackoverflow.com/questions/36455020 */ @@ -162,19 +177,17 @@ private Geometry preciseBuffer(Geometry geometry, double distanceInMeters) { } } - record NamedEdgeGroup( - double percentInBuffer, - int numberOfCandidates, - I18NString name, - StreetEdge sidewalk - ) { + private record NamedEdgeGroup(double percentInBuffer, I18NString name) { NamedEdgeGroup { Objects.requireNonNull(name); - Objects.requireNonNull(sidewalk); } } - record CandidateGroup(I18NString name, List edges, Set levels) { + /** + * A group of edges that are near a sidewalk that have the same name. These groups are used + * to figure out if the name of the group can be applied to a nearby sidewalk. + */ + private record CandidateGroup(I18NString name, List edges, Set levels) { double intersectionLength(Geometry polygon) { return edges .stream() @@ -200,6 +213,9 @@ private double length(Geometry intersection) { }; } + /** + * Get the closest distance in meters between any of the edges in the group and the given geometry. + */ double nearestDistanceTo(Geometry g) { return edges .stream() @@ -212,5 +228,5 @@ private double length(Geometry intersection) { } } - record EdgeOnLevel(StreetEdge edge, Set levels) {} + private record EdgeOnLevel(StreetEdge edge, Set levels) {} } diff --git a/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java b/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java index 249557ba1fe..010b21ec2d1 100644 --- a/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java @@ -52,7 +52,7 @@ public static EdgeNamer fromConfig(NodeAdapter root, String parameterName) { .of(parameterName) .summary("A custom OSM namer to use.") .since(OtpVersion.V1_5) - .asEnum(EdgeNamerType.NONE); + .asEnum(EdgeNamerType.DEFAULT); return fromConfig(osmNaming); } @@ -60,19 +60,19 @@ public static EdgeNamer fromConfig(NodeAdapter root, String parameterName) { * Create a custom namer if needed, return null if not found / by default. */ public static EdgeNamer fromConfig(EdgeNamerType type) { - if(type == null{ + if (type == null) { return new DefaultNamer(); } return switch (type) { case PORTLAND -> new PortlandCustomNamer(); case SIDEWALKS -> new SidewalkNamer(); - case NONE -> new DefaultNamer(); + case DEFAULT -> new DefaultNamer(); }; } } enum EdgeNamerType { - NONE, + DEFAULT, PORTLAND, SIDEWALKS, } diff --git a/src/test/java/org/opentripplanner/graph_builder/services/osm/EdgeNamerTest.java b/src/test/java/org/opentripplanner/graph_builder/services/osm/EdgeNamerTest.java index 70e9dbbc198..ff471fbfbf6 100644 --- a/src/test/java/org/opentripplanner/graph_builder/services/osm/EdgeNamerTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/services/osm/EdgeNamerTest.java @@ -1,12 +1,19 @@ package org.opentripplanner.graph_builder.services.osm; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + import org.junit.jupiter.api.Test; +import org.opentripplanner.graph_builder.module.osm.naming.DefaultNamer; +import org.opentripplanner.graph_builder.services.osm.EdgeNamer.EdgeNamerType; class EdgeNamerTest { @Test - void nullType(){ - var namer = EdgeNamer.EdgeNamerFactory.fromConfig(null); + void nullType() { + assertInstanceOf(DefaultNamer.class, EdgeNamer.EdgeNamerFactory.fromConfig(null)); + assertInstanceOf( + DefaultNamer.class, + EdgeNamer.EdgeNamerFactory.fromConfig(EdgeNamerType.DEFAULT) + ); } - -} \ No newline at end of file +} From 86707901c27ba775a72f948d2c24954324c56148 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 28 Mar 2024 12:27:22 +0100 Subject: [PATCH 15/85] Add test, docs --- docs/BuildConfiguration.md | 6 +- .../apis/gtfs/GraphQLScalars.java | 7 +- .../framework/json/ObjectMappers.java | 9 ++ .../module/osm/naming/SidewalkNamer.java | 45 ++++++- .../module/osm/naming/SidewalkNamerTest.java | 114 ++++++++++++++++++ .../wayproperty/specifier/WayTestData.java | 9 +- .../test/support/GeoJsonIo.java | 26 ++++ 7 files changed, 204 insertions(+), 12 deletions(-) create mode 100644 src/test/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamerTest.java create mode 100644 src/test/java/org/opentripplanner/test/support/GeoJsonIo.java diff --git a/docs/BuildConfiguration.md b/docs/BuildConfiguration.md index a95421e47f7..fe3a2109ba4 100644 --- a/docs/BuildConfiguration.md +++ b/docs/BuildConfiguration.md @@ -35,7 +35,7 @@ Sections follow that describe particular settings in more depth. | maxTransferDuration | `duration` | Transfers up to this duration with the default walk speed value will be pre-calculated and included in the Graph. | *Optional* | `"PT30M"` | 2.1 | | [multiThreadElevationCalculations](#multiThreadElevationCalculations) | `boolean` | Configuring multi-threading during elevation calculations. | *Optional* | `false` | 2.0 | | [osmCacheDataInMem](#osmCacheDataInMem) | `boolean` | If OSM data should be cached in memory during processing. | *Optional* | `false` | 2.0 | -| [osmNaming](#osmNaming) | `enum` | A custom OSM namer to use. | *Optional* | `"none"` | 1.5 | +| [osmNaming](#osmNaming) | `enum` | A custom OSM namer to use. | *Optional* | `"default"` | 1.5 | | platformEntriesLinking | `boolean` | Link unconnected entries to public transport platforms. | *Optional* | `false` | 2.0 | | [readCachedElevations](#readCachedElevations) | `boolean` | Whether to read cached elevation data. | *Optional* | `true` | 2.0 | | staticBikeParkAndRide | `boolean` | Whether we should create bike P+R stations from OSM data. | *Optional* | `false` | 1.5 | @@ -538,9 +538,9 @@ data, and to `false` to read the stream from the source each time.

osmNaming

-**Since version:** `1.5` ∙ **Type:** `enum` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"none"` +**Since version:** `1.5` ∙ **Type:** `enum` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"default"` **Path:** / -**Enum values:** `none` | `portland` | `sidewalks` +**Enum values:** `default` | `portland` | `sidewalks` A custom OSM namer to use. diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java index 42ffe992539..5ab24c89543 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java @@ -1,6 +1,5 @@ package org.opentripplanner.apis.gtfs; -import com.bedatadriven.jackson.datatype.jts.JtsModule; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import graphql.language.StringValue; @@ -16,15 +15,15 @@ import java.time.format.DateTimeFormatter; import javax.annotation.Nonnull; import org.locationtech.jts.geom.Geometry; -import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.graphql.scalar.DurationScalarFactory; +import org.opentripplanner.framework.json.ObjectMappers; import org.opentripplanner.framework.model.Grams; import org.opentripplanner.framework.time.OffsetDateTimeParser; public class GraphQLScalars { - private static final ObjectMapper geoJsonMapper = new ObjectMapper() - .registerModule(new JtsModule(GeometryUtils.getGeometryFactory())); + private static final ObjectMapper geoJsonMapper = ObjectMappers.geoJson(); + public static GraphQLScalarType DURATION_SCALAR = DurationScalarFactory.createDurationScalar(); public static final GraphQLScalarType POLYLINE_SCALAR = GraphQLScalarType diff --git a/src/main/java/org/opentripplanner/framework/json/ObjectMappers.java b/src/main/java/org/opentripplanner/framework/json/ObjectMappers.java index 8802db0fc41..1670ae94fb3 100644 --- a/src/main/java/org/opentripplanner/framework/json/ObjectMappers.java +++ b/src/main/java/org/opentripplanner/framework/json/ObjectMappers.java @@ -1,7 +1,9 @@ package org.opentripplanner.framework.json; +import com.bedatadriven.jackson.datatype.jts.JtsModule; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; +import org.opentripplanner.framework.geometry.GeometryUtils; public class ObjectMappers { @@ -13,4 +15,11 @@ public static ObjectMapper ignoringExtraFields() { mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); return mapper; } + + /** + * Returns a mapper that can serialize JTS geometries into GeoJSON. + */ + public static ObjectMapper geoJson() { + return new ObjectMapper().registerModule(new JtsModule(GeometryUtils.getGeometryFactory())); + } } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index a44b4551919..60d0f267337 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -34,11 +34,30 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * A namer that assigns names of nearby streets to sidewalks if they meet certain + * geometric similarity criteria. + *

+ * The algorithm works as follows: + * - for each sidewalk we look up (named) street edges nearby + * - group those edges into groups where each edge has the same name + * - draw a flat-capped buffer around the sidewalk, like this: https://tinyurl.com/4fpe882h + * - check how much of a named edge group is inside the buffer + * - remove those groups which are below MIN_PERCENT_IN_BUFFER + * - take the group that has the highest percentage (as a proportion of the sidewalk length) inside + * the buffer and apply its name to the sidewalk. + *

+ * This works very well for OSM data where the sidewalk runs a parallel to the street and at each + * intersection the sidewalk is also split. It doesn't work well for sidewalks that go around + * the corner, like https://www.openstreetmap.org/way/1059101564. These cases are, however, detected + * by the above algorithm and the sidewalk name remains the same. + */ public class SidewalkNamer implements EdgeNamer { private static final Logger LOG = LoggerFactory.getLogger(SidewalkNamer.class); private static final int MAX_DISTANCE_TO_SIDEWALK = 50; private static final double MIN_PERCENT_IN_BUFFER = .85; + private static final int BUFFER_METERS = 25; private HashGridSpatialIndex streetEdges = new HashGridSpatialIndex<>(); private Collection unnamedSidewalks = new ArrayList<>(); @@ -102,9 +121,12 @@ public void postprocess() { unnamedSidewalks = null; } + /** + * The actual worker method that runs the business logic on an individual sidewalk edge. + */ private void assignNameToSidewalk(EdgeOnLevel sidewalkOnLevel, AtomicInteger namesApplied) { var sidewalk = sidewalkOnLevel.edge; - var buffer = preciseBuffer(sidewalk.getGeometry(), 25); + var buffer = preciseBuffer(sidewalk.getGeometry(), BUFFER_METERS); var sidewalkLength = SphericalDistanceLibrary.length(sidewalk.getGeometry()); var envelope = sidewalk.getGeometry().getEnvelopeInternal(); @@ -112,7 +134,9 @@ private void assignNameToSidewalk(EdgeOnLevel sidewalkOnLevel, AtomicInteger nam var candidates = streetEdges.query(envelope); groupEdgesByName(candidates) + // remove edges that are far away .filter(g -> g.nearestDistanceTo(sidewalk.getGeometry()) < MAX_DISTANCE_TO_SIDEWALK) + // make sure we only compare sidewalks and streets that are on the same level .filter(g -> g.levels.equals(sidewalkOnLevel.levels)) .map(g -> computePercentInsideBuffer(g, buffer, sidewalkLength)) // remove those groups where less than a certain percentage is inside the buffer around @@ -126,6 +150,10 @@ private void assignNameToSidewalk(EdgeOnLevel sidewalkOnLevel, AtomicInteger nam }); } + /** + * Compute the length of the group that is inside the buffer and return it as a percentage + * of the lenght of the sidewalk. + */ private static NamedEdgeGroup computePercentInsideBuffer( CandidateGroup g, Geometry buffer, @@ -136,6 +164,11 @@ private static NamedEdgeGroup computePercentInsideBuffer( return new NamedEdgeGroup(percentInBuffer, g.name); } + /** + * If a single street is split into several eges each individual part of the street would potentially + * have a low similarity with the (longer) sidewalk. For that reason we combined them into a group + * and have a better basis for comparison. + */ private static Stream groupEdgesByName(List candidates) { return candidates .stream() @@ -157,6 +190,13 @@ private static Stream groupEdgesByName(List candida } /** + * Add a buffer around a geometry that uses both meters as the input and makes sure + * that the buffer is the same distance (in meters) anywhere on earth. + *

+ * Background: If you call the regular buffer() method on a JTS geometry that uses WGS84 as the + * coordinate reference system, the buffer will be accurate at the equator but will become more + * and more elongated the furter north/south you go. + *

* Taken from https://stackoverflow.com/questions/36455020 */ private Geometry preciseBuffer(Geometry geometry, double distanceInMeters) { @@ -188,6 +228,9 @@ private record NamedEdgeGroup(double percentInBuffer, I18NString name) { * to figure out if the name of the group can be applied to a nearby sidewalk. */ private record CandidateGroup(I18NString name, List edges, Set levels) { + /** + * How much of this group intersects with the give geometry, in meters. + */ double intersectionLength(Geometry polygon) { return edges .stream() diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamerTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamerTest.java new file mode 100644 index 00000000000..3faa77ee531 --- /dev/null +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamerTest.java @@ -0,0 +1,114 @@ +package org.opentripplanner.graph_builder.module.osm.naming; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.opentripplanner.framework.geometry.GeometryUtils; +import org.opentripplanner.framework.geometry.WgsCoordinate; +import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.graph_builder.services.osm.EdgeNamer; +import org.opentripplanner.openstreetmap.model.OSMWay; +import org.opentripplanner.openstreetmap.wayproperty.specifier.WayTestData; +import org.opentripplanner.street.model.StreetTraversalPermission; +import org.opentripplanner.street.model._data.StreetModelForTest; +import org.opentripplanner.street.model.edge.StreetEdge; +import org.opentripplanner.street.model.edge.StreetEdgeBuilder; +import org.opentripplanner.test.support.GeoJsonIo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class SidewalkNamerTest { + + private static final I18NString SIDEWALK = I18NString.of("sidewalk"); + private static final Logger LOG = LoggerFactory.getLogger(SidewalkNamerTest.class); + + @Test + void postprocess() { + var builder = new ModelBuilder(); + var sidewalk = builder.addUnamedSidewalk( + new WgsCoordinate(33.75029, -84.39198), + new WgsCoordinate(33.74932, -84.39275) + ); + + LOG.info( + "Geometry of {}: {}", + sidewalk.edge.getName(), + GeoJsonIo.toUrl(sidewalk.edge.getGeometry()) + ); + + var pryorStreet = builder.addStreetEdge( + "Pryor Street", + new WgsCoordinate(33.75032, -84.39190), + new WgsCoordinate(33.74924, -84.39275) + ); + + LOG.info( + "Geometry of {}: {}", + pryorStreet.edge.getName(), + GeoJsonIo.toUrl(pryorStreet.edge.getGeometry()) + ); + + assertNotEquals(sidewalk.edge.getName(), pryorStreet.edge.getName()); + builder.postProcess(new SidewalkNamer()); + assertEquals(sidewalk.edge.getName(), pryorStreet.edge.getName()); + } + + private static class ModelBuilder { + + private final List pairs = new ArrayList<>(); + + EdgePair addUnamedSidewalk(WgsCoordinate... coordinates) { + var edge = edgeBuilder(coordinates) + .withName(SIDEWALK) + .withPermission(StreetTraversalPermission.PEDESTRIAN) + .withBogusName(true) + .buildAndConnect(); + + var way = WayTestData.footwaySidewalk(); + assertTrue(way.isSidewalk()); + var p = new EdgePair(way, edge); + pairs.add(p); + return p; + } + + EdgePair addStreetEdge(String name, WgsCoordinate... coordinates) { + var edge = edgeBuilder(coordinates) + .withName(I18NString.of(name)) + .withPermission(StreetTraversalPermission.ALL) + .buildAndConnect(); + var way = WayTestData.highwayTertiary(); + way.addTag("name", name); + assertFalse(way.isSidewalk()); + assertTrue(way.isNamed()); + var p = new EdgePair(way, edge); + pairs.add(p); + return p; + } + + void postProcess(EdgeNamer namer) { + pairs.forEach(p -> namer.recordEdge(p.way, p.edge)); + namer.postprocess(); + } + + private static StreetEdgeBuilder edgeBuilder(WgsCoordinate... c) { + var coordinates = Arrays.stream(c).toList(); + var ls = GeometryUtils.makeLineString(c); + return new StreetEdgeBuilder<>() + .withFromVertex( + StreetModelForTest.intersectionVertex(coordinates.getFirst().asJtsCoordinate()) + ) + .withToVertex( + StreetModelForTest.intersectionVertex(coordinates.getLast().asJtsCoordinate()) + ) + .withGeometry(ls); + } + } + + private record EdgePair(OSMWay way, StreetEdge edge) {} +} diff --git a/src/test/java/org/opentripplanner/openstreetmap/wayproperty/specifier/WayTestData.java b/src/test/java/org/opentripplanner/openstreetmap/wayproperty/specifier/WayTestData.java index 20dbcbb5a78..b09f690f794 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/wayproperty/specifier/WayTestData.java +++ b/src/test/java/org/opentripplanner/openstreetmap/wayproperty/specifier/WayTestData.java @@ -121,9 +121,10 @@ public static OSMWithTags cyclewayBoth() { return way; } - public static OSMWithTags footwaySidewalk() { - var way = new OSMWithTags(); + public static OSMWay footwaySidewalk() { + var way = new OSMWay(); way.addTag("footway", "sidewalk"); + way.addTag("highway", "footway"); return way; } @@ -155,8 +156,8 @@ public static OSMWithTags highwayTrunk() { return way; } - public static OSMWithTags highwayTertiary() { - var way = new OSMWithTags(); + public static OSMWay highwayTertiary() { + var way = new OSMWay(); way.addTag("highway", "tertiary"); return way; } diff --git a/src/test/java/org/opentripplanner/test/support/GeoJsonIo.java b/src/test/java/org/opentripplanner/test/support/GeoJsonIo.java new file mode 100644 index 00000000000..8e25407fca5 --- /dev/null +++ b/src/test/java/org/opentripplanner/test/support/GeoJsonIo.java @@ -0,0 +1,26 @@ +package org.opentripplanner.test.support; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import org.locationtech.jts.geom.Geometry; +import org.opentripplanner.framework.json.ObjectMappers; + +/** + * Helper class for generating URLs to geojson.io. + */ +public class GeoJsonIo { + + private static final ObjectMapper MAPPER = ObjectMappers.geoJson(); + + public static String toUrl(Geometry geometry) { + try { + var geoJson = MAPPER.writeValueAsString(geometry); + var encoded = URLEncoder.encode(geoJson, StandardCharsets.UTF_8); + return "http://geojson.io/#data=data:application/json,%s".formatted(encoded); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } +} From 4b6a27ea9f89ecefb5b6049de05688b40c81f961 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 28 Mar 2024 13:48:20 +0100 Subject: [PATCH 16/85] Sort by keys before comparing --- .../org/opentripplanner/test/support/JsonAssertions.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/java/org/opentripplanner/test/support/JsonAssertions.java b/src/test/java/org/opentripplanner/test/support/JsonAssertions.java index 2dab1e96190..f57e5a6741d 100644 --- a/src/test/java/org/opentripplanner/test/support/JsonAssertions.java +++ b/src/test/java/org/opentripplanner/test/support/JsonAssertions.java @@ -5,12 +5,17 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; import org.opentripplanner.standalone.config.framework.json.JsonSupport; public class JsonAssertions { private static final ObjectMapper MAPPER = new ObjectMapper(); + static { + MAPPER.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); + } + /** * Take two JSON documents and reformat them before comparing {@code actual} with {@code expected}. */ From f596432db06b97c672a0906756c4c6c08151b850 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 28 Mar 2024 14:19:51 +0100 Subject: [PATCH 17/85] Sort order for JSON files --- .../test/support/JsonAssertions.java | 16 ++++++++++------ .../opentripplanner/apis/vectortiles/style.json | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/opentripplanner/test/support/JsonAssertions.java b/src/test/java/org/opentripplanner/test/support/JsonAssertions.java index f57e5a6741d..1fe87268eee 100644 --- a/src/test/java/org/opentripplanner/test/support/JsonAssertions.java +++ b/src/test/java/org/opentripplanner/test/support/JsonAssertions.java @@ -5,17 +5,12 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; import org.opentripplanner.standalone.config.framework.json.JsonSupport; public class JsonAssertions { private static final ObjectMapper MAPPER = new ObjectMapper(); - static { - MAPPER.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); - } - /** * Take two JSON documents and reformat them before comparing {@code actual} with {@code expected}. */ @@ -32,8 +27,17 @@ public static void assertEqualJson(String expected, String actual) { */ public static void assertEqualJson(String expected, JsonNode actual) { try { + var actualNode = MAPPER.readTree(actual.toString()); var exp = MAPPER.readTree(expected); - assertEquals(JsonSupport.prettyPrint(exp), JsonSupport.prettyPrint(actual)); + assertEquals( + exp, + actualNode, + () -> + "Expected '%s' but actual was '%s'".formatted( + JsonSupport.prettyPrint(exp), + JsonSupport.prettyPrint(actualNode) + ) + ); } catch (JsonProcessingException e) { throw new RuntimeException(e); } diff --git a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index f8a6a80e44c..c0e31a26f5d 100644 --- a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -244,6 +244,6 @@ } } ], - "version" : 8, - "glyphs" : "https://cdn.jsdelivr.net/gh/klokantech/klokantech-gl-fonts@master/{fontstack}/{range}.pbf" + "glyphs" : "https://cdn.jsdelivr.net/gh/klokantech/klokantech-gl-fonts@master/{fontstack}/{range}.pbf", + "version" : 8 } \ No newline at end of file From a312d2a1e4a1cadf4354b991366bf936827d155e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 28 Mar 2024 14:42:20 +0100 Subject: [PATCH 18/85] Revert set conversion --- .../org/opentripplanner/openstreetmap/model/OSMWithTags.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java index 5cf5ce52a2f..51bc9da1dd8 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java @@ -533,7 +533,7 @@ public Set getMultiTagValues(Set refTags) { .flatMap(v -> Arrays.stream(v.split(";"))) .map(String::strip) .filter(v -> !v.isBlank()) - .collect(Collectors.toSet()); + .collect(Collectors.toUnmodifiableSet()); } public OsmProvider getOsmProvider() { From eecca1dfee8cd684455a6ace23dc37e14307a697 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Fri, 5 Apr 2024 20:56:40 +0300 Subject: [PATCH 19/85] Pass in logger when creating OtpHttpClient --- .../ridehailing/service/uber/UberService.java | 2 +- .../updater/SiriETGooglePubsubUpdater.java | 2 +- .../ext/siri/updater/SiriHttpLoader.java | 2 +- .../azure/AbstractAzureSiriUpdater.java | 2 +- .../SmooveBikeRentalDataSource.java | 6 ++--- .../vehicleparking/bikely/BikelyUpdater.java | 2 +- .../hslpark/HslFacilitiesDownloader.java | 14 +++++----- .../hslpark/HslHubsDownloader.java | 14 +++++----- .../VehicleRentalServiceDirectoryFetcher.java | 4 +-- .../https/HttpsDataSourceRepository.java | 6 ++++- .../datastore/https/HttpsFileDataSource.java | 6 ++++- .../framework/io/JsonDataListDownloader.java | 16 ++++++------ .../framework/io/OtpHttpClient.java | 26 ++++++++++++------- .../alert/GtfsRealtimeAlertsUpdater.java | 2 +- .../configure/UpdaterConfigurator.java | 6 ++++- .../updater/spi/GenericJsonDataSource.java | 2 +- .../trip/GtfsRealtimeTripUpdateSource.java | 2 +- ...GtfsRealtimeHttpVehiclePositionSource.java | 2 +- .../datasources/GbfsFeedLoader.java | 2 +- .../datasources/GbfsFeedLoaderTest.java | 8 +++++- .../GbfsVehicleRentalDataSourceTest.java | 10 ++++--- 21 files changed, 82 insertions(+), 54 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/ridehailing/service/uber/UberService.java b/src/ext/java/org/opentripplanner/ext/ridehailing/service/uber/UberService.java index bfc65566e6e..42ec65716de 100644 --- a/src/ext/java/org/opentripplanner/ext/ridehailing/service/uber/UberService.java +++ b/src/ext/java/org/opentripplanner/ext/ridehailing/service/uber/UberService.java @@ -79,7 +79,7 @@ public UberService(RideHailingServiceParameters config) { this.timeEstimateUri = timeEstimateUri; this.bannedTypes = bannedTypes; this.wheelchairAccessibleProductId = wheelchairAccessibleProductId; - this.otpHttpClient = new OtpHttpClient(); + this.otpHttpClient = new OtpHttpClient(LOG); } @Override diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETGooglePubsubUpdater.java b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETGooglePubsubUpdater.java index 4b5797f03de..84c6c9abab1 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETGooglePubsubUpdater.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETGooglePubsubUpdater.java @@ -312,7 +312,7 @@ private void initializeData() { } private ByteString fetchInitialData() { - try (OtpHttpClient otpHttpClient = new OtpHttpClient()) { + try (OtpHttpClient otpHttpClient = new OtpHttpClient(LOG)) { return otpHttpClient.getAndMap( dataInitializationUrl, initialGetDataTimeout, diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriHttpLoader.java b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriHttpLoader.java index 7a094dc0863..a94580a4ce5 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriHttpLoader.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriHttpLoader.java @@ -35,7 +35,7 @@ public SiriHttpLoader( this.timeout = timeout; this.requestHeaders = requestHeaders; this.previewInterval = previewInterval; - this.otpHttpClient = new OtpHttpClient(timeout, timeout); + this.otpHttpClient = new OtpHttpClient(timeout, timeout, LOG); } /** diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdater.java b/src/ext/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdater.java index 1915105960e..9aa2e1d9a66 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdater.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdater.java @@ -206,7 +206,7 @@ public String getConfigRef() { protected Optional fetchInitialSiriData(URI uri) { var headers = HttpHeaders.of().acceptApplicationXML().build().asMap(); - try (OtpHttpClient otpHttpClient = new OtpHttpClient()) { + try (OtpHttpClient otpHttpClient = new OtpHttpClient(LOG)) { var t1 = System.currentTimeMillis(); var siriOptional = otpHttpClient.executeAndMapOptional( new HttpGet(uri), diff --git a/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSource.java b/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSource.java index abeaab42137..73f46ecd501 100644 --- a/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSource.java +++ b/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSource.java @@ -23,7 +23,7 @@ public class SmooveBikeRentalDataSource extends GenericJsonDataSource implements VehicleRentalDatasource { - private static final Logger log = LoggerFactory.getLogger(SmooveBikeRentalDataSource.class); + private static final Logger LOG = LoggerFactory.getLogger(SmooveBikeRentalDataSource.class); public static final String DEFAULT_NETWORK_NAME = "smoove"; @@ -33,7 +33,7 @@ public class SmooveBikeRentalDataSource private final RentalVehicleType vehicleType; public SmooveBikeRentalDataSource(SmooveBikeRentalDataSourceParameters config) { - this(config, new OtpHttpClient()); + this(config, new OtpHttpClient(LOG)); } public SmooveBikeRentalDataSource( @@ -76,7 +76,7 @@ protected VehicleRentalStation parseElement(JsonNode node) { station.longitude = Double.parseDouble(coordinates[1].trim()); } catch (NumberFormatException e) { // E.g. coordinates is empty - log.warn("Error parsing bike rental station {}", station.id, e); + LOG.warn("Error parsing bike rental station {}", station.id, e); return null; } if (!node.path("style").asText().equals("Station on")) { diff --git a/src/ext/java/org/opentripplanner/ext/vehicleparking/bikely/BikelyUpdater.java b/src/ext/java/org/opentripplanner/ext/vehicleparking/bikely/BikelyUpdater.java index 5758d5d99e1..1c711d6d347 100644 --- a/src/ext/java/org/opentripplanner/ext/vehicleparking/bikely/BikelyUpdater.java +++ b/src/ext/java/org/opentripplanner/ext/vehicleparking/bikely/BikelyUpdater.java @@ -44,7 +44,7 @@ public class BikelyUpdater implements DataSource { .put("lonMax", 0) .put("latMin", 0) .put("latMax", 0); - private final OtpHttpClient httpClient = new OtpHttpClient(); + private final OtpHttpClient httpClient = new OtpHttpClient(LOG); private final BikelyUpdaterParameters parameters; private List lots; diff --git a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslFacilitiesDownloader.java b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslFacilitiesDownloader.java index 31664170a04..4b89eabe19a 100644 --- a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslFacilitiesDownloader.java +++ b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslFacilitiesDownloader.java @@ -20,7 +20,7 @@ public class HslFacilitiesDownloader { - private static final Logger log = LoggerFactory.getLogger(HslFacilitiesDownloader.class); + private static final Logger LOG = LoggerFactory.getLogger(HslFacilitiesDownloader.class); private static final ObjectMapper mapper = new ObjectMapper(); private final String jsonParsePath; @@ -36,14 +36,14 @@ public HslFacilitiesDownloader( this.url = url; this.jsonParsePath = jsonParsePath; this.facilitiesParser = facilitiesParser; - this.otpHttpClient = new OtpHttpClient(); + this.otpHttpClient = new OtpHttpClient(LOG); } public List downloadFacilities( Map hubForPark ) { if (url == null) { - log.warn("Cannot download updates, because url is null!"); + LOG.warn("Cannot download updates, because url is null!"); return null; } @@ -55,17 +55,17 @@ public List downloadFacilities( try { return parseJSON(is, hubForPark); } catch (IllegalArgumentException e) { - log.warn("Error parsing facilities from {}", url, e); + LOG.warn("Error parsing facilities from {}", url, e); } catch (JsonProcessingException e) { - log.warn("Error parsing facilities from {} (bad JSON of some sort)", url, e); + LOG.warn("Error parsing facilities from {} (bad JSON of some sort)", url, e); } catch (IOException e) { - log.warn("Error reading facilities from {}", url, e); + LOG.warn("Error reading facilities from {}", url, e); } return null; } ); } catch (OtpHttpClientException e) { - log.warn("Failed to get data from url {}", url); + LOG.warn("Failed to get data from url {}", url); return null; } } diff --git a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslHubsDownloader.java b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslHubsDownloader.java index c5712e2067e..102551705df 100644 --- a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslHubsDownloader.java +++ b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslHubsDownloader.java @@ -18,7 +18,7 @@ public class HslHubsDownloader { - private static final Logger log = LoggerFactory.getLogger(HslHubsDownloader.class); + private static final Logger LOG = LoggerFactory.getLogger(HslHubsDownloader.class); private static final ObjectMapper mapper = new ObjectMapper(); private final String jsonParsePath; @@ -34,12 +34,12 @@ public HslHubsDownloader( this.url = url; this.jsonParsePath = jsonParsePath; this.hubsParser = hubsParser; - otpHttpClient = new OtpHttpClient(); + otpHttpClient = new OtpHttpClient(LOG); } public Map downloadHubs() { if (url == null) { - log.warn("Cannot download updates, because url is null!"); + LOG.warn("Cannot download updates, because url is null!"); return null; } try { @@ -50,17 +50,17 @@ public Map downloadHubs() { try { return parseJSON(is); } catch (IllegalArgumentException e) { - log.warn("Error parsing hubs from {}", url, e); + LOG.warn("Error parsing hubs from {}", url, e); } catch (JsonProcessingException e) { - log.warn("Error parsing hubs from {} (bad JSON of some sort)", url, e); + LOG.warn("Error parsing hubs from {} (bad JSON of some sort)", url, e); } catch (IOException e) { - log.warn("Error reading hubs from {}", url, e); + LOG.warn("Error reading hubs from {}", url, e); } return null; } ); } catch (OtpHttpClientException e) { - log.warn("Failed to get data from url {}", url); + LOG.warn("Failed to get data from url {}", url); return null; } } diff --git a/src/ext/java/org/opentripplanner/ext/vehiclerentalservicedirectory/VehicleRentalServiceDirectoryFetcher.java b/src/ext/java/org/opentripplanner/ext/vehiclerentalservicedirectory/VehicleRentalServiceDirectoryFetcher.java index d51d0d70b6c..05c54178c7b 100644 --- a/src/ext/java/org/opentripplanner/ext/vehiclerentalservicedirectory/VehicleRentalServiceDirectoryFetcher.java +++ b/src/ext/java/org/opentripplanner/ext/vehiclerentalservicedirectory/VehicleRentalServiceDirectoryFetcher.java @@ -61,7 +61,7 @@ public static List createUpdatersFromEndpoint( } int maxHttpConnections = sources.size(); - var otpHttpClient = new OtpHttpClient(maxHttpConnections); + var otpHttpClient = new OtpHttpClient(maxHttpConnections, LOG); var serviceDirectory = new VehicleRentalServiceDirectoryFetcher( vertexLinker, @@ -154,7 +154,7 @@ private VehicleRentalUpdater fetchAndCreateUpdater( private static JsonNode listSources(VehicleRentalServiceDirectoryFetcherParameters parameters) { JsonNode node; URI url = parameters.getUrl(); - try (OtpHttpClient otpHttpClient = new OtpHttpClient()) { + try (OtpHttpClient otpHttpClient = new OtpHttpClient(LOG)) { node = otpHttpClient.getAndMapAsJsonNode(url, Map.of(), new ObjectMapper()); } catch (OtpHttpClientException e) { LOG.warn("Error fetching list of vehicle rental endpoints from {}", url, e); diff --git a/src/main/java/org/opentripplanner/datastore/https/HttpsDataSourceRepository.java b/src/main/java/org/opentripplanner/datastore/https/HttpsDataSourceRepository.java index bf087d0c0ad..402007ea394 100644 --- a/src/main/java/org/opentripplanner/datastore/https/HttpsDataSourceRepository.java +++ b/src/main/java/org/opentripplanner/datastore/https/HttpsDataSourceRepository.java @@ -12,12 +12,16 @@ import org.opentripplanner.datastore.base.DataSourceRepository; import org.opentripplanner.datastore.file.ZipStreamDataSourceDecorator; import org.opentripplanner.framework.io.OtpHttpClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This data store accesses files in read-only mode over HTTPS. */ public class HttpsDataSourceRepository implements DataSourceRepository { + private static final Logger LOG = LoggerFactory.getLogger(HttpsFileDataSource.class); + private static final Duration HTTP_HEAD_REQUEST_TIMEOUT = Duration.ofSeconds(20); @Override @@ -75,7 +79,7 @@ private CompositeDataSource createCompositeSource(URI uri, FileType type) { } protected List

getHttpHeaders(URI uri) { - try (OtpHttpClient otpHttpClient = new OtpHttpClient()) { + try (OtpHttpClient otpHttpClient = new OtpHttpClient(LOG)) { return otpHttpClient.getHeaders(uri, HTTP_HEAD_REQUEST_TIMEOUT, Map.of()); } } diff --git a/src/main/java/org/opentripplanner/datastore/https/HttpsFileDataSource.java b/src/main/java/org/opentripplanner/datastore/https/HttpsFileDataSource.java index 14b770036b8..9b55019332f 100644 --- a/src/main/java/org/opentripplanner/datastore/https/HttpsFileDataSource.java +++ b/src/main/java/org/opentripplanner/datastore/https/HttpsFileDataSource.java @@ -13,6 +13,8 @@ import org.opentripplanner.datastore.file.DirectoryDataSource; import org.opentripplanner.datastore.file.ZipFileDataSource; import org.opentripplanner.framework.io.OtpHttpClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This class is a wrapper around an HTTPS resource. @@ -22,6 +24,8 @@ */ final class HttpsFileDataSource implements DataSource { + private static final Logger LOG = LoggerFactory.getLogger(HttpsFileDataSource.class); + private static final Duration HTTP_GET_REQUEST_TIMEOUT = Duration.ofSeconds(20); private final URI uri; private final FileType type; @@ -35,7 +39,7 @@ final class HttpsFileDataSource implements DataSource { this.uri = uri; this.type = type; this.httpsDataSourceMetadata = httpsDataSourceMetadata; - otpHttpClient = new OtpHttpClient(); + otpHttpClient = new OtpHttpClient(LOG); } /** diff --git a/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java b/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java index bb602adb472..b45604b26aa 100644 --- a/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java +++ b/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java @@ -17,7 +17,7 @@ public class JsonDataListDownloader { - private static final Logger log = LoggerFactory.getLogger(JsonDataListDownloader.class); + private static final Logger LOG = LoggerFactory.getLogger(JsonDataListDownloader.class); private final String jsonParsePath; private final Map headers; private final Function elementParser; @@ -30,7 +30,7 @@ public JsonDataListDownloader( @Nonnull Function elementParser, @Nonnull Map headers ) { - this(url, jsonParsePath, elementParser, headers, new OtpHttpClient()); + this(url, jsonParsePath, elementParser, headers, new OtpHttpClient(LOG)); } public JsonDataListDownloader( @@ -49,7 +49,7 @@ public JsonDataListDownloader( public List download() { if (url == null) { - log.warn("Cannot download updates, because url is null!"); + LOG.warn("Cannot download updates, because url is null!"); return null; } try { @@ -60,17 +60,17 @@ public List download() { try { return parseJSON(is); } catch (IllegalArgumentException e) { - log.warn("Error parsing bike rental feed from {}", url, e); + LOG.warn("Error parsing bike rental feed from {}", url, e); } catch (JsonProcessingException e) { - log.warn("Error parsing bike rental feed from {} (bad JSON of some sort)", url, e); + LOG.warn("Error parsing bike rental feed from {} (bad JSON of some sort)", url, e); } catch (IOException e) { - log.warn("Error reading bike rental feed from {}", url, e); + LOG.warn("Error reading bike rental feed from {}", url, e); } return null; } ); } catch (OtpHttpClientException e) { - log.warn("Failed to get data from url {}", url); + LOG.warn("Failed to get data from url {}", url); return null; } } @@ -111,7 +111,7 @@ private List parseJSON(InputStream dataStream) throws IllegalArgumentExceptio out.add(parsedElement); } } catch (Exception e) { - log.error("Could not process element in JSON list downloaded from {}", url, e); + LOG.error("Could not process element in JSON list downloaded from {}", url, e); } } return out; diff --git a/src/main/java/org/opentripplanner/framework/io/OtpHttpClient.java b/src/main/java/org/opentripplanner/framework/io/OtpHttpClient.java index 1ac8891553c..774dad34a24 100644 --- a/src/main/java/org/opentripplanner/framework/io/OtpHttpClient.java +++ b/src/main/java/org/opentripplanner/framework/io/OtpHttpClient.java @@ -39,7 +39,6 @@ import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * HTTP client providing convenience methods to send HTTP requests and map HTTP responses to Java @@ -79,7 +78,6 @@ */ public class OtpHttpClient implements AutoCloseable { - private static final Logger LOG = LoggerFactory.getLogger(OtpHttpClient.class); private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(5); private static final Duration DEFAULT_TTL = Duration.ofMinutes(1); @@ -91,34 +89,41 @@ public class OtpHttpClient implements AutoCloseable { private final CloseableHttpClient httpClient; + private final Logger log; + /** * Creates an HTTP client with default timeout, default connection time-to-live and default max * number of connections. */ - public OtpHttpClient() { - this(DEFAULT_TIMEOUT, DEFAULT_TTL); + public OtpHttpClient(Logger logger) { + this(DEFAULT_TIMEOUT, DEFAULT_TTL, logger); } /** * Creates an HTTP client with default timeout, default connection time-to-live and the given max * number of connections. */ - public OtpHttpClient(int maxConnections) { - this(DEFAULT_TIMEOUT, DEFAULT_TTL, maxConnections); + public OtpHttpClient(int maxConnections, Logger logger) { + this(DEFAULT_TIMEOUT, DEFAULT_TTL, maxConnections, logger); } /** * Creates an HTTP client the given timeout and connection time-to-live and the default max * number of connections. */ - public OtpHttpClient(Duration timeout, Duration connectionTtl) { - this(timeout, connectionTtl, DEFAULT_MAX_TOTAL_CONNECTIONS); + public OtpHttpClient(Duration timeout, Duration connectionTtl, Logger logger) { + this(timeout, connectionTtl, DEFAULT_MAX_TOTAL_CONNECTIONS, logger); } /** * Creates an HTTP client with custom configuration. */ - private OtpHttpClient(Duration timeout, Duration connectionTtl, int maxConnections) { + private OtpHttpClient( + Duration timeout, + Duration connectionTtl, + int maxConnections, + Logger logger + ) { Objects.requireNonNull(timeout); Objects.requireNonNull(connectionTtl); @@ -145,6 +150,7 @@ private OtpHttpClient(Duration timeout, Duration connectionTtl, int maxConnectio .setDefaultRequestConfig(requestConfig(timeout)); httpClient = httpClientBuilder.build(); + log = logger; } /** @@ -162,7 +168,7 @@ public List
getHeaders( requestHeaderValues, response -> { if (isFailedRequest(response)) { - LOG.warn( + log.warn( "Headers of resource {} unavailable. HTTP error code {}", sanitizeUri(uri), response.getCode() diff --git a/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java b/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java index 21f6c81bc67..ca5434d2e7b 100644 --- a/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java +++ b/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java @@ -50,7 +50,7 @@ public GtfsRealtimeAlertsUpdater( this.updateHandler.setFeedId(config.feedId()); this.updateHandler.setTransitAlertService(transitAlertService); this.updateHandler.setFuzzyTripMatcher(fuzzyTripMatcher); - this.otpHttpClient = new OtpHttpClient(); + this.otpHttpClient = new OtpHttpClient(LOG); LOG.info("Creating real-time alert updater running every {}: {}", pollingPeriod(), url); } diff --git a/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java b/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java index c755578da41..5286173041c 100644 --- a/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java +++ b/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java @@ -28,6 +28,8 @@ import org.opentripplanner.updater.vehicle_position.PollingVehiclePositionUpdater; import org.opentripplanner.updater.vehicle_rental.VehicleRentalUpdater; import org.opentripplanner.updater.vehicle_rental.datasources.VehicleRentalDataSourceFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Sets up and starts all the graph updaters. @@ -38,6 +40,8 @@ */ public class UpdaterConfigurator { + private static final Logger LOG = LoggerFactory.getLogger(UpdaterConfigurator.class); + private final Graph graph; private final TransitModel transitModel; private final UpdatersParameters updatersParameters; @@ -137,7 +141,7 @@ private List createUpdatersFromConfig() { if (!updatersParameters.getVehicleRentalParameters().isEmpty()) { int maxHttpConnections = updatersParameters.getVehicleRentalParameters().size(); - OtpHttpClient otpHttpClient = new OtpHttpClient(maxHttpConnections); + OtpHttpClient otpHttpClient = new OtpHttpClient(maxHttpConnections, LOG); for (var configItem : updatersParameters.getVehicleRentalParameters()) { var source = VehicleRentalDataSourceFactory.create( configItem.sourceParameters(), diff --git a/src/main/java/org/opentripplanner/updater/spi/GenericJsonDataSource.java b/src/main/java/org/opentripplanner/updater/spi/GenericJsonDataSource.java index df275073f72..11e4717df26 100644 --- a/src/main/java/org/opentripplanner/updater/spi/GenericJsonDataSource.java +++ b/src/main/java/org/opentripplanner/updater/spi/GenericJsonDataSource.java @@ -15,7 +15,7 @@ public abstract class GenericJsonDataSource implements DataSource { protected List updates = List.of(); protected GenericJsonDataSource(String url, String jsonParsePath, HttpHeaders headers) { - this(url, jsonParsePath, headers, new OtpHttpClient()); + this(url, jsonParsePath, headers, new OtpHttpClient(LOG)); } protected GenericJsonDataSource( diff --git a/src/main/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSource.java b/src/main/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSource.java index 765f6d11845..3fa9fc6e779 100644 --- a/src/main/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSource.java +++ b/src/main/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSource.java @@ -33,7 +33,7 @@ public GtfsRealtimeTripUpdateSource(PollingTripUpdaterParameters config) { this.url = config.url(); this.headers = HttpHeaders.of().acceptProtobuf().add(config.headers()).build(); MfdzRealtimeExtensions.registerAllExtensions(registry); - otpHttpClient = new OtpHttpClient(); + otpHttpClient = new OtpHttpClient(LOG); } public List getUpdates() { diff --git a/src/main/java/org/opentripplanner/updater/vehicle_position/GtfsRealtimeHttpVehiclePositionSource.java b/src/main/java/org/opentripplanner/updater/vehicle_position/GtfsRealtimeHttpVehiclePositionSource.java index 5983eef7c40..d68a67eaa9a 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_position/GtfsRealtimeHttpVehiclePositionSource.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_position/GtfsRealtimeHttpVehiclePositionSource.java @@ -33,7 +33,7 @@ public class GtfsRealtimeHttpVehiclePositionSource { public GtfsRealtimeHttpVehiclePositionSource(URI url, HttpHeaders headers) { this.url = url; this.headers = HttpHeaders.of().acceptProtobuf().add(headers).build(); - this.otpHttpClient = new OtpHttpClient(); + this.otpHttpClient = new OtpHttpClient(LOG); } /** diff --git a/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFeedLoader.java b/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFeedLoader.java index 58a625192f9..87f58f24f85 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFeedLoader.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFeedLoader.java @@ -98,7 +98,7 @@ public GbfsFeedLoader( } GbfsFeedLoader(String url, HttpHeaders httpHeaders, String languageCode) { - this(url, httpHeaders, languageCode, new OtpHttpClient()); + this(url, httpHeaders, languageCode, new OtpHttpClient(LOG)); } /** diff --git a/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFeedLoaderTest.java b/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFeedLoaderTest.java index 5bb9a3a750c..2c5c36b29e7 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFeedLoaderTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFeedLoaderTest.java @@ -30,6 +30,8 @@ import org.junit.jupiter.api.Test; import org.opentripplanner.framework.io.OtpHttpClient; import org.opentripplanner.updater.spi.HttpHeaders; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This tests that {@link GbfsFeedLoader} handles loading of different versions of GBFS correctly, @@ -90,7 +92,11 @@ void getV10FeedWithExplicitLanguage() { @Test @Disabled void fetchAllPublicFeeds() { - try (OtpHttpClient otpHttpClient = new OtpHttpClient()) { + try ( + OtpHttpClient otpHttpClient = new OtpHttpClient( + LoggerFactory.getLogger(GbfsFeedLoaderTest.class) + ) + ) { List exceptions = otpHttpClient.getAndMap( URI.create("https://raw.githubusercontent.com/NABSA/gbfs/master/systems.csv"), Map.of(), diff --git a/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSourceTest.java b/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSourceTest.java index dba2e10510b..9ac5f2d44d2 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSourceTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSourceTest.java @@ -16,12 +16,16 @@ import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; import org.opentripplanner.updater.spi.HttpHeaders; import org.opentripplanner.updater.vehicle_rental.datasources.params.GbfsVehicleRentalDataSourceParameters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This tests the mapping between data coming from a {@link GbfsFeedLoader} to OTP station models. */ class GbfsVehicleRentalDataSourceTest { + private static final Logger LOG = LoggerFactory.getLogger(GbfsVehicleRentalDataSourceTest.class); + @Test void makeStationFromV22() { var dataSource = new GbfsVehicleRentalDataSource( @@ -34,7 +38,7 @@ void makeStationFromV22() { false, false ), - new OtpHttpClient() + new OtpHttpClient(LOG) ); dataSource.setup(); @@ -125,7 +129,7 @@ void geofencing() { true, false ), - new OtpHttpClient() + new OtpHttpClient(LOG) ); dataSource.setup(); @@ -167,7 +171,7 @@ void makeStationFromV10() { false, true ), - new OtpHttpClient() + new OtpHttpClient(LOG) ); dataSource.setup(); From 78aaa64004607af2f4b52afe596b2307b939eb0c Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Mon, 8 Apr 2024 15:19:41 +0300 Subject: [PATCH 20/85] Trace log error response --- .../framework/io/OtpHttpClient.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/main/java/org/opentripplanner/framework/io/OtpHttpClient.java b/src/main/java/org/opentripplanner/framework/io/OtpHttpClient.java index 774dad34a24..959f2cbb496 100644 --- a/src/main/java/org/opentripplanner/framework/io/OtpHttpClient.java +++ b/src/main/java/org/opentripplanner/framework/io/OtpHttpClient.java @@ -2,8 +2,10 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; @@ -14,6 +16,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; @@ -168,6 +171,7 @@ public List
getHeaders( requestHeaderValues, response -> { if (isFailedRequest(response)) { + logResponse(response); log.warn( "Headers of resource {} unavailable. HTTP error code {}", sanitizeUri(uri), @@ -422,6 +426,7 @@ protected T executeAndMapWithResponseHandler( private T mapResponse(ClassicHttpResponse response, ResponseMapper contentMapper) { if (isFailedRequest(response)) { + logResponse(response); throw new OtpHttpClientException( "HTTP request failed with status code " + response.getCode() ); @@ -491,6 +496,24 @@ private static String sanitizeUri(URI uri) { return uri.toString().replace('?' + uri.getQuery(), ""); } + private void logResponse(ClassicHttpResponse response) { + try { + if ( + log.isTraceEnabled() && + response.getEntity() != null && + response.getEntity().getContent() != null + ) { + var entity = response.getEntity(); + String content = new BufferedReader(new InputStreamReader(entity.getContent())) + .lines() + .collect(Collectors.joining("\n")); + log.trace("HTTP request failed with status code {}: \n{}", response.getCode(), content); + } + } catch (Exception e) { + log.debug(e.getMessage()); + } + } + @FunctionalInterface public interface ResponseMapper { /** From 1c66f062eac75e8da0db9f3e1b03e91a709f5b39 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 10 Apr 2024 11:37:32 +0200 Subject: [PATCH 21/85] Apply review feedback about documentation --- .../module/osm/naming/SidewalkNamer.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index 60d0f267337..049c3cfee24 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -102,8 +102,8 @@ public void postprocess() { .forEach(sidewalkOnLevel -> { assignNameToSidewalk(sidewalkOnLevel, namesApplied); - //Keep lambda! A method-ref would cause incorrect class and line number to be logged - //noinspection Convert2MethodRef + // Keep lambda! A method-ref would cause incorrect class and line number to be logged + // noinspection Convert2MethodRef progress.step(m -> LOG.info(m)); }); @@ -139,9 +139,9 @@ private void assignNameToSidewalk(EdgeOnLevel sidewalkOnLevel, AtomicInteger nam // make sure we only compare sidewalks and streets that are on the same level .filter(g -> g.levels.equals(sidewalkOnLevel.levels)) .map(g -> computePercentInsideBuffer(g, buffer, sidewalkLength)) - // remove those groups where less than a certain percentage is inside the buffer around - // the sidewalk. this safety mechanism for sidewalks that snake around the corner - // like https://www.openstreetmap.org/way/1059101564 + // Remove those groups where less than a certain percentage is inside the buffer around + // the sidewalk. This is a safety mechanism for sidewalks that snake around the corner, + // like https://www.openstreetmap.org/way/1059101564 . .filter(group -> group.percentInBuffer > MIN_PERCENT_IN_BUFFER) .max(Comparator.comparingDouble(NamedEdgeGroup::percentInBuffer)) .ifPresent(group -> { @@ -152,7 +152,7 @@ private void assignNameToSidewalk(EdgeOnLevel sidewalkOnLevel, AtomicInteger nam /** * Compute the length of the group that is inside the buffer and return it as a percentage - * of the lenght of the sidewalk. + * of the length of the sidewalk. */ private static NamedEdgeGroup computePercentInsideBuffer( CandidateGroup g, @@ -165,8 +165,8 @@ private static NamedEdgeGroup computePercentInsideBuffer( } /** - * If a single street is split into several eges each individual part of the street would potentially - * have a low similarity with the (longer) sidewalk. For that reason we combined them into a group + * If a single street is split into several edges, each individual part of the street would potentially + * have a low similarity with the (longer) sidewalk. For that reason we combine them into a group * and have a better basis for comparison. */ private static Stream groupEdgesByName(List candidates) { @@ -190,12 +190,12 @@ private static Stream groupEdgesByName(List candida } /** - * Add a buffer around a geometry that uses both meters as the input and makes sure - * that the buffer is the same distance (in meters) anywhere on earth. + * Add a buffer around a geometry that makes sure that the buffer is the same distance (in meters) + * anywhere on earth. *

* Background: If you call the regular buffer() method on a JTS geometry that uses WGS84 as the * coordinate reference system, the buffer will be accurate at the equator but will become more - * and more elongated the furter north/south you go. + * and more elongated the further north/south you go. *

* Taken from https://stackoverflow.com/questions/36455020 */ @@ -229,7 +229,7 @@ private record NamedEdgeGroup(double percentInBuffer, I18NString name) { */ private record CandidateGroup(I18NString name, List edges, Set levels) { /** - * How much of this group intersects with the give geometry, in meters. + * How much of this group intersects with the given geometry, in meters. */ double intersectionLength(Geometry polygon) { return edges From c53e83801fb45ed030af0c920cac4df16e3a2577 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 12 Apr 2024 08:25:30 +0200 Subject: [PATCH 22/85] Fix more typos --- .../graph_builder/module/osm/naming/SidewalkNamer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index 049c3cfee24..e6eab4d3d0e 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -141,7 +141,7 @@ private void assignNameToSidewalk(EdgeOnLevel sidewalkOnLevel, AtomicInteger nam .map(g -> computePercentInsideBuffer(g, buffer, sidewalkLength)) // Remove those groups where less than a certain percentage is inside the buffer around // the sidewalk. This is a safety mechanism for sidewalks that snake around the corner, - // like https://www.openstreetmap.org/way/1059101564 . + // like https://www.openstreetmap.org/way/1059101564. .filter(group -> group.percentInBuffer > MIN_PERCENT_IN_BUFFER) .max(Comparator.comparingDouble(NamedEdgeGroup::percentInBuffer)) .ifPresent(group -> { @@ -195,7 +195,7 @@ private static Stream groupEdgesByName(List candida *

* Background: If you call the regular buffer() method on a JTS geometry that uses WGS84 as the * coordinate reference system, the buffer will be accurate at the equator but will become more - * and more elongated the further north/south you go. + * and more elongated the farther north/south you go. *

* Taken from https://stackoverflow.com/questions/36455020 */ From b1ed4fffe80281836c9c4362c851e24edd56700f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 12 Feb 2024 17:59:06 +0100 Subject: [PATCH 23/85] Add flex duration factors and offsets --- .../gtfs/mapping/StopTimeMapper.java | 16 +++++++ .../org/opentripplanner/model/StopTime.java | 47 ++++++++++--------- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java index fc75236f4e4..2c01dda3e1b 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java @@ -1,5 +1,8 @@ package org.opentripplanner.gtfs.mapping; +import static org.onebusaway.gtfs.model.StopTime.MISSING_VALUE; + +import java.time.Duration; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -102,6 +105,19 @@ private StopTime doMap(org.onebusaway.gtfs.model.StopTime rhs) { lhs.setFarePeriodId(rhs.getFarePeriodId()); lhs.setFlexWindowStart(rhs.getStartPickupDropOffWindow()); lhs.setFlexWindowEnd(rhs.getEndPickupDropOffWindow()); + if(rhs.getMeanDurationOffset() != MISSING_VALUE) { + lhs.setMeanDurationFactor(rhs.getMeanDurationFactor()); + } + if(rhs.getMeanDurationOffset() != MISSING_VALUE) { + lhs.setMeanDurationOffset(Duration.ofSeconds((long) rhs.getMeanDurationOffset())); + } + if(rhs.getSafeDurationFactor() != MISSING_VALUE){ + lhs.setSafeDurationFactor(rhs.getSafeDurationFactor()); + } + if(rhs.getSafeDurationOffset()!= MISSING_VALUE){ + lhs.setSafeDurationOffset(Duration.ofSeconds((long) rhs.getSafeDurationOffset())); + } + lhs.setFlexContinuousPickup( PickDropMapper.mapFlexContinuousPickDrop(rhs.getContinuousPickup()) ); diff --git a/src/main/java/org/opentripplanner/model/StopTime.java b/src/main/java/org/opentripplanner/model/StopTime.java index 2ae04484426..8320e532c2c 100644 --- a/src/main/java/org/opentripplanner/model/StopTime.java +++ b/src/main/java/org/opentripplanner/model/StopTime.java @@ -1,6 +1,7 @@ /* This file is based on code copied from project OneBusAway, see the LICENSE file for further information. */ package org.opentripplanner.model; +import java.time.Duration; import java.util.List; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.time.TimeUtils; @@ -50,6 +51,14 @@ public final class StopTime implements Comparable { private int flexWindowEnd = MISSING_VALUE; + private double meanDurationFactor = MISSING_VALUE; + + private Duration meanDurationOffset = Duration.ZERO; + + private double safeDurationFactor = MISSING_VALUE; + + private Duration safeDurationOffset = Duration.ZERO; + // Disabled by default private PickDrop flexContinuousPickup = PickDrop.NONE; @@ -62,28 +71,6 @@ public final class StopTime implements Comparable { public StopTime() {} - public StopTime(StopTime st) { - this.trip = st.trip; - this.stop = st.stop; - this.arrivalTime = st.arrivalTime; - this.departureTime = st.departureTime; - this.timepoint = st.timepoint; - this.stopSequence = st.stopSequence; - this.stopHeadsign = st.stopHeadsign; - this.routeShortName = st.routeShortName; - this.pickupType = st.pickupType; - this.dropOffType = st.dropOffType; - this.shapeDistTraveled = st.shapeDistTraveled; - this.farePeriodId = st.farePeriodId; - this.flexWindowStart = st.flexWindowStart; - this.flexWindowEnd = st.flexWindowEnd; - this.flexContinuousPickup = st.flexContinuousPickup; - this.flexContinuousDropOff = st.flexContinuousDropOff; - this.dropOffBookingInfo = st.dropOffBookingInfo; - this.pickupBookingInfo = st.pickupBookingInfo; - this.headsignVias = st.headsignVias; - } - /** * The id is used to navigate/link StopTime to other entities (Map from StopTime.id -> Entity.id). * There is no need to navigate in the opposite direction. The StopTime id is NOT stored in a @@ -310,6 +297,22 @@ public String toString() { ); } + public void setMeanDurationFactor(double meanDurationFactor) { + this.meanDurationFactor = meanDurationFactor; + } + + public void setMeanDurationOffset(Duration meanDurationOffset) { + this.meanDurationOffset = meanDurationOffset; + } + + public void setSafeDurationOffset(Duration safeDurationOffset) { + this.safeDurationOffset = safeDurationOffset; + } + + public void setSafeDurationFactor(double safeDurationFactor) { + this.safeDurationFactor = safeDurationFactor; + } + private static int getAvailableTime(int... times) { for (var time : times) { if (time != MISSING_VALUE) { From 95527bbac94835a4032e43a34eb5872bed8bbb25 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 14 Feb 2024 17:23:12 +0100 Subject: [PATCH 24/85] Add initial implementation of flex duration factors --- .../DurationFactorCalculator.java | 26 +++++++++++++++++++ .../ext/flex/flexpathcalculator/FlexPath.java | 6 +++++ .../ext/flex/trip/StopTimeWindow.java | 13 ++++++++++ .../ext/flex/trip/UnscheduledTrip.java | 12 +++++---- .../gtfs/mapping/StopTimeMapper.java | 8 +++--- .../org/opentripplanner/model/StopTime.java | 15 +++++++++++ 6 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java new file mode 100644 index 00000000000..c4bc23848d7 --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java @@ -0,0 +1,26 @@ +package org.opentripplanner.ext.flex.flexpathcalculator; + +import java.time.Duration; +import javax.annotation.Nullable; +import org.opentripplanner.street.model.vertex.Vertex; + +public class DurationFactorCalculator implements FlexPathCalculator { + + private final FlexPathCalculator underlying; + + public DurationFactorCalculator(FlexPathCalculator underlying) { + this.underlying = underlying; + } + + @Nullable + @Override + public FlexPath calculateFlexPath(Vertex fromv, Vertex tov, int fromStopIndex, int toStopIndex) { + var path = underlying.calculateFlexPath(fromv, tov, fromStopIndex, toStopIndex); + + if (path == null) { + return null; + } else { + return path.withDurationFactors(1.5f, Duration.ofMinutes(10)); + } + } +} diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java index 5c5557890d6..c36a59524d1 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java @@ -1,5 +1,6 @@ package org.opentripplanner.ext.flex.flexpathcalculator; +import java.time.Duration; import java.util.function.Supplier; import org.locationtech.jts.geom.LineString; @@ -32,4 +33,9 @@ public LineString getGeometry() { } return geometry; } + + public FlexPath withDurationFactors(float factor, Duration offset) { + int updatedDuration = (int) ((durationSeconds * factor) + offset.toSeconds()); + return new FlexPath(distanceMeters, updatedDuration, geometrySupplier); + } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/StopTimeWindow.java b/src/ext/java/org/opentripplanner/ext/flex/trip/StopTimeWindow.java index 5b7ebbbc575..821fe290cbb 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/StopTimeWindow.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/StopTimeWindow.java @@ -3,6 +3,7 @@ import static org.opentripplanner.model.StopTime.MISSING_VALUE; import java.io.Serializable; +import java.util.OptionalDouble; import org.opentripplanner.framework.lang.IntRange; import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.StopTime; @@ -18,6 +19,8 @@ class StopTimeWindow implements Serializable { private final PickDrop pickupType; private final PickDrop dropOffType; + private final double meanDurationFactor; + StopTimeWindow(StopTime st) { stop = st.getStop(); @@ -32,6 +35,12 @@ class StopTimeWindow implements Serializable { // Do not allow for pickup/dropoff if times are not available pickupType = start == MISSING_VALUE ? PickDrop.NONE : st.getPickupType(); dropOffType = end == MISSING_VALUE ? PickDrop.NONE : st.getDropOffType(); + + if (st.meanDurationFactor().isPresent()) { + meanDurationFactor = st.meanDurationFactor().getAsDouble(); + } else { + meanDurationFactor = 1; + } } public StopLocation stop() { @@ -58,6 +67,10 @@ public IntRange timeWindow() { return IntRange.ofInclusive(start, end); } + public double meanDurationFactor() { + return meanDurationFactor; + } + private static int getAvailableTime(int... times) { for (var time : times) { if (time != MISSING_VALUE) { diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index c5968507676..10b00b6ab98 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -14,6 +14,7 @@ import java.util.stream.Stream; import javax.annotation.Nonnull; import org.opentripplanner.ext.flex.FlexServiceDate; +import org.opentripplanner.ext.flex.flexpathcalculator.DurationFactorCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; import org.opentripplanner.ext.flex.template.FlexAccessTemplate; import org.opentripplanner.ext.flex.template.FlexEgressTemplate; @@ -81,8 +82,6 @@ public static UnscheduledTripBuilder of(FeedScopedId id) { * - One or more stop times with a flexible time window but no fixed stop in between them */ public static boolean isUnscheduledTrip(List stopTimes) { - Predicate hasFlexWindow = st -> - st.getFlexWindowStart() != MISSING_VALUE || st.getFlexWindowEnd() != MISSING_VALUE; Predicate hasContinuousStops = stopTime -> stopTime.getFlexContinuousDropOff() != NONE || stopTime.getFlexContinuousPickup() != NONE; if (stopTimes.isEmpty()) { @@ -90,9 +89,9 @@ public static boolean isUnscheduledTrip(List stopTimes) { } else if (stopTimes.stream().anyMatch(hasContinuousStops)) { return false; } else if (N_STOPS.contains(stopTimes.size())) { - return stopTimes.stream().anyMatch(hasFlexWindow); + return stopTimes.stream().anyMatch(StopTime::hasFlexWindow); } else { - return stopTimes.stream().allMatch(hasFlexWindow); + return stopTimes.stream().allMatch(StopTime::hasFlexWindow); } } @@ -120,6 +119,9 @@ public Stream getFlexAccessTemplates( } else { indices = IntStream.range(fromIndex + 1, lastIndexInTrip + 1); } + + var updatedCalculator = new DurationFactorCalculator(calculator); + // check for every stop after fromIndex if you can alight, if so return a template return indices // if you cannot alight at an index, the trip is not possible @@ -137,7 +139,7 @@ public Stream getFlexAccessTemplates( alightStop.index, alightStop.stop, date, - calculator, + updatedCalculator, config ) ); diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java index 2c01dda3e1b..0a1f6269e4f 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java @@ -105,16 +105,16 @@ private StopTime doMap(org.onebusaway.gtfs.model.StopTime rhs) { lhs.setFarePeriodId(rhs.getFarePeriodId()); lhs.setFlexWindowStart(rhs.getStartPickupDropOffWindow()); lhs.setFlexWindowEnd(rhs.getEndPickupDropOffWindow()); - if(rhs.getMeanDurationOffset() != MISSING_VALUE) { + if (rhs.getMeanDurationOffset() != MISSING_VALUE) { lhs.setMeanDurationFactor(rhs.getMeanDurationFactor()); } - if(rhs.getMeanDurationOffset() != MISSING_VALUE) { + if (rhs.getMeanDurationOffset() != MISSING_VALUE) { lhs.setMeanDurationOffset(Duration.ofSeconds((long) rhs.getMeanDurationOffset())); } - if(rhs.getSafeDurationFactor() != MISSING_VALUE){ + if (rhs.getSafeDurationFactor() != MISSING_VALUE) { lhs.setSafeDurationFactor(rhs.getSafeDurationFactor()); } - if(rhs.getSafeDurationOffset()!= MISSING_VALUE){ + if (rhs.getSafeDurationOffset() != MISSING_VALUE) { lhs.setSafeDurationOffset(Duration.ofSeconds((long) rhs.getSafeDurationOffset())); } diff --git a/src/main/java/org/opentripplanner/model/StopTime.java b/src/main/java/org/opentripplanner/model/StopTime.java index 8320e532c2c..64045bbbbfe 100644 --- a/src/main/java/org/opentripplanner/model/StopTime.java +++ b/src/main/java/org/opentripplanner/model/StopTime.java @@ -3,6 +3,7 @@ import java.time.Duration; import java.util.List; +import java.util.OptionalDouble; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.time.TimeUtils; import org.opentripplanner.transit.model.site.StopLocation; @@ -313,6 +314,13 @@ public void setSafeDurationFactor(double safeDurationFactor) { this.safeDurationFactor = safeDurationFactor; } + public OptionalDouble meanDurationFactor() { + if (meanDurationFactor == MISSING_VALUE) { + return OptionalDouble.empty(); + } + return OptionalDouble.of(meanDurationFactor); + } + private static int getAvailableTime(int... times) { for (var time : times) { if (time != MISSING_VALUE) { @@ -322,4 +330,11 @@ private static int getAvailableTime(int... times) { return MISSING_VALUE; } + + /** + * Does this stop time define a flex window? + */ + public boolean hasFlexWindow() { + return flexWindowStart != MISSING_VALUE || flexWindowEnd != MISSING_VALUE; + } } From ea0960aa70e7cceff521777fd9ebb9f69ea50e75 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 15 Mar 2024 09:27:24 +0100 Subject: [PATCH 25/85] Remove stop-time-based factors --- .../ext/flex/trip/StopTimeWindow.java | 13 -------- .../gtfs/mapping/StopTimeMapper.java | 15 --------- .../org/opentripplanner/model/StopTime.java | 33 ------------------- 3 files changed, 61 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/StopTimeWindow.java b/src/ext/java/org/opentripplanner/ext/flex/trip/StopTimeWindow.java index 821fe290cbb..5b7ebbbc575 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/StopTimeWindow.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/StopTimeWindow.java @@ -3,7 +3,6 @@ import static org.opentripplanner.model.StopTime.MISSING_VALUE; import java.io.Serializable; -import java.util.OptionalDouble; import org.opentripplanner.framework.lang.IntRange; import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.StopTime; @@ -19,8 +18,6 @@ class StopTimeWindow implements Serializable { private final PickDrop pickupType; private final PickDrop dropOffType; - private final double meanDurationFactor; - StopTimeWindow(StopTime st) { stop = st.getStop(); @@ -35,12 +32,6 @@ class StopTimeWindow implements Serializable { // Do not allow for pickup/dropoff if times are not available pickupType = start == MISSING_VALUE ? PickDrop.NONE : st.getPickupType(); dropOffType = end == MISSING_VALUE ? PickDrop.NONE : st.getDropOffType(); - - if (st.meanDurationFactor().isPresent()) { - meanDurationFactor = st.meanDurationFactor().getAsDouble(); - } else { - meanDurationFactor = 1; - } } public StopLocation stop() { @@ -67,10 +58,6 @@ public IntRange timeWindow() { return IntRange.ofInclusive(start, end); } - public double meanDurationFactor() { - return meanDurationFactor; - } - private static int getAvailableTime(int... times) { for (var time : times) { if (time != MISSING_VALUE) { diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java index 0a1f6269e4f..67b250c5061 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java @@ -1,8 +1,5 @@ package org.opentripplanner.gtfs.mapping; -import static org.onebusaway.gtfs.model.StopTime.MISSING_VALUE; - -import java.time.Duration; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -105,18 +102,6 @@ private StopTime doMap(org.onebusaway.gtfs.model.StopTime rhs) { lhs.setFarePeriodId(rhs.getFarePeriodId()); lhs.setFlexWindowStart(rhs.getStartPickupDropOffWindow()); lhs.setFlexWindowEnd(rhs.getEndPickupDropOffWindow()); - if (rhs.getMeanDurationOffset() != MISSING_VALUE) { - lhs.setMeanDurationFactor(rhs.getMeanDurationFactor()); - } - if (rhs.getMeanDurationOffset() != MISSING_VALUE) { - lhs.setMeanDurationOffset(Duration.ofSeconds((long) rhs.getMeanDurationOffset())); - } - if (rhs.getSafeDurationFactor() != MISSING_VALUE) { - lhs.setSafeDurationFactor(rhs.getSafeDurationFactor()); - } - if (rhs.getSafeDurationOffset() != MISSING_VALUE) { - lhs.setSafeDurationOffset(Duration.ofSeconds((long) rhs.getSafeDurationOffset())); - } lhs.setFlexContinuousPickup( PickDropMapper.mapFlexContinuousPickDrop(rhs.getContinuousPickup()) diff --git a/src/main/java/org/opentripplanner/model/StopTime.java b/src/main/java/org/opentripplanner/model/StopTime.java index 64045bbbbfe..e753b8d2885 100644 --- a/src/main/java/org/opentripplanner/model/StopTime.java +++ b/src/main/java/org/opentripplanner/model/StopTime.java @@ -1,9 +1,7 @@ /* This file is based on code copied from project OneBusAway, see the LICENSE file for further information. */ package org.opentripplanner.model; -import java.time.Duration; import java.util.List; -import java.util.OptionalDouble; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.time.TimeUtils; import org.opentripplanner.transit.model.site.StopLocation; @@ -52,14 +50,6 @@ public final class StopTime implements Comparable { private int flexWindowEnd = MISSING_VALUE; - private double meanDurationFactor = MISSING_VALUE; - - private Duration meanDurationOffset = Duration.ZERO; - - private double safeDurationFactor = MISSING_VALUE; - - private Duration safeDurationOffset = Duration.ZERO; - // Disabled by default private PickDrop flexContinuousPickup = PickDrop.NONE; @@ -298,29 +288,6 @@ public String toString() { ); } - public void setMeanDurationFactor(double meanDurationFactor) { - this.meanDurationFactor = meanDurationFactor; - } - - public void setMeanDurationOffset(Duration meanDurationOffset) { - this.meanDurationOffset = meanDurationOffset; - } - - public void setSafeDurationOffset(Duration safeDurationOffset) { - this.safeDurationOffset = safeDurationOffset; - } - - public void setSafeDurationFactor(double safeDurationFactor) { - this.safeDurationFactor = safeDurationFactor; - } - - public OptionalDouble meanDurationFactor() { - if (meanDurationFactor == MISSING_VALUE) { - return OptionalDouble.empty(); - } - return OptionalDouble.of(meanDurationFactor); - } - private static int getAvailableTime(int... times) { for (var time : times) { if (time != MISSING_VALUE) { From a0eb06655a65deb18005d9ba5f2441c072d3fa59 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 15 Mar 2024 10:29:47 +0100 Subject: [PATCH 26/85] Move factors into calcultor --- .../DurationFactorCalculator.java | 14 +++++++++----- .../ext/flex/trip/UnscheduledTrip.java | 3 ++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java index c4bc23848d7..24a4d665fe6 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java @@ -6,21 +6,25 @@ public class DurationFactorCalculator implements FlexPathCalculator { - private final FlexPathCalculator underlying; + private final FlexPathCalculator delegate; + private final float factor; + private final Duration offset; - public DurationFactorCalculator(FlexPathCalculator underlying) { - this.underlying = underlying; + public DurationFactorCalculator(FlexPathCalculator delegate, float factor, Duration offset) { + this.delegate = delegate; + this.factor = factor; + this.offset = offset; } @Nullable @Override public FlexPath calculateFlexPath(Vertex fromv, Vertex tov, int fromStopIndex, int toStopIndex) { - var path = underlying.calculateFlexPath(fromv, tov, fromStopIndex, toStopIndex); + var path = delegate.calculateFlexPath(fromv, tov, fromStopIndex, toStopIndex); if (path == null) { return null; } else { - return path.withDurationFactors(1.5f, Duration.ofMinutes(10)); + return path.withDurationFactors(factor, offset); } } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index 10b00b6ab98..e2ab49045d6 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -3,6 +3,7 @@ import static org.opentripplanner.model.PickDrop.NONE; import static org.opentripplanner.model.StopTime.MISSING_VALUE; +import java.time.Duration; import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -120,7 +121,7 @@ public Stream getFlexAccessTemplates( indices = IntStream.range(fromIndex + 1, lastIndexInTrip + 1); } - var updatedCalculator = new DurationFactorCalculator(calculator); + var updatedCalculator = new DurationFactorCalculator(calculator, 1.5f, Duration.ofMinutes(20)); // check for every stop after fromIndex if you can alight, if so return a template return indices From cc7601275a2ba2551028b40d229da7afd799188c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 15 Mar 2024 14:24:04 +0100 Subject: [PATCH 27/85] Encapsulate factors, add parsing --- .../ext/flex/FlexTripsMapper.java | 9 ++++++- .../DurationFactorCalculator.java | 12 ++++----- .../ext/flex/flexpathcalculator/FlexPath.java | 6 ++--- .../ext/flex/trip/FlexDurationFactors.java | 27 +++++++++++++++++++ .../ext/flex/trip/UnscheduledTrip.java | 15 +++++++++-- .../ext/flex/trip/UnscheduledTripBuilder.java | 10 +++++++ .../GTFSToOtpTransitServiceMapper.java | 1 + .../gtfs/mapping/TripMapper.java | 26 ++++++++++++++++-- .../model/impl/OtpTransitServiceBuilder.java | 9 +++++++ 9 files changed, 100 insertions(+), 15 deletions(-) create mode 100644 src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index f5ab34cce49..d4918e8ed33 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; +import org.opentripplanner.ext.flex.trip.FlexDurationFactors; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip; import org.opentripplanner.ext.flex.trip.UnscheduledTrip; @@ -34,10 +35,16 @@ public class FlexTripsMapper { for (Trip trip : stopTimesByTrip.keys()) { /* Fetch the stop times for this trip. Copy the list since it's immutable. */ List stopTimes = new ArrayList<>(stopTimesByTrip.get(trip)); + var factors = builder.getFlexDurationFactors().getOrDefault(trip, FlexDurationFactors.ZERO); if (UnscheduledTrip.isUnscheduledTrip(stopTimes)) { result.add( - UnscheduledTrip.of(trip.getId()).withTrip(trip).withStopTimes(stopTimes).build() + UnscheduledTrip + .of(trip.getId()) + .withTrip(trip) + .withStopTimes(stopTimes) + .withDurationFactors(factors) + .build() ); } else if (ScheduledDeviatedTrip.isScheduledFlexTrip(stopTimes)) { result.add( diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java index 24a4d665fe6..3e5a6b1db79 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java @@ -1,19 +1,17 @@ package org.opentripplanner.ext.flex.flexpathcalculator; -import java.time.Duration; import javax.annotation.Nullable; +import org.opentripplanner.ext.flex.trip.FlexDurationFactors; import org.opentripplanner.street.model.vertex.Vertex; public class DurationFactorCalculator implements FlexPathCalculator { private final FlexPathCalculator delegate; - private final float factor; - private final Duration offset; + private final FlexDurationFactors factors; - public DurationFactorCalculator(FlexPathCalculator delegate, float factor, Duration offset) { + public DurationFactorCalculator(FlexPathCalculator delegate, FlexDurationFactors factors) { this.delegate = delegate; - this.factor = factor; - this.offset = offset; + this.factors = factors; } @Nullable @@ -24,7 +22,7 @@ public FlexPath calculateFlexPath(Vertex fromv, Vertex tov, int fromStopIndex, i if (path == null) { return null; } else { - return path.withDurationFactors(factor, offset); + return path.withDurationFactors(factors); } } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java index c36a59524d1..7ba3aad43d4 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java @@ -1,8 +1,8 @@ package org.opentripplanner.ext.flex.flexpathcalculator; -import java.time.Duration; import java.util.function.Supplier; import org.locationtech.jts.geom.LineString; +import org.opentripplanner.ext.flex.trip.FlexDurationFactors; /** * This class contains the results from a FlexPathCalculator. @@ -34,8 +34,8 @@ public LineString getGeometry() { return geometry; } - public FlexPath withDurationFactors(float factor, Duration offset) { - int updatedDuration = (int) ((durationSeconds * factor) + offset.toSeconds()); + public FlexPath withDurationFactors(FlexDurationFactors factors) { + int updatedDuration = (int) ((durationSeconds * factors.factor()) + factors.offsetInSeconds()); return new FlexPath(distanceMeters, updatedDuration, geometrySupplier); } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java b/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java new file mode 100644 index 00000000000..41b610730d4 --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java @@ -0,0 +1,27 @@ +package org.opentripplanner.ext.flex.trip; + +import java.time.Duration; + +public class FlexDurationFactors { + + public static FlexDurationFactors ZERO = new FlexDurationFactors(Duration.ZERO, 1); + private final int offset; + private final float factor; + + public FlexDurationFactors(Duration offset, float factor) { + this.offset = (int) offset.toSeconds(); + this.factor = factor; + } + + public float factor() { + return factor; + } + + public int offsetInSeconds() { + return offset; + } + + boolean nonZero() { + return offset != 0 && factor != 1.0; + } +} diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index e2ab49045d6..9c71696c02b 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -3,9 +3,9 @@ import static org.opentripplanner.model.PickDrop.NONE; import static org.opentripplanner.model.StopTime.MISSING_VALUE; -import java.time.Duration; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.Function; @@ -52,6 +52,8 @@ public class UnscheduledTrip extends FlexTrip stopTimes = builder.stopTimes(); @@ -69,6 +71,7 @@ public UnscheduledTrip(UnscheduledTripBuilder builder) { this.dropOffBookingInfos[i] = stopTimes.get(0).getDropOffBookingInfo(); this.pickupBookingInfos[i] = stopTimes.get(0).getPickupBookingInfo(); } + this.duractionFactors = Objects.requireNonNull(builder.durationFactors()); } public static UnscheduledTripBuilder of(FeedScopedId id) { @@ -121,7 +124,7 @@ public Stream getFlexAccessTemplates( indices = IntStream.range(fromIndex + 1, lastIndexInTrip + 1); } - var updatedCalculator = new DurationFactorCalculator(calculator, 1.5f, Duration.ofMinutes(20)); + final var updatedCalculator = flexPathCalculator(calculator); // check for every stop after fromIndex if you can alight, if so return a template return indices @@ -146,6 +149,14 @@ public Stream getFlexAccessTemplates( ); } + private FlexPathCalculator flexPathCalculator(FlexPathCalculator calculator) { + if (duractionFactors.nonZero()) { + return new DurationFactorCalculator(calculator, duractionFactors); + } else { + return calculator; + } + } + @Override public Stream getFlexEgressTemplates( NearbyStop egress, diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java index 678b7fcce5e..e2d296d8154 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java @@ -8,6 +8,7 @@ public class UnscheduledTripBuilder extends FlexTripBuilder { private List stopTimes; + private FlexDurationFactors durationFactors = FlexDurationFactors.ZERO; UnscheduledTripBuilder(FeedScopedId id) { super(id); @@ -29,6 +30,15 @@ public List stopTimes() { return stopTimes; } + public UnscheduledTripBuilder withDurationFactors(FlexDurationFactors factors) { + this.durationFactors = factors; + return this; + } + + public FlexDurationFactors durationFactors() { + return durationFactors; + } + @Override UnscheduledTripBuilder instance() { return this; diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java index 4d5fe6bd051..15a4c2e28b7 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java @@ -171,6 +171,7 @@ public void mapStopTripAndRouteDataIntoBuilder() { builder.getPathways().addAll(pathwayMapper.map(data.getAllPathways())); builder.getStopTimesSortedByTrip().addAll(stopTimeMapper.map(data.getAllStopTimes())); + builder.getFlexDurationFactors().putAll(tripMapper.flexDurationFactors()); builder.getTripsById().addAll(tripMapper.map(data.getAllTrips())); fareRulesBuilder.fareAttributes().addAll(fareAttributeMapper.map(data.getAllFareAttributes())); diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java index a80ae035ed1..77d6eb879b0 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java @@ -1,8 +1,11 @@ package org.opentripplanner.gtfs.mapping; +import java.time.Duration; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.Optional; +import org.opentripplanner.ext.flex.trip.FlexDurationFactors; import org.opentripplanner.framework.collection.MapUtils; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.transit.model.timetable.Trip; @@ -12,9 +15,10 @@ class TripMapper { private final RouteMapper routeMapper; private final DirectionMapper directionMapper; - private TranslationHelper translationHelper; + private final TranslationHelper translationHelper; private final Map mappedTrips = new HashMap<>(); + private final Map flexDurationFactors = new HashMap<>(); TripMapper( RouteMapper routeMapper, @@ -38,6 +42,13 @@ Collection getMappedTrips() { return mappedTrips.values(); } + /** + * The map of flex duration factors per flex trip. + */ + Map flexDurationFactors() { + return flexDurationFactors; + } + private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { var lhs = Trip.of(AgencyAndIdMapper.mapAgencyAndId(rhs.getId())); @@ -62,6 +73,17 @@ private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { lhs.withBikesAllowed(BikeAccessMapper.mapForTrip(rhs)); lhs.withGtfsFareId(rhs.getFareId()); - return lhs.build(); + var trip = lhs.build(); + mapFlexDurationFactots(rhs).ifPresent(f -> flexDurationFactors.put(trip, f)); + return trip; + } + + private Optional mapFlexDurationFactots(org.onebusaway.gtfs.model.Trip rhs) { + if (rhs.getMeanDurationFactor() == null && rhs.getMeanDurationOffset() == null) { + return Optional.empty(); + } else { + var offset = Duration.ofSeconds(rhs.getMeanDurationOffset().longValue()); + return Optional.of(new FlexDurationFactors(offset, rhs.getMeanDurationFactor().floatValue())); + } } } diff --git a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java index 382d6042a05..e4d0d76cbe3 100644 --- a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java +++ b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java @@ -3,10 +3,13 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; +import org.opentripplanner.ext.flex.trip.FlexDurationFactors; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.gtfs.mapping.StaySeatedNotAllowed; @@ -92,6 +95,8 @@ public class OtpTransitServiceBuilder { private final TripStopTimes stopTimesByTrip = new TripStopTimes(); + private final Map flexDurationFactors = new HashMap<>(); + private final EntityById fareZonesById = new DefaultEntityById<>(); private final List transfers = new ArrayList<>(); @@ -209,6 +214,10 @@ public TripStopTimes getStopTimesSortedByTrip() { return stopTimesByTrip; } + public Map getFlexDurationFactors() { + return flexDurationFactors; + } + public EntityById getFareZonesById() { return fareZonesById; } From dca331bff6144a4d51902bcfe78115bac00c40ee Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 15 Mar 2024 15:07:31 +0100 Subject: [PATCH 28/85] Make factors serializable --- .../ext/flex/FlexTripsMapper.java | 2 +- .../ext/flex/trip/FlexDurationFactors.java | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index d4918e8ed33..819b51f9df2 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -35,9 +35,9 @@ public class FlexTripsMapper { for (Trip trip : stopTimesByTrip.keys()) { /* Fetch the stop times for this trip. Copy the list since it's immutable. */ List stopTimes = new ArrayList<>(stopTimesByTrip.get(trip)); - var factors = builder.getFlexDurationFactors().getOrDefault(trip, FlexDurationFactors.ZERO); if (UnscheduledTrip.isUnscheduledTrip(stopTimes)) { + var factors = builder.getFlexDurationFactors().getOrDefault(trip, FlexDurationFactors.ZERO); result.add( UnscheduledTrip .of(trip.getId()) diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java b/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java index 41b610730d4..adcb25dc1e4 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java @@ -1,15 +1,21 @@ package org.opentripplanner.ext.flex.trip; +import java.io.Serializable; import java.time.Duration; +import org.opentripplanner.framework.time.DurationUtils; +import org.opentripplanner.framework.tostring.ToStringBuilder; -public class FlexDurationFactors { +public class FlexDurationFactors implements Serializable { public static FlexDurationFactors ZERO = new FlexDurationFactors(Duration.ZERO, 1); private final int offset; private final float factor; public FlexDurationFactors(Duration offset, float factor) { - this.offset = (int) offset.toSeconds(); + if (factor < 0.1) { + throw new IllegalArgumentException("Flex duration factor must not be less than 0.1"); + } + this.offset = (int) DurationUtils.requireNonNegative(offset).toSeconds(); this.factor = factor; } @@ -24,4 +30,13 @@ public int offsetInSeconds() { boolean nonZero() { return offset != 0 && factor != 1.0; } + + @Override + public String toString() { + return ToStringBuilder + .of(FlexDurationFactors.class) + .addNum("factor", factor) + .addDurationSec("offset", offset) + .toString(); + } } From 721845e1b7287a8186b4abdf517501b51474ded7 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 5 Apr 2024 12:28:37 +0200 Subject: [PATCH 29/85] Use safe instead of mean values --- .../mapping/GTFSToOtpTransitServiceMapper.java | 2 +- .../opentripplanner/gtfs/mapping/TripMapper.java | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java index 15a4c2e28b7..4ee1cdcf1d7 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java @@ -171,7 +171,7 @@ public void mapStopTripAndRouteDataIntoBuilder() { builder.getPathways().addAll(pathwayMapper.map(data.getAllPathways())); builder.getStopTimesSortedByTrip().addAll(stopTimeMapper.map(data.getAllStopTimes())); - builder.getFlexDurationFactors().putAll(tripMapper.flexDurationFactors()); + builder.getFlexDurationFactors().putAll(tripMapper.flexSafeDurationFactors()); builder.getTripsById().addAll(tripMapper.map(data.getAllTrips())); fareRulesBuilder.fareAttributes().addAll(fareAttributeMapper.map(data.getAllFareAttributes())); diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java index 77d6eb879b0..0164e7d77f9 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java @@ -18,7 +18,7 @@ class TripMapper { private final TranslationHelper translationHelper; private final Map mappedTrips = new HashMap<>(); - private final Map flexDurationFactors = new HashMap<>(); + private final Map flexSafeDurationFactors = new HashMap<>(); TripMapper( RouteMapper routeMapper, @@ -45,8 +45,8 @@ Collection getMappedTrips() { /** * The map of flex duration factors per flex trip. */ - Map flexDurationFactors() { - return flexDurationFactors; + Map flexSafeDurationFactors() { + return flexSafeDurationFactors; } private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { @@ -74,16 +74,16 @@ private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { lhs.withGtfsFareId(rhs.getFareId()); var trip = lhs.build(); - mapFlexDurationFactots(rhs).ifPresent(f -> flexDurationFactors.put(trip, f)); + mapSafeDurationFactors(rhs).ifPresent(f -> flexSafeDurationFactors.put(trip, f)); return trip; } - private Optional mapFlexDurationFactots(org.onebusaway.gtfs.model.Trip rhs) { - if (rhs.getMeanDurationFactor() == null && rhs.getMeanDurationOffset() == null) { + private Optional mapSafeDurationFactors(org.onebusaway.gtfs.model.Trip rhs) { + if (rhs.getSafeDurationFactor() == null && rhs.getSafeDurationOffset() == null) { return Optional.empty(); } else { - var offset = Duration.ofSeconds(rhs.getMeanDurationOffset().longValue()); - return Optional.of(new FlexDurationFactors(offset, rhs.getMeanDurationFactor().floatValue())); + var offset = Duration.ofSeconds(rhs.getSafeDurationOffset().longValue()); + return Optional.of(new FlexDurationFactors(offset, rhs.getSafeDurationFactor().floatValue())); } } } From abcfa935489f7dfdc735ec92e7dead8cd7ca4f1c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 5 Apr 2024 12:32:15 +0200 Subject: [PATCH 30/85] Use correct booking info instances --- .../org/opentripplanner/ext/flex/trip/UnscheduledTrip.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index 9c71696c02b..712a446c7ed 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -68,8 +68,8 @@ public UnscheduledTrip(UnscheduledTripBuilder builder) { for (int i = 0; i < size; i++) { this.stopTimes[i] = new StopTimeWindow(stopTimes.get(i)); - this.dropOffBookingInfos[i] = stopTimes.get(0).getDropOffBookingInfo(); - this.pickupBookingInfos[i] = stopTimes.get(0).getPickupBookingInfo(); + this.dropOffBookingInfos[i] = stopTimes.get(i).getDropOffBookingInfo(); + this.pickupBookingInfos[i] = stopTimes.get(i).getPickupBookingInfo(); } this.duractionFactors = Objects.requireNonNull(builder.durationFactors()); } From 5066052c4452afdc15f10896f4c01ee510415dab Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Fri, 5 Apr 2024 16:05:31 +0200 Subject: [PATCH 31/85] Implement duration modifier for ScheduledDeviated trip --- .../ext/flex/FlexTripsMapper.java | 14 ++++--- .../DurationFactorCalculator.java | 6 +-- .../ext/flex/flexpathcalculator/FlexPath.java | 4 +- .../ScheduledFlexPathCalculator.java | 9 +++-- ...Factors.java => FlexDurationModifier.java} | 14 ++++--- .../ext/flex/trip/ScheduledDeviatedTrip.java | 39 +++++++++++++++++-- .../trip/ScheduledDeviatedTripBuilder.java | 10 +++++ .../ext/flex/trip/UnscheduledTrip.java | 8 ++-- .../ext/flex/trip/UnscheduledTripBuilder.java | 10 ++--- .../gtfs/mapping/TripMapper.java | 14 ++++--- .../model/impl/OtpTransitServiceBuilder.java | 7 ++-- 11 files changed, 96 insertions(+), 39 deletions(-) rename src/ext/java/org/opentripplanner/ext/flex/trip/{FlexDurationFactors.java => FlexDurationModifier.java} (68%) diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index 819b51f9df2..5881bc798a8 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -4,7 +4,7 @@ import java.util.ArrayList; import java.util.List; -import org.opentripplanner.ext.flex.trip.FlexDurationFactors; +import org.opentripplanner.ext.flex.trip.FlexDurationModifier; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip; import org.opentripplanner.ext.flex.trip.UnscheduledTrip; @@ -35,20 +35,24 @@ public class FlexTripsMapper { for (Trip trip : stopTimesByTrip.keys()) { /* Fetch the stop times for this trip. Copy the list since it's immutable. */ List stopTimes = new ArrayList<>(stopTimesByTrip.get(trip)); - + var modifier = builder.getFlexDurationFactors().getOrDefault(trip, FlexDurationModifier.NONE); if (UnscheduledTrip.isUnscheduledTrip(stopTimes)) { - var factors = builder.getFlexDurationFactors().getOrDefault(trip, FlexDurationFactors.ZERO); result.add( UnscheduledTrip .of(trip.getId()) .withTrip(trip) .withStopTimes(stopTimes) - .withDurationFactors(factors) + .withDurationModifier(modifier) .build() ); } else if (ScheduledDeviatedTrip.isScheduledFlexTrip(stopTimes)) { result.add( - ScheduledDeviatedTrip.of(trip.getId()).withTrip(trip).withStopTimes(stopTimes).build() + ScheduledDeviatedTrip + .of(trip.getId()) + .withTrip(trip) + .withStopTimes(stopTimes) + .withDurationModifier(modifier) + .build() ); } else if (hasContinuousStops(stopTimes) && FlexTrip.containsFlexStops(stopTimes)) { store.add( diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java index 3e5a6b1db79..e0873fab187 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java @@ -1,15 +1,15 @@ package org.opentripplanner.ext.flex.flexpathcalculator; import javax.annotation.Nullable; -import org.opentripplanner.ext.flex.trip.FlexDurationFactors; +import org.opentripplanner.ext.flex.trip.FlexDurationModifier; import org.opentripplanner.street.model.vertex.Vertex; public class DurationFactorCalculator implements FlexPathCalculator { private final FlexPathCalculator delegate; - private final FlexDurationFactors factors; + private final FlexDurationModifier factors; - public DurationFactorCalculator(FlexPathCalculator delegate, FlexDurationFactors factors) { + public DurationFactorCalculator(FlexPathCalculator delegate, FlexDurationModifier factors) { this.delegate = delegate; this.factors = factors; } diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java index 7ba3aad43d4..22752726ae7 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java @@ -2,7 +2,7 @@ import java.util.function.Supplier; import org.locationtech.jts.geom.LineString; -import org.opentripplanner.ext.flex.trip.FlexDurationFactors; +import org.opentripplanner.ext.flex.trip.FlexDurationModifier; /** * This class contains the results from a FlexPathCalculator. @@ -34,7 +34,7 @@ public LineString getGeometry() { return geometry; } - public FlexPath withDurationFactors(FlexDurationFactors factors) { + public FlexPath withDurationFactors(FlexDurationModifier factors) { int updatedDuration = (int) ((durationSeconds * factors.factor()) + factors.offsetInSeconds()); return new FlexPath(distanceMeters, updatedDuration, geometrySupplier); } diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculator.java index 7d953abc4fd..cd5228dada5 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculator.java @@ -20,7 +20,7 @@ public ScheduledFlexPathCalculator(FlexPathCalculator flexPathCalculator, FlexTr @Override public FlexPath calculateFlexPath(Vertex fromv, Vertex tov, int fromStopIndex, int toStopIndex) { - FlexPath flexPath = flexPathCalculator.calculateFlexPath( + final var flexPath = flexPathCalculator.calculateFlexPath( fromv, tov, fromStopIndex, @@ -29,7 +29,6 @@ public FlexPath calculateFlexPath(Vertex fromv, Vertex tov, int fromStopIndex, i if (flexPath == null) { return null; } - int distance = flexPath.distanceMeters; int departureTime = trip.earliestDepartureTime( Integer.MIN_VALUE, fromStopIndex, @@ -50,6 +49,10 @@ public FlexPath calculateFlexPath(Vertex fromv, Vertex tov, int fromStopIndex, i if (departureTime >= arrivalTime) { return null; } - return new FlexPath(distance, arrivalTime - departureTime, flexPath::getGeometry); + return new FlexPath( + flexPath.distanceMeters, + arrivalTime - departureTime, + flexPath::getGeometry + ); } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java b/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationModifier.java similarity index 68% rename from src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java rename to src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationModifier.java index adcb25dc1e4..60f795d7280 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationFactors.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationModifier.java @@ -5,13 +5,13 @@ import org.opentripplanner.framework.time.DurationUtils; import org.opentripplanner.framework.tostring.ToStringBuilder; -public class FlexDurationFactors implements Serializable { +public class FlexDurationModifier implements Serializable { - public static FlexDurationFactors ZERO = new FlexDurationFactors(Duration.ZERO, 1); + public static FlexDurationModifier NONE = new FlexDurationModifier(Duration.ZERO, 1); private final int offset; private final float factor; - public FlexDurationFactors(Duration offset, float factor) { + public FlexDurationModifier(Duration offset, float factor) { if (factor < 0.1) { throw new IllegalArgumentException("Flex duration factor must not be less than 0.1"); } @@ -27,14 +27,18 @@ public int offsetInSeconds() { return offset; } - boolean nonZero() { + /** + * Check if this instance actually modifies the duration or simply passes it back without + * change. + */ + boolean modifies() { return offset != 0 && factor != 1.0; } @Override public String toString() { return ToStringBuilder - .of(FlexDurationFactors.class) + .of(FlexDurationModifier.class) .addNum("factor", factor) .addDurationSec("offset", offset) .toString(); diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java index e16e1e5e1f7..33a064e610c 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java @@ -1,5 +1,7 @@ package org.opentripplanner.ext.flex.trip; +import static org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip.TimeType.FIXED_TIME; +import static org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip.TimeType.FLEXIBLE_TIME; import static org.opentripplanner.model.PickDrop.NONE; import static org.opentripplanner.model.StopTime.MISSING_VALUE; @@ -15,6 +17,7 @@ import java.util.stream.Stream; import javax.annotation.Nonnull; import org.opentripplanner.ext.flex.FlexServiceDate; +import org.opentripplanner.ext.flex.flexpathcalculator.DurationFactorCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.ScheduledFlexPathCalculator; import org.opentripplanner.ext.flex.template.FlexAccessTemplate; @@ -42,6 +45,8 @@ public class ScheduledDeviatedTrip private final BookingInfo[] dropOffBookingInfos; private final BookingInfo[] pickupBookingInfos; + private final FlexDurationModifier durationModifier; + ScheduledDeviatedTrip(ScheduledDeviatedTripBuilder builder) { super(builder); List stopTimes = builder.stopTimes(); @@ -59,6 +64,7 @@ public class ScheduledDeviatedTrip this.dropOffBookingInfos[i] = stopTimes.get(i).getDropOffBookingInfo(); this.pickupBookingInfos[i] = stopTimes.get(i).getPickupBookingInfo(); } + this.durationModifier = builder.durationModifier(); } public static ScheduledDeviatedTripBuilder of(FeedScopedId id) { @@ -104,7 +110,7 @@ public Stream getFlexAccessTemplates( toIndex, stop, date, - scheduledCalculator, + getCalculator(fromIndex, toIndex, scheduledCalculator), config ) ); @@ -114,6 +120,25 @@ public Stream getFlexAccessTemplates( return res.stream(); } + /** + * If any of the stops involved in the path computation, then apply the duration factors. + * If both from and to have a fixed time, then don't apply the factors. + */ + private FlexPathCalculator getCalculator( + int fromIndex, + int toIndex, + FlexPathCalculator scheduledCalculator + ) { + final boolean usesFlexWindow = + stopTimes[fromIndex].timeType == FLEXIBLE_TIME || + stopTimes[toIndex].timeType == FLEXIBLE_TIME; + if (usesFlexWindow && durationModifier.modifies()) { + return new DurationFactorCalculator(scheduledCalculator, FlexDurationModifier.NONE); + } else { + return scheduledCalculator; + } + } + @Override public Stream getFlexEgressTemplates( NearbyStop egress, @@ -144,7 +169,7 @@ public Stream getFlexEgressTemplates( toIndex, stop, date, - scheduledCalculator, + getCalculator(fromIndex, toIndex, scheduledCalculator), config ) ); @@ -176,7 +201,7 @@ public int earliestDepartureTime(int stopIndex) { @Override public int latestArrivalTime( int arrivalTime, - int fromStopIndex, + int ignored, int toStopIndex, int flexTripDurationSeconds ) { @@ -297,10 +322,13 @@ private static class ScheduledDeviatedStopTime implements Serializable { private final int arrivalTime; private final PickDrop pickupType; private final PickDrop dropOffType; + private final TimeType timeType; private ScheduledDeviatedStopTime(StopTime st) { this.stop = st.getStop(); + this.timeType = st.hasFlexWindow() ? FLEXIBLE_TIME : FIXED_TIME; + // Store the time the user is guaranteed to arrive at latest this.arrivalTime = st.getLatestPossibleArrivalTime(); // Store the time the user needs to be ready for pickup @@ -315,4 +343,9 @@ private ScheduledDeviatedStopTime(StopTime st) { this.dropOffType = arrivalTime == MISSING_VALUE ? PickDrop.NONE : st.getDropOffType(); } } + + enum TimeType { + FIXED_TIME, + FLEXIBLE_TIME, + } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java index 4033bbe7c59..3a33ebd50ec 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java @@ -8,6 +8,7 @@ public class ScheduledDeviatedTripBuilder extends FlexTripBuilder { private List stopTimes; + private FlexDurationModifier durationModifier = FlexDurationModifier.NONE; ScheduledDeviatedTripBuilder(FeedScopedId id) { super(id); @@ -29,6 +30,15 @@ public List stopTimes() { return stopTimes; } + public FlexDurationModifier durationModifier() { + return durationModifier; + } + + public ScheduledDeviatedTripBuilder withDurationModifier(FlexDurationModifier modifier) { + this.durationModifier = modifier; + return this; + } + @Override ScheduledDeviatedTripBuilder instance() { return this; diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index 712a446c7ed..d85c6b72c27 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -52,7 +52,7 @@ public class UnscheduledTrip extends FlexTrip getFlexAccessTemplates( } private FlexPathCalculator flexPathCalculator(FlexPathCalculator calculator) { - if (duractionFactors.nonZero()) { - return new DurationFactorCalculator(calculator, duractionFactors); + if (durationModifier.modifies()) { + return new DurationFactorCalculator(calculator, durationModifier); } else { return calculator; } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java index e2d296d8154..7e2132ed036 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java @@ -8,7 +8,7 @@ public class UnscheduledTripBuilder extends FlexTripBuilder { private List stopTimes; - private FlexDurationFactors durationFactors = FlexDurationFactors.ZERO; + private FlexDurationModifier durationModifier = FlexDurationModifier.NONE; UnscheduledTripBuilder(FeedScopedId id) { super(id); @@ -30,13 +30,13 @@ public List stopTimes() { return stopTimes; } - public UnscheduledTripBuilder withDurationFactors(FlexDurationFactors factors) { - this.durationFactors = factors; + public UnscheduledTripBuilder withDurationModifier(FlexDurationModifier factors) { + this.durationModifier = factors; return this; } - public FlexDurationFactors durationFactors() { - return durationFactors; + public FlexDurationModifier durationModifier() { + return durationModifier; } @Override diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java index 0164e7d77f9..03ef077d319 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java @@ -5,7 +5,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; -import org.opentripplanner.ext.flex.trip.FlexDurationFactors; +import org.opentripplanner.ext.flex.trip.FlexDurationModifier; import org.opentripplanner.framework.collection.MapUtils; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.transit.model.timetable.Trip; @@ -18,7 +18,7 @@ class TripMapper { private final TranslationHelper translationHelper; private final Map mappedTrips = new HashMap<>(); - private final Map flexSafeDurationFactors = new HashMap<>(); + private final Map flexSafeDurationFactors = new HashMap<>(); TripMapper( RouteMapper routeMapper, @@ -45,7 +45,7 @@ Collection getMappedTrips() { /** * The map of flex duration factors per flex trip. */ - Map flexSafeDurationFactors() { + Map flexSafeDurationFactors() { return flexSafeDurationFactors; } @@ -78,12 +78,16 @@ private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { return trip; } - private Optional mapSafeDurationFactors(org.onebusaway.gtfs.model.Trip rhs) { + private Optional mapSafeDurationFactors( + org.onebusaway.gtfs.model.Trip rhs + ) { if (rhs.getSafeDurationFactor() == null && rhs.getSafeDurationOffset() == null) { return Optional.empty(); } else { var offset = Duration.ofSeconds(rhs.getSafeDurationOffset().longValue()); - return Optional.of(new FlexDurationFactors(offset, rhs.getSafeDurationFactor().floatValue())); + return Optional.of( + new FlexDurationModifier(offset, rhs.getSafeDurationFactor().floatValue()) + ); } } } diff --git a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java index e4d0d76cbe3..5811fe042c3 100644 --- a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java +++ b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java @@ -7,9 +7,8 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; -import org.opentripplanner.ext.flex.trip.FlexDurationFactors; +import org.opentripplanner.ext.flex.trip.FlexDurationModifier; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.gtfs.mapping.StaySeatedNotAllowed; @@ -95,7 +94,7 @@ public class OtpTransitServiceBuilder { private final TripStopTimes stopTimesByTrip = new TripStopTimes(); - private final Map flexDurationFactors = new HashMap<>(); + private final Map flexDurationFactors = new HashMap<>(); private final EntityById fareZonesById = new DefaultEntityById<>(); @@ -214,7 +213,7 @@ public TripStopTimes getStopTimesSortedByTrip() { return stopTimesByTrip; } - public Map getFlexDurationFactors() { + public Map getFlexDurationFactors() { return flexDurationFactors; } From 90b15cd8684da65bb5bf2e1d984d00bc53129cdc Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Sat, 6 Apr 2024 22:13:08 +0200 Subject: [PATCH 32/85] Rename and test --- .../DurationModifierCalculatorTest.java | 35 +++++++++++++++++ .../flex/flexpathcalculator/FlexPathTest.java | 39 +++++++++++++++++++ .../ext/flex/FlexTripsMapper.java | 4 +- ...r.java => DurationModifierCalculator.java} | 10 ++--- .../ext/flex/flexpathcalculator/FlexPath.java | 11 ++++-- ...ionModifier.java => DurationModifier.java} | 8 ++-- .../ext/flex/trip/ScheduledDeviatedTrip.java | 6 +-- .../trip/ScheduledDeviatedTripBuilder.java | 6 +-- .../ext/flex/trip/UnscheduledTrip.java | 6 +-- .../ext/flex/trip/UnscheduledTripBuilder.java | 6 +-- .../gtfs/mapping/TripMapper.java | 14 +++---- .../model/impl/OtpTransitServiceBuilder.java | 6 +-- .../_support/geometry/Polygons.java | 4 +- 13 files changed, 115 insertions(+), 40 deletions(-) create mode 100644 src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java create mode 100644 src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java rename src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/{DurationFactorCalculator.java => DurationModifierCalculator.java} (62%) rename src/ext/java/org/opentripplanner/ext/flex/trip/{FlexDurationModifier.java => DurationModifier.java} (79%) diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java new file mode 100644 index 00000000000..fd81344893b --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java @@ -0,0 +1,35 @@ +package org.opentripplanner.ext.flex.flexpathcalculator; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.time.Duration; +import org.junit.jupiter.api.Test; +import org.opentripplanner.ext.flex.trip.DurationModifier; +import org.opentripplanner.framework.geometry.GeometryUtils; +import org.opentripplanner.street.model._data.StreetModelForTest; + +class DurationModifierCalculatorTest { + + private static final int THIRTY_MINS_IN_SECONDS = (int) Duration.ofMinutes(30).toSeconds(); + + @Test + void calculate() { + FlexPathCalculator delegate = (fromv, tov, fromStopIndex, toStopIndex) -> + new FlexPath(10_000, THIRTY_MINS_IN_SECONDS, () -> GeometryUtils.makeLineString(1, 1, 2, 2)); + + var mod = new DurationModifier(Duration.ofMinutes(10), 1.5f); + var calc = new DurationModifierCalculator(delegate, mod); + var path = calc.calculateFlexPath(StreetModelForTest.V1, StreetModelForTest.V2, 0, 5); + assertEquals(3300, path.durationSeconds); + } + + @Test + void nullValue() { + FlexPathCalculator delegate = (fromv, tov, fromStopIndex, toStopIndex) -> null; + var mod = new DurationModifier(Duration.ofMinutes(10), 1.5f); + var calc = new DurationModifierCalculator(delegate, mod); + var path = calc.calculateFlexPath(StreetModelForTest.V1, StreetModelForTest.V2, 0, 5); + assertNull(path); + } +} diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java new file mode 100644 index 00000000000..9bee310573b --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java @@ -0,0 +1,39 @@ +package org.opentripplanner.ext.flex.flexpathcalculator; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.time.Duration; +import java.util.List; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.locationtech.jts.geom.LineString; +import org.opentripplanner.ext.flex.trip.DurationModifier; +import org.opentripplanner.framework.geometry.GeometryUtils; + +class FlexPathTest { + + private static final int THIRTY_MINS_IN_SECONDS = (int) Duration.ofMinutes(30).toSeconds(); + private static final LineString LINE_STRING = GeometryUtils.makeLineString(1, 1, 2, 2); + private static final FlexPath PATH = new FlexPath( + 10_000, + THIRTY_MINS_IN_SECONDS, + () -> LINE_STRING + ); + + static List cases() { + return List.of( + Arguments.of(DurationModifier.NONE, THIRTY_MINS_IN_SECONDS), + Arguments.of(new DurationModifier(Duration.ofMinutes(10), 1), 2400), + Arguments.of(new DurationModifier(Duration.ofMinutes(10), 1.5f), 3300), + Arguments.of(new DurationModifier(Duration.ZERO, 3), 5400) + ); + } + + @ParameterizedTest + @MethodSource("cases") + void calculate(DurationModifier mod, int expectedSeconds) { + var modified = PATH.withDurationModifier(mod); + assertEquals(expectedSeconds, modified.durationSeconds); + } +} diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index 5881bc798a8..e6cad4d3e40 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -4,7 +4,7 @@ import java.util.ArrayList; import java.util.List; -import org.opentripplanner.ext.flex.trip.FlexDurationModifier; +import org.opentripplanner.ext.flex.trip.DurationModifier; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip; import org.opentripplanner.ext.flex.trip.UnscheduledTrip; @@ -35,7 +35,7 @@ public class FlexTripsMapper { for (Trip trip : stopTimesByTrip.keys()) { /* Fetch the stop times for this trip. Copy the list since it's immutable. */ List stopTimes = new ArrayList<>(stopTimesByTrip.get(trip)); - var modifier = builder.getFlexDurationFactors().getOrDefault(trip, FlexDurationModifier.NONE); + var modifier = builder.getFlexDurationFactors().getOrDefault(trip, DurationModifier.NONE); if (UnscheduledTrip.isUnscheduledTrip(stopTimes)) { result.add( UnscheduledTrip diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java similarity index 62% rename from src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java rename to src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java index e0873fab187..643c2bb7b6f 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationFactorCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java @@ -1,15 +1,15 @@ package org.opentripplanner.ext.flex.flexpathcalculator; import javax.annotation.Nullable; -import org.opentripplanner.ext.flex.trip.FlexDurationModifier; +import org.opentripplanner.ext.flex.trip.DurationModifier; import org.opentripplanner.street.model.vertex.Vertex; -public class DurationFactorCalculator implements FlexPathCalculator { +public class DurationModifierCalculator implements FlexPathCalculator { private final FlexPathCalculator delegate; - private final FlexDurationModifier factors; + private final DurationModifier factors; - public DurationFactorCalculator(FlexPathCalculator delegate, FlexDurationModifier factors) { + public DurationModifierCalculator(FlexPathCalculator delegate, DurationModifier factors) { this.delegate = delegate; this.factors = factors; } @@ -22,7 +22,7 @@ public FlexPath calculateFlexPath(Vertex fromv, Vertex tov, int fromStopIndex, i if (path == null) { return null; } else { - return path.withDurationFactors(factors); + return path.withDurationModifier(factors); } } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java index 22752726ae7..54c11198b19 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java @@ -1,12 +1,14 @@ package org.opentripplanner.ext.flex.flexpathcalculator; import java.util.function.Supplier; +import javax.annotation.concurrent.Immutable; import org.locationtech.jts.geom.LineString; -import org.opentripplanner.ext.flex.trip.FlexDurationModifier; +import org.opentripplanner.ext.flex.trip.DurationModifier; /** * This class contains the results from a FlexPathCalculator. */ +@Immutable public class FlexPath { private final Supplier geometrySupplier; @@ -34,8 +36,11 @@ public LineString getGeometry() { return geometry; } - public FlexPath withDurationFactors(FlexDurationModifier factors) { - int updatedDuration = (int) ((durationSeconds * factors.factor()) + factors.offsetInSeconds()); + /** + * Returns an (immutable) copy of this path with the duration modified. + */ + public FlexPath withDurationModifier(DurationModifier mod) { + int updatedDuration = (int) ((durationSeconds * mod.factor()) + mod.offsetInSeconds()); return new FlexPath(distanceMeters, updatedDuration, geometrySupplier); } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationModifier.java b/src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java similarity index 79% rename from src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationModifier.java rename to src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java index 60f795d7280..b9abdd843ff 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/FlexDurationModifier.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java @@ -5,13 +5,13 @@ import org.opentripplanner.framework.time.DurationUtils; import org.opentripplanner.framework.tostring.ToStringBuilder; -public class FlexDurationModifier implements Serializable { +public class DurationModifier implements Serializable { - public static FlexDurationModifier NONE = new FlexDurationModifier(Duration.ZERO, 1); + public static DurationModifier NONE = new DurationModifier(Duration.ZERO, 1); private final int offset; private final float factor; - public FlexDurationModifier(Duration offset, float factor) { + public DurationModifier(Duration offset, float factor) { if (factor < 0.1) { throw new IllegalArgumentException("Flex duration factor must not be less than 0.1"); } @@ -38,7 +38,7 @@ boolean modifies() { @Override public String toString() { return ToStringBuilder - .of(FlexDurationModifier.class) + .of(DurationModifier.class) .addNum("factor", factor) .addDurationSec("offset", offset) .toString(); diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java index 33a064e610c..553fd2959a5 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java @@ -17,7 +17,7 @@ import java.util.stream.Stream; import javax.annotation.Nonnull; import org.opentripplanner.ext.flex.FlexServiceDate; -import org.opentripplanner.ext.flex.flexpathcalculator.DurationFactorCalculator; +import org.opentripplanner.ext.flex.flexpathcalculator.DurationModifierCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.ScheduledFlexPathCalculator; import org.opentripplanner.ext.flex.template.FlexAccessTemplate; @@ -45,7 +45,7 @@ public class ScheduledDeviatedTrip private final BookingInfo[] dropOffBookingInfos; private final BookingInfo[] pickupBookingInfos; - private final FlexDurationModifier durationModifier; + private final DurationModifier durationModifier; ScheduledDeviatedTrip(ScheduledDeviatedTripBuilder builder) { super(builder); @@ -133,7 +133,7 @@ private FlexPathCalculator getCalculator( stopTimes[fromIndex].timeType == FLEXIBLE_TIME || stopTimes[toIndex].timeType == FLEXIBLE_TIME; if (usesFlexWindow && durationModifier.modifies()) { - return new DurationFactorCalculator(scheduledCalculator, FlexDurationModifier.NONE); + return new DurationModifierCalculator(scheduledCalculator, durationModifier); } else { return scheduledCalculator; } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java index 3a33ebd50ec..3bd89ef2e02 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java @@ -8,7 +8,7 @@ public class ScheduledDeviatedTripBuilder extends FlexTripBuilder { private List stopTimes; - private FlexDurationModifier durationModifier = FlexDurationModifier.NONE; + private DurationModifier durationModifier = DurationModifier.NONE; ScheduledDeviatedTripBuilder(FeedScopedId id) { super(id); @@ -30,11 +30,11 @@ public List stopTimes() { return stopTimes; } - public FlexDurationModifier durationModifier() { + public DurationModifier durationModifier() { return durationModifier; } - public ScheduledDeviatedTripBuilder withDurationModifier(FlexDurationModifier modifier) { + public ScheduledDeviatedTripBuilder withDurationModifier(DurationModifier modifier) { this.durationModifier = modifier; return this; } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index d85c6b72c27..d8e942d0c98 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -15,7 +15,7 @@ import java.util.stream.Stream; import javax.annotation.Nonnull; import org.opentripplanner.ext.flex.FlexServiceDate; -import org.opentripplanner.ext.flex.flexpathcalculator.DurationFactorCalculator; +import org.opentripplanner.ext.flex.flexpathcalculator.DurationModifierCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; import org.opentripplanner.ext.flex.template.FlexAccessTemplate; import org.opentripplanner.ext.flex.template.FlexEgressTemplate; @@ -52,7 +52,7 @@ public class UnscheduledTrip extends FlexTrip getFlexAccessTemplates( private FlexPathCalculator flexPathCalculator(FlexPathCalculator calculator) { if (durationModifier.modifies()) { - return new DurationFactorCalculator(calculator, durationModifier); + return new DurationModifierCalculator(calculator, durationModifier); } else { return calculator; } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java index 7e2132ed036..7b4617048a6 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java @@ -8,7 +8,7 @@ public class UnscheduledTripBuilder extends FlexTripBuilder { private List stopTimes; - private FlexDurationModifier durationModifier = FlexDurationModifier.NONE; + private DurationModifier durationModifier = DurationModifier.NONE; UnscheduledTripBuilder(FeedScopedId id) { super(id); @@ -30,12 +30,12 @@ public List stopTimes() { return stopTimes; } - public UnscheduledTripBuilder withDurationModifier(FlexDurationModifier factors) { + public UnscheduledTripBuilder withDurationModifier(DurationModifier factors) { this.durationModifier = factors; return this; } - public FlexDurationModifier durationModifier() { + public DurationModifier durationModifier() { return durationModifier; } diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java index 03ef077d319..cc6e44343e6 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java @@ -5,7 +5,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; -import org.opentripplanner.ext.flex.trip.FlexDurationModifier; +import org.opentripplanner.ext.flex.trip.DurationModifier; import org.opentripplanner.framework.collection.MapUtils; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.transit.model.timetable.Trip; @@ -18,7 +18,7 @@ class TripMapper { private final TranslationHelper translationHelper; private final Map mappedTrips = new HashMap<>(); - private final Map flexSafeDurationFactors = new HashMap<>(); + private final Map flexSafeDurationFactors = new HashMap<>(); TripMapper( RouteMapper routeMapper, @@ -45,7 +45,7 @@ Collection getMappedTrips() { /** * The map of flex duration factors per flex trip. */ - Map flexSafeDurationFactors() { + Map flexSafeDurationFactors() { return flexSafeDurationFactors; } @@ -78,16 +78,12 @@ private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { return trip; } - private Optional mapSafeDurationFactors( - org.onebusaway.gtfs.model.Trip rhs - ) { + private Optional mapSafeDurationFactors(org.onebusaway.gtfs.model.Trip rhs) { if (rhs.getSafeDurationFactor() == null && rhs.getSafeDurationOffset() == null) { return Optional.empty(); } else { var offset = Duration.ofSeconds(rhs.getSafeDurationOffset().longValue()); - return Optional.of( - new FlexDurationModifier(offset, rhs.getSafeDurationFactor().floatValue()) - ); + return Optional.of(new DurationModifier(offset, rhs.getSafeDurationFactor().floatValue())); } } } diff --git a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java index 5811fe042c3..16b7bf22388 100644 --- a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java +++ b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.opentripplanner.ext.flex.trip.FlexDurationModifier; +import org.opentripplanner.ext.flex.trip.DurationModifier; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.gtfs.mapping.StaySeatedNotAllowed; @@ -94,7 +94,7 @@ public class OtpTransitServiceBuilder { private final TripStopTimes stopTimesByTrip = new TripStopTimes(); - private final Map flexDurationFactors = new HashMap<>(); + private final Map flexDurationFactors = new HashMap<>(); private final EntityById fareZonesById = new DefaultEntityById<>(); @@ -213,7 +213,7 @@ public TripStopTimes getStopTimesSortedByTrip() { return stopTimesByTrip; } - public Map getFlexDurationFactors() { + public Map getFlexDurationFactors() { return flexDurationFactors; } diff --git a/src/test/java/org/opentripplanner/_support/geometry/Polygons.java b/src/test/java/org/opentripplanner/_support/geometry/Polygons.java index a386d8a27e1..ee110ab4f4f 100644 --- a/src/test/java/org/opentripplanner/_support/geometry/Polygons.java +++ b/src/test/java/org/opentripplanner/_support/geometry/Polygons.java @@ -20,7 +20,7 @@ public class Polygons { } ); - public static Polygon OSLO = FAC.createPolygon( + public static final Polygon OSLO = FAC.createPolygon( new Coordinate[] { Coordinates.of(59.961055202323195, 10.62535658370308), Coordinates.of(59.889009435700416, 10.62535658370308), @@ -29,7 +29,7 @@ public class Polygons { Coordinates.of(59.961055202323195, 10.62535658370308), } ); - public static Polygon OSLO_FROGNER_PARK = FAC.createPolygon( + public static final Polygon OSLO_FROGNER_PARK = FAC.createPolygon( new Coordinate[] { Coordinates.of(59.92939032560119, 10.69770054003061), Coordinates.of(59.929138466684975, 10.695210909925208), From 41b2e505dc14d981fa1c15f4ae203494a7c0a301 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 8 Apr 2024 00:03:09 +0200 Subject: [PATCH 33/85] Extract class for building flex stop times --- .../ext/flex/FlexStopTimesForTest.java | 49 +++++++++++++++++++ .../DurationModifierCalculatorTest.java | 4 +- .../flex/flexpathcalculator/FlexPathTest.java | 6 +-- .../ScheduledFlexPathCalculatorTest.java | 38 ++++++++++++++ .../ext/flex/trip/UnscheduledTripTest.java | 42 +++------------- .../ext/flex/FlexibleTransitLeg.java | 1 + .../_support/geometry/LineStrings.java | 9 ++++ 7 files changed, 108 insertions(+), 41 deletions(-) create mode 100644 src/ext-test/java/org/opentripplanner/ext/flex/FlexStopTimesForTest.java create mode 100644 src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculatorTest.java create mode 100644 src/test/java/org/opentripplanner/_support/geometry/LineStrings.java diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/FlexStopTimesForTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/FlexStopTimesForTest.java new file mode 100644 index 00000000000..d76382999a2 --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/flex/FlexStopTimesForTest.java @@ -0,0 +1,49 @@ +package org.opentripplanner.ext.flex; + +import static org.opentripplanner.model.StopTime.MISSING_VALUE; + +import org.opentripplanner._support.geometry.Polygons; +import org.opentripplanner.framework.time.TimeUtils; +import org.opentripplanner.model.StopTime; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.site.RegularStop; +import org.opentripplanner.transit.model.site.StopLocation; + +public class FlexStopTimesForTest { + + private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of(); + private static final StopLocation AREA_STOP = TEST_MODEL.areaStopForTest("area", Polygons.BERLIN); + private static final RegularStop REGULAR_STOP = TEST_MODEL.stop("stop").build(); + + public static StopTime area(String startTime, String endTime) { + return area(AREA_STOP, endTime, startTime); + } + + public static StopTime area(StopLocation areaStop, String endTime, String startTime) { + var stopTime = new StopTime(); + stopTime.setStop(areaStop); + stopTime.setFlexWindowStart(TimeUtils.time(startTime)); + stopTime.setFlexWindowEnd(TimeUtils.time(endTime)); + return stopTime; + } + + public static StopTime regularArrival(String arrivalTime) { + return regularStopTime(TimeUtils.time(arrivalTime), MISSING_VALUE); + } + + public static StopTime regularStopTime(String arrivalTime, String departureTime) { + return regularStopTime(TimeUtils.time(arrivalTime), TimeUtils.time(departureTime)); + } + + public static StopTime regularStopTime(int arrivalTime, int departureTime) { + var stopTime = new StopTime(); + stopTime.setStop(REGULAR_STOP); + stopTime.setArrivalTime(arrivalTime); + stopTime.setDepartureTime(departureTime); + return stopTime; + } + + public static StopTime regularDeparture(String departureTime) { + return regularStopTime(MISSING_VALUE, TimeUtils.time(departureTime)); + } +} diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java index fd81344893b..f610cbe09c4 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java @@ -5,8 +5,8 @@ import java.time.Duration; import org.junit.jupiter.api.Test; +import org.opentripplanner._support.geometry.LineStrings; import org.opentripplanner.ext.flex.trip.DurationModifier; -import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.street.model._data.StreetModelForTest; class DurationModifierCalculatorTest { @@ -16,7 +16,7 @@ class DurationModifierCalculatorTest { @Test void calculate() { FlexPathCalculator delegate = (fromv, tov, fromStopIndex, toStopIndex) -> - new FlexPath(10_000, THIRTY_MINS_IN_SECONDS, () -> GeometryUtils.makeLineString(1, 1, 2, 2)); + new FlexPath(10_000, THIRTY_MINS_IN_SECONDS, () -> LineStrings.SIMPLE); var mod = new DurationModifier(Duration.ofMinutes(10), 1.5f); var calc = new DurationModifierCalculator(delegate, mod); diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java index 9bee310573b..6a4c79ca2cc 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java @@ -7,18 +7,16 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.locationtech.jts.geom.LineString; +import org.opentripplanner._support.geometry.LineStrings; import org.opentripplanner.ext.flex.trip.DurationModifier; -import org.opentripplanner.framework.geometry.GeometryUtils; class FlexPathTest { private static final int THIRTY_MINS_IN_SECONDS = (int) Duration.ofMinutes(30).toSeconds(); - private static final LineString LINE_STRING = GeometryUtils.makeLineString(1, 1, 2, 2); private static final FlexPath PATH = new FlexPath( 10_000, THIRTY_MINS_IN_SECONDS, - () -> LINE_STRING + () -> LineStrings.SIMPLE ); static List cases() { diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculatorTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculatorTest.java new file mode 100644 index 00000000000..70f39af2420 --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/ScheduledFlexPathCalculatorTest.java @@ -0,0 +1,38 @@ +package org.opentripplanner.ext.flex.flexpathcalculator; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.ext.flex.FlexStopTimesForTest.area; +import static org.opentripplanner.ext.flex.FlexStopTimesForTest.regularStopTime; +import static org.opentripplanner.street.model._data.StreetModelForTest.V1; +import static org.opentripplanner.street.model._data.StreetModelForTest.V2; +import static org.opentripplanner.transit.model._data.TransitModelForTest.id; + +import java.time.Duration; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.opentripplanner._support.geometry.LineStrings; +import org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip; + +class ScheduledFlexPathCalculatorTest { + + private static final ScheduledDeviatedTrip TRIP = ScheduledDeviatedTrip + .of(id("123")) + .withStopTimes( + List.of( + regularStopTime("10:00", "10:01"), + area("10:10", "10:20"), + regularStopTime("10:25", "10:26"), + area("10:40", "10:50") + ) + ) + .build(); + + @Test + void calculateTime() { + var c = (FlexPathCalculator) (fromv, tov, fromStopIndex, toStopIndex) -> + new FlexPath(10_000, (int) Duration.ofMinutes(10).toSeconds(), () -> LineStrings.SIMPLE); + var calc = new ScheduledFlexPathCalculator(c, TRIP); + var path = calc.calculateFlexPath(V1, V2, 0, 1); + assertEquals(Duration.ofMinutes(19), Duration.ofSeconds(path.durationSeconds)); + } +} diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java index fabe534ff23..bfcd9bad565 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.ext.flex.FlexStopTimesForTest.area; import static org.opentripplanner.ext.flex.trip.UnscheduledTrip.isUnscheduledTrip; import static org.opentripplanner.ext.flex.trip.UnscheduledTripTest.TestCase.tc; import static org.opentripplanner.model.PickDrop.NONE; @@ -22,6 +23,7 @@ import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner._support.geometry.Polygons; import org.opentripplanner.ext.flex.FlexServiceDate; +import org.opentripplanner.ext.flex.FlexStopTimesForTest; import org.opentripplanner.ext.flex.flexpathcalculator.DirectFlexPathCalculator; import org.opentripplanner.ext.flex.template.FlexAccessTemplate; import org.opentripplanner.ext.flex.template.FlexEgressTemplate; @@ -48,11 +50,10 @@ class UnscheduledTripTest { private static final int T15_00 = TimeUtils.hm2time(15, 0); private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of(); + private static final StopLocation AREA_STOP = TEST_MODEL.areaStopForTest("area", Polygons.BERLIN); private static final RegularStop REGULAR_STOP = TEST_MODEL.stop("stop").build(); - private static final StopLocation AREA_STOP = TEST_MODEL.areaStopForTest("area", Polygons.BERLIN); - @Nested class IsUnscheduledTrip { @@ -196,7 +197,7 @@ void testUnscheduledFeederTripToScheduledStop() { static Stream testRegularStopToAreaEarliestDepartureTimeTestCases() { // REGULAR-STOP to AREA - (10:00-14:00) => (14:00) - var tc = tc(regularDeparture("10:00"), area("10:00", "14:00")); + var tc = tc(FlexStopTimesForTest.regularDeparture("10:00"), area("10:00", "14:00")); return Stream.of( tc .expected("Requested departure time is before flex service departure time", "10:00") @@ -245,7 +246,7 @@ void testRegularStopToAreaEarliestDepartureTime(TestCase tc) { static Stream testAreaToRegularStopEarliestDepartureTestCases() { // AREA TO REGULAR-STOP - (10:00-14:00) => (14:00) - var tc = tc(area("10:00", "14:00"), regularArrival("14:00")); + var tc = tc(area("10:00", "14:00"), FlexStopTimesForTest.regularArrival("14:00")); return Stream.of( tc .expected( @@ -366,7 +367,7 @@ void testAreaToAreaEarliestDepartureTime(TestCase tc) { static Stream testRegularStopToAreaLatestArrivalTimeTestCases() { // REGULAR-STOP to AREA - (10:00-14:00) => (14:00) - var tc = tc(regularDeparture("10:00"), area("10:00", "14:00")); + var tc = tc(FlexStopTimesForTest.regularDeparture("10:00"), area("10:00", "14:00")); return Stream.of( tc .expectedNotFound("Requested arrival time is before flex service arrival window start") @@ -421,7 +422,7 @@ void testRegularStopToAreaLatestArrivalTime(TestCase tc) { static Stream testAreaToRegularStopLatestArrivalTimeTestCases() { // AREA TO REGULAR-STOP - (10:00-14:00) => (14:00) - var tc = tc(area("10:00", "14:00"), regularArrival("14:00")); + var tc = tc(area("10:00", "14:00"), FlexStopTimesForTest.regularArrival("14:00")); return Stream.of( tc .expectedNotFound("Requested arrival time is before flex service arrival window start") @@ -661,35 +662,6 @@ private static String timeToString(int time) { return TimeUtils.timeToStrCompact(time, MISSING_VALUE, "MISSING_VALUE"); } - private static StopTime area(String startTime, String endTime) { - return area(AREA_STOP, endTime, startTime); - } - - @Nonnull - private static StopTime area(StopLocation areaStop, String endTime, String startTime) { - var stopTime = new StopTime(); - stopTime.setStop(areaStop); - stopTime.setFlexWindowStart(TimeUtils.time(startTime)); - stopTime.setFlexWindowEnd(TimeUtils.time(endTime)); - return stopTime; - } - - private static StopTime regularDeparture(String departureTime) { - return regularStopTime(MISSING_VALUE, TimeUtils.time(departureTime)); - } - - private static StopTime regularArrival(String arrivalTime) { - return regularStopTime(TimeUtils.time(arrivalTime), MISSING_VALUE); - } - - private static StopTime regularStopTime(int arrivalTime, int departureTime) { - var stopTime = new StopTime(); - stopTime.setStop(REGULAR_STOP); - stopTime.setArrivalTime(arrivalTime); - stopTime.setDepartureTime(departureTime); - return stopTime; - } - @Nonnull private static NearbyStop nearbyStop(AreaStop stop) { return new NearbyStop(stop, 1000, List.of(), null); diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java b/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java index fac1118556f..b9e10e29214 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java @@ -196,6 +196,7 @@ public int getGeneralizedCost() { return generalizedCost; } + @Override public void addAlert(TransitAlert alert) { transitAlerts.add(alert); } diff --git a/src/test/java/org/opentripplanner/_support/geometry/LineStrings.java b/src/test/java/org/opentripplanner/_support/geometry/LineStrings.java new file mode 100644 index 00000000000..515f161be92 --- /dev/null +++ b/src/test/java/org/opentripplanner/_support/geometry/LineStrings.java @@ -0,0 +1,9 @@ +package org.opentripplanner._support.geometry; + +import org.locationtech.jts.geom.LineString; +import org.opentripplanner.framework.geometry.GeometryUtils; + +public class LineStrings { + + public static final LineString SIMPLE = GeometryUtils.makeLineString(0, 0, 1, 1); +} From dec30cd3c95d788e2bb7691ec2ae73c611adcc00 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 9 Apr 2024 09:57:06 +0200 Subject: [PATCH 34/85] Remove durationModifier to ScheduledDeviatedTrip --- .../ext/flex/FlexTripsMapper.java | 3 +-- .../ext/flex/trip/ScheduledDeviatedTrip.java | 27 ++----------------- .../trip/ScheduledDeviatedTripBuilder.java | 10 ------- 3 files changed, 3 insertions(+), 37 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index e6cad4d3e40..b4123a3b556 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -35,8 +35,8 @@ public class FlexTripsMapper { for (Trip trip : stopTimesByTrip.keys()) { /* Fetch the stop times for this trip. Copy the list since it's immutable. */ List stopTimes = new ArrayList<>(stopTimesByTrip.get(trip)); - var modifier = builder.getFlexDurationFactors().getOrDefault(trip, DurationModifier.NONE); if (UnscheduledTrip.isUnscheduledTrip(stopTimes)) { + var modifier = builder.getFlexDurationFactors().getOrDefault(trip, DurationModifier.NONE); result.add( UnscheduledTrip .of(trip.getId()) @@ -51,7 +51,6 @@ public class FlexTripsMapper { .of(trip.getId()) .withTrip(trip) .withStopTimes(stopTimes) - .withDurationModifier(modifier) .build() ); } else if (hasContinuousStops(stopTimes) && FlexTrip.containsFlexStops(stopTimes)) { diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java index 553fd2959a5..84982c837dc 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java @@ -17,7 +17,6 @@ import java.util.stream.Stream; import javax.annotation.Nonnull; import org.opentripplanner.ext.flex.FlexServiceDate; -import org.opentripplanner.ext.flex.flexpathcalculator.DurationModifierCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.ScheduledFlexPathCalculator; import org.opentripplanner.ext.flex.template.FlexAccessTemplate; @@ -45,8 +44,6 @@ public class ScheduledDeviatedTrip private final BookingInfo[] dropOffBookingInfos; private final BookingInfo[] pickupBookingInfos; - private final DurationModifier durationModifier; - ScheduledDeviatedTrip(ScheduledDeviatedTripBuilder builder) { super(builder); List stopTimes = builder.stopTimes(); @@ -64,7 +61,6 @@ public class ScheduledDeviatedTrip this.dropOffBookingInfos[i] = stopTimes.get(i).getDropOffBookingInfo(); this.pickupBookingInfos[i] = stopTimes.get(i).getPickupBookingInfo(); } - this.durationModifier = builder.durationModifier(); } public static ScheduledDeviatedTripBuilder of(FeedScopedId id) { @@ -110,7 +106,7 @@ public Stream getFlexAccessTemplates( toIndex, stop, date, - getCalculator(fromIndex, toIndex, scheduledCalculator), + scheduledCalculator, config ) ); @@ -120,25 +116,6 @@ public Stream getFlexAccessTemplates( return res.stream(); } - /** - * If any of the stops involved in the path computation, then apply the duration factors. - * If both from and to have a fixed time, then don't apply the factors. - */ - private FlexPathCalculator getCalculator( - int fromIndex, - int toIndex, - FlexPathCalculator scheduledCalculator - ) { - final boolean usesFlexWindow = - stopTimes[fromIndex].timeType == FLEXIBLE_TIME || - stopTimes[toIndex].timeType == FLEXIBLE_TIME; - if (usesFlexWindow && durationModifier.modifies()) { - return new DurationModifierCalculator(scheduledCalculator, durationModifier); - } else { - return scheduledCalculator; - } - } - @Override public Stream getFlexEgressTemplates( NearbyStop egress, @@ -169,7 +146,7 @@ public Stream getFlexEgressTemplates( toIndex, stop, date, - getCalculator(fromIndex, toIndex, scheduledCalculator), + scheduledCalculator, config ) ); diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java index 3bd89ef2e02..4033bbe7c59 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTripBuilder.java @@ -8,7 +8,6 @@ public class ScheduledDeviatedTripBuilder extends FlexTripBuilder { private List stopTimes; - private DurationModifier durationModifier = DurationModifier.NONE; ScheduledDeviatedTripBuilder(FeedScopedId id) { super(id); @@ -30,15 +29,6 @@ public List stopTimes() { return stopTimes; } - public DurationModifier durationModifier() { - return durationModifier; - } - - public ScheduledDeviatedTripBuilder withDurationModifier(DurationModifier modifier) { - this.durationModifier = modifier; - return this; - } - @Override ScheduledDeviatedTripBuilder instance() { return this; From 354c96839897497b91615971e40775b5d3e3fafa Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 9 Apr 2024 12:37:01 +0200 Subject: [PATCH 35/85] Add test and docs for DurationModifier --- .../ext/flex/trip/DurationModifierTest.java | 20 +++++++++++++++++++ .../ext/flex/trip/DurationModifier.java | 7 +++++++ 2 files changed, 27 insertions(+) create mode 100644 src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java new file mode 100644 index 00000000000..c6f4e75f7c5 --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java @@ -0,0 +1,20 @@ +package org.opentripplanner.ext.flex.trip; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.Duration; +import org.junit.jupiter.api.Test; + +class DurationModifierTest { + + @Test + void doesNotModify() { + assertFalse(DurationModifier.NONE.modifies()); + } + + @Test + void modifies() { + assertTrue(new DurationModifier(Duration.ofMinutes(1), 1.5f).modifies()); + } +} \ No newline at end of file diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java b/src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java index b9abdd843ff..4832a7cc9bc 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java @@ -5,12 +5,19 @@ import org.opentripplanner.framework.time.DurationUtils; import org.opentripplanner.framework.tostring.ToStringBuilder; +/** + * A modifier to influence the A*-calculated driving time of flex trips. + */ public class DurationModifier implements Serializable { public static DurationModifier NONE = new DurationModifier(Duration.ZERO, 1); private final int offset; private final float factor; + /** + * @param offset A fixed offset to add to the driving time. + * @param factor A factor to multiply the driving time with. + */ public DurationModifier(Duration offset, float factor) { if (factor < 0.1) { throw new IllegalArgumentException("Flex duration factor must not be less than 0.1"); From 991406ed25845660bd0a25e972e86c66227b7409 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 9 Apr 2024 13:42:44 +0200 Subject: [PATCH 36/85] Cleanup, tests and documentation --- doc-templates/Flex.md | 4 +- docs/sandbox/Flex.md | 4 +- .../flex/flexpathcalculator/FlexPathTest.java | 1 + .../ext/flex/trip/DurationModifierTest.java | 2 +- .../trip/UnscheduledDrivingDurationTest.java | 44 ++++++++++++++++++ .../ext/flex/trip/UnscheduledTripTest.java | 11 ++--- .../ext/flex/FlexTripsMapper.java | 6 +-- .../DurationModifierCalculator.java | 4 ++ .../ext/flex/trip/ScheduledDeviatedTrip.java | 10 ----- .../ext/flex/trip/UnscheduledTrip.java | 6 ++- .../GTFSToOtpTransitServiceMapper.java | 2 +- .../gtfs/mapping/TripMapper.java | 10 ++--- .../gtfs/mapping/TripMapperTest.java | 45 ++++++++++++++----- 13 files changed, 107 insertions(+), 42 deletions(-) create mode 100644 src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java diff --git a/doc-templates/Flex.md b/doc-templates/Flex.md index 2015e898cae..50512f66133 100644 --- a/doc-templates/Flex.md +++ b/doc-templates/Flex.md @@ -10,8 +10,8 @@ To enable this turn on `FlexRouting` as a feature in `otp-config.json`. -The GTFS feeds should conform to the -[GTFS-Flex v2 draft PR](https://github.com/google/transit/pull/388) +The GTFS feeds must conform to the final, approved version of the draft which has been +merged into the [mainline specification](https://gtfs.org/schedule/reference/) in March 2024. ## Configuration diff --git a/docs/sandbox/Flex.md b/docs/sandbox/Flex.md index 61d15851a56..277e4e617f2 100644 --- a/docs/sandbox/Flex.md +++ b/docs/sandbox/Flex.md @@ -10,8 +10,8 @@ To enable this turn on `FlexRouting` as a feature in `otp-config.json`. -The GTFS feeds should conform to the -[GTFS-Flex v2 draft PR](https://github.com/google/transit/pull/388) +The GTFS feeds must conform to the final, approved version of the draft which has been +merged into the [mainline specification](https://gtfs.org/schedule/reference/) in March 2024. ## Configuration diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java index 6a4c79ca2cc..b79093e26c5 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java @@ -33,5 +33,6 @@ static List cases() { void calculate(DurationModifier mod, int expectedSeconds) { var modified = PATH.withDurationModifier(mod); assertEquals(expectedSeconds, modified.durationSeconds); + assertEquals(LineStrings.SIMPLE, modified.getGeometry()); } } diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java index c6f4e75f7c5..05fb9965935 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java @@ -17,4 +17,4 @@ void doesNotModify() { void modifies() { assertTrue(new DurationModifier(Duration.ofMinutes(1), 1.5f).modifies()); } -} \ No newline at end of file +} diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java new file mode 100644 index 00000000000..45d8c5be624 --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java @@ -0,0 +1,44 @@ +package org.opentripplanner.ext.flex.trip; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.street.model._data.StreetModelForTest.V1; +import static org.opentripplanner.street.model._data.StreetModelForTest.V2; +import static org.opentripplanner.transit.model._data.TransitModelForTest.id; + +import java.time.Duration; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.opentripplanner._support.geometry.LineStrings; +import org.opentripplanner.ext.flex.FlexStopTimesForTest; +import org.opentripplanner.ext.flex.flexpathcalculator.FlexPath; +import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; +import org.opentripplanner.model.StopTime; + +class UnscheduledDrivingDurationTest { + + static final FlexPathCalculator STATIC_CALCULATOR = (fromv, tov, fromStopIndex, toStopIndex) -> + new FlexPath(10_000, (int) Duration.ofMinutes(10).toSeconds(), () -> LineStrings.SIMPLE); + private static final StopTime STOP_TIME = FlexStopTimesForTest.area("10:00", "18:00"); + + @Test + void noModifier() { + var trip = UnscheduledTrip.of(id("1")).withStopTimes(List.of(STOP_TIME)).build(); + + var calculator = trip.flexPathCalculator(STATIC_CALCULATOR); + var path = calculator.calculateFlexPath(V1, V2, 0, 0); + assertEquals(600, path.durationSeconds); + } + + @Test + void withModifier() { + var trip = UnscheduledTrip + .of(id("1")) + .withStopTimes(List.of(STOP_TIME)) + .withDurationModifier(new DurationModifier(Duration.ofMinutes(2), 1.5f)) + .build(); + + var calculator = trip.flexPathCalculator(STATIC_CALCULATOR); + var path = calculator.calculateFlexPath(V1, V2, 0, 0); + assertEquals(1020, path.durationSeconds); + } +} diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java index bfcd9bad565..80bda31fabf 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java @@ -4,6 +4,8 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.ext.flex.FlexStopTimesForTest.area; +import static org.opentripplanner.ext.flex.FlexStopTimesForTest.regularArrival; +import static org.opentripplanner.ext.flex.FlexStopTimesForTest.regularDeparture; import static org.opentripplanner.ext.flex.trip.UnscheduledTrip.isUnscheduledTrip; import static org.opentripplanner.ext.flex.trip.UnscheduledTripTest.TestCase.tc; import static org.opentripplanner.model.PickDrop.NONE; @@ -23,7 +25,6 @@ import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner._support.geometry.Polygons; import org.opentripplanner.ext.flex.FlexServiceDate; -import org.opentripplanner.ext.flex.FlexStopTimesForTest; import org.opentripplanner.ext.flex.flexpathcalculator.DirectFlexPathCalculator; import org.opentripplanner.ext.flex.template.FlexAccessTemplate; import org.opentripplanner.ext.flex.template.FlexEgressTemplate; @@ -197,7 +198,7 @@ void testUnscheduledFeederTripToScheduledStop() { static Stream testRegularStopToAreaEarliestDepartureTimeTestCases() { // REGULAR-STOP to AREA - (10:00-14:00) => (14:00) - var tc = tc(FlexStopTimesForTest.regularDeparture("10:00"), area("10:00", "14:00")); + var tc = tc(regularDeparture("10:00"), area("10:00", "14:00")); return Stream.of( tc .expected("Requested departure time is before flex service departure time", "10:00") @@ -246,7 +247,7 @@ void testRegularStopToAreaEarliestDepartureTime(TestCase tc) { static Stream testAreaToRegularStopEarliestDepartureTestCases() { // AREA TO REGULAR-STOP - (10:00-14:00) => (14:00) - var tc = tc(area("10:00", "14:00"), FlexStopTimesForTest.regularArrival("14:00")); + var tc = tc(area("10:00", "14:00"), regularArrival("14:00")); return Stream.of( tc .expected( @@ -367,7 +368,7 @@ void testAreaToAreaEarliestDepartureTime(TestCase tc) { static Stream testRegularStopToAreaLatestArrivalTimeTestCases() { // REGULAR-STOP to AREA - (10:00-14:00) => (14:00) - var tc = tc(FlexStopTimesForTest.regularDeparture("10:00"), area("10:00", "14:00")); + var tc = tc(regularDeparture("10:00"), area("10:00", "14:00")); return Stream.of( tc .expectedNotFound("Requested arrival time is before flex service arrival window start") @@ -422,7 +423,7 @@ void testRegularStopToAreaLatestArrivalTime(TestCase tc) { static Stream testAreaToRegularStopLatestArrivalTimeTestCases() { // AREA TO REGULAR-STOP - (10:00-14:00) => (14:00) - var tc = tc(area("10:00", "14:00"), FlexStopTimesForTest.regularArrival("14:00")); + var tc = tc(area("10:00", "14:00"), regularArrival("14:00")); return Stream.of( tc .expectedNotFound("Requested arrival time is before flex service arrival window start") diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index b4123a3b556..bb2ed2764f6 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -47,11 +47,7 @@ public class FlexTripsMapper { ); } else if (ScheduledDeviatedTrip.isScheduledFlexTrip(stopTimes)) { result.add( - ScheduledDeviatedTrip - .of(trip.getId()) - .withTrip(trip) - .withStopTimes(stopTimes) - .build() + ScheduledDeviatedTrip.of(trip.getId()).withTrip(trip).withStopTimes(stopTimes).build() ); } else if (hasContinuousStops(stopTimes) && FlexTrip.containsFlexStops(stopTimes)) { store.add( diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java index 643c2bb7b6f..158e6a933c7 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java @@ -4,6 +4,10 @@ import org.opentripplanner.ext.flex.trip.DurationModifier; import org.opentripplanner.street.model.vertex.Vertex; +/** + * A calculator to delegates the main computation to another instance and applies a duration + * modifier afterward. + */ public class DurationModifierCalculator implements FlexPathCalculator { private final FlexPathCalculator delegate; diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java index 84982c837dc..0026d98c964 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java @@ -1,7 +1,5 @@ package org.opentripplanner.ext.flex.trip; -import static org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip.TimeType.FIXED_TIME; -import static org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip.TimeType.FLEXIBLE_TIME; import static org.opentripplanner.model.PickDrop.NONE; import static org.opentripplanner.model.StopTime.MISSING_VALUE; @@ -299,13 +297,10 @@ private static class ScheduledDeviatedStopTime implements Serializable { private final int arrivalTime; private final PickDrop pickupType; private final PickDrop dropOffType; - private final TimeType timeType; private ScheduledDeviatedStopTime(StopTime st) { this.stop = st.getStop(); - this.timeType = st.hasFlexWindow() ? FLEXIBLE_TIME : FIXED_TIME; - // Store the time the user is guaranteed to arrive at latest this.arrivalTime = st.getLatestPossibleArrivalTime(); // Store the time the user needs to be ready for pickup @@ -320,9 +315,4 @@ private ScheduledDeviatedStopTime(StopTime st) { this.dropOffType = arrivalTime == MISSING_VALUE ? PickDrop.NONE : st.getDropOffType(); } } - - enum TimeType { - FIXED_TIME, - FLEXIBLE_TIME, - } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index d8e942d0c98..bc3bd52cc4c 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -149,7 +149,11 @@ public Stream getFlexAccessTemplates( ); } - private FlexPathCalculator flexPathCalculator(FlexPathCalculator calculator) { + /** + * Get the correct {@link FlexPathCalculator} depending on the {@code durationModified}. + * If the modifier doesn't actually modify, we don't + */ + protected FlexPathCalculator flexPathCalculator(FlexPathCalculator calculator) { if (durationModifier.modifies()) { return new DurationModifierCalculator(calculator, durationModifier); } else { diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java index 4ee1cdcf1d7..24db367f829 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java @@ -171,7 +171,7 @@ public void mapStopTripAndRouteDataIntoBuilder() { builder.getPathways().addAll(pathwayMapper.map(data.getAllPathways())); builder.getStopTimesSortedByTrip().addAll(stopTimeMapper.map(data.getAllStopTimes())); - builder.getFlexDurationFactors().putAll(tripMapper.flexSafeDurationFactors()); + builder.getFlexDurationFactors().putAll(tripMapper.flexSafeDurationModifiers()); builder.getTripsById().addAll(tripMapper.map(data.getAllTrips())); fareRulesBuilder.fareAttributes().addAll(fareAttributeMapper.map(data.getAllFareAttributes())); diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java index cc6e44343e6..ec7d1379ca0 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java @@ -18,7 +18,7 @@ class TripMapper { private final TranslationHelper translationHelper; private final Map mappedTrips = new HashMap<>(); - private final Map flexSafeDurationFactors = new HashMap<>(); + private final Map flexSafeDurationModifiers = new HashMap<>(); TripMapper( RouteMapper routeMapper, @@ -45,8 +45,8 @@ Collection getMappedTrips() { /** * The map of flex duration factors per flex trip. */ - Map flexSafeDurationFactors() { - return flexSafeDurationFactors; + Map flexSafeDurationModifiers() { + return flexSafeDurationModifiers; } private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { @@ -74,11 +74,11 @@ private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { lhs.withGtfsFareId(rhs.getFareId()); var trip = lhs.build(); - mapSafeDurationFactors(rhs).ifPresent(f -> flexSafeDurationFactors.put(trip, f)); + mapSafeDurationModifier(rhs).ifPresent(f -> flexSafeDurationModifiers.put(trip, f)); return trip; } - private Optional mapSafeDurationFactors(org.onebusaway.gtfs.model.Trip rhs) { + private Optional mapSafeDurationModifier(org.onebusaway.gtfs.model.Trip rhs) { if (rhs.getSafeDurationFactor() == null && rhs.getSafeDurationOffset() == null) { return Optional.empty(); } else { diff --git a/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java b/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java index 4f0c70f22d2..141c1d5bf6b 100644 --- a/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java +++ b/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java @@ -33,11 +33,15 @@ public class TripMapperTest { public static final DataImportIssueStore ISSUE_STORE = DataImportIssueStore.NOOP; - private final TripMapper subject = new TripMapper( - new RouteMapper(new AgencyMapper(FEED_ID), ISSUE_STORE, new TranslationHelper()), - new DirectionMapper(ISSUE_STORE), - new TranslationHelper() - ); + private final TripMapper subject = defaultTripMapper(); + + private static TripMapper defaultTripMapper() { + return new TripMapper( + new RouteMapper(new AgencyMapper(FEED_ID), ISSUE_STORE, new TranslationHelper()), + new DirectionMapper(ISSUE_STORE), + new TranslationHelper() + ); + } static { GtfsTestData data = new GtfsTestData(); @@ -56,14 +60,14 @@ public class TripMapperTest { } @Test - public void testMapCollection() throws Exception { + void testMapCollection() throws Exception { assertNull(subject.map((Collection) null)); assertTrue(subject.map(Collections.emptyList()).isEmpty()); assertEquals(1, subject.map(Collections.singleton(TRIP)).size()); } @Test - public void testMap() throws Exception { + void testMap() throws Exception { org.opentripplanner.transit.model.timetable.Trip result = subject.map(TRIP); assertEquals("A:1", result.getId().toString()); @@ -80,7 +84,7 @@ public void testMap() throws Exception { } @Test - public void testMapWithNulls() throws Exception { + void testMapWithNulls() throws Exception { Trip input = new Trip(); input.setId(AGENCY_AND_ID); input.setRoute(new GtfsTestData().route); @@ -101,12 +105,33 @@ public void testMapWithNulls() throws Exception { assertEquals(BikeAccess.UNKNOWN, result.getBikesAllowed()); } - /** Mapping the same object twice, should return the the same instance. */ + /** Mapping the same object twice, should return the same instance. */ @Test - public void testMapCache() throws Exception { + void testMapCache() throws Exception { org.opentripplanner.transit.model.timetable.Trip result1 = subject.map(TRIP); org.opentripplanner.transit.model.timetable.Trip result2 = subject.map(TRIP); assertSame(result1, result2); } + + @Test + void noFlexDurationModifier() { + var mapper = defaultTripMapper(); + mapper.map(TRIP); + assertTrue(mapper.flexSafeDurationModifiers().isEmpty()); + } + + @Test + void flexDurationModifier() { + var flexTrip = new Trip(); + flexTrip.setId(new AgencyAndId("1", "1")); + flexTrip.setSafeDurationFactor(1.5); + flexTrip.setSafeDurationOffset(600d); + flexTrip.setRoute(new GtfsTestData().route); + var mapper = defaultTripMapper(); + var mapped = mapper.map(flexTrip); + var mod = mapper.flexSafeDurationModifiers().get(mapped); + assertEquals(1.5f, mod.factor()); + assertEquals(600, mod.offsetInSeconds()); + } } From 70585decabdfe488947bb1ddf4aa7ef207b155ef Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 16 Apr 2024 09:40:41 +0200 Subject: [PATCH 37/85] Remove extra expandBy --- .../graph_builder/module/osm/naming/SidewalkNamer.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index e6eab4d3d0e..763a531250f 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -129,9 +129,7 @@ private void assignNameToSidewalk(EdgeOnLevel sidewalkOnLevel, AtomicInteger nam var buffer = preciseBuffer(sidewalk.getGeometry(), BUFFER_METERS); var sidewalkLength = SphericalDistanceLibrary.length(sidewalk.getGeometry()); - var envelope = sidewalk.getGeometry().getEnvelopeInternal(); - envelope.expandBy(0.000002); - var candidates = streetEdges.query(envelope); + var candidates = streetEdges.query(buffer.getEnvelopeInternal()); groupEdgesByName(candidates) // remove edges that are far away From 3056805c63b67a93e16e4b206a0aef785cb275d8 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 16 Apr 2024 09:43:54 +0200 Subject: [PATCH 38/85] Use better name for variable --- .../opentripplanner/apis/vectortiles/DebugStyleSpec.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 16e3b247bc7..92d577480e2 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -50,7 +50,7 @@ public class DebugStyleSpec { 1, List.of(new ZoomStop(15, 0.2f), new ZoomStop(MAX_ZOOM, 3)) ); - private static final Class[] ALL_EDGES = new Class[] { + private static final Class[] EDGES_TO_DISPLAY = new Class[] { StreetEdge.class, AreaEdge.class, EscalatorEdge.class, @@ -83,7 +83,7 @@ static StyleSpec build( .typeLine() .vectorSourceLayer(edges) .lineColor(MAGENTA) - .edgeFilter(ALL_EDGES) + .edgeFilter(EDGES_TO_DISPLAY) .lineWidth(LINE_WIDTH) .minZoom(13) .maxZoom(MAX_ZOOM) @@ -93,7 +93,7 @@ static StyleSpec build( .typeSymbol() .lineText("name") .vectorSourceLayer(edges) - .edgeFilter(ALL_EDGES) + .edgeFilter(EDGES_TO_DISPLAY) .minZoom(17) .maxZoom(MAX_ZOOM) .intiallyHidden(), From de0e38292a806fd0534009965abcf91ed5366d5b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 16 Apr 2024 17:24:15 +0200 Subject: [PATCH 39/85] Compute UTM CRS only once --- .../module/osm/naming/SidewalkNamer.java | 91 +++++++++++++------ 1 file changed, 62 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index 763a531250f..d17de3f96c1 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -16,6 +16,8 @@ import org.geotools.geometry.jts.JTS; import org.geotools.referencing.CRS; import org.geotools.referencing.crs.DefaultGeographicCRS; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.MultiLineString; @@ -61,6 +63,7 @@ public class SidewalkNamer implements EdgeNamer { private HashGridSpatialIndex streetEdges = new HashGridSpatialIndex<>(); private Collection unnamedSidewalks = new ArrayList<>(); + private PreciseBuffer preciseBuffer; @Override public I18NString name(OSMWithTags way) { @@ -96,6 +99,8 @@ public void postprocess() { unnamedSidewalks.size() ); + this.preciseBuffer = new PreciseBuffer(computeCentroid(), BUFFER_METERS); + final AtomicInteger namesApplied = new AtomicInteger(0); unnamedSidewalks .parallelStream() @@ -121,12 +126,23 @@ public void postprocess() { unnamedSidewalks = null; } + /** + * Compute the centroid of all sidewalk edges. + */ + private Coordinate computeCentroid() { + var envelope = new Envelope(); + unnamedSidewalks.forEach(e -> + envelope.expandToInclude(e.edge.getGeometry().getEnvelopeInternal()) + ); + return envelope.centre(); + } + /** * The actual worker method that runs the business logic on an individual sidewalk edge. */ private void assignNameToSidewalk(EdgeOnLevel sidewalkOnLevel, AtomicInteger namesApplied) { var sidewalk = sidewalkOnLevel.edge; - var buffer = preciseBuffer(sidewalk.getGeometry(), BUFFER_METERS); + var buffer = preciseBuffer.preciseBuffer(sidewalk.getGeometry()); var sidewalkLength = SphericalDistanceLibrary.length(sidewalk.getGeometry()); var candidates = streetEdges.query(buffer.getEnvelopeInternal()); @@ -187,34 +203,6 @@ private static Stream groupEdgesByName(List candida }); } - /** - * Add a buffer around a geometry that makes sure that the buffer is the same distance (in meters) - * anywhere on earth. - *

- * Background: If you call the regular buffer() method on a JTS geometry that uses WGS84 as the - * coordinate reference system, the buffer will be accurate at the equator but will become more - * and more elongated the farther north/south you go. - *

- * Taken from https://stackoverflow.com/questions/36455020 - */ - private Geometry preciseBuffer(Geometry geometry, double distanceInMeters) { - try { - var coordinate = geometry.getCentroid().getCoordinate(); - String code = "AUTO:42001,%s,%s".formatted(coordinate.x, coordinate.y); - CoordinateReferenceSystem auto = CRS.decode(code); - - MathTransform toTransform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, auto); - MathTransform fromTransform = CRS.findMathTransform(auto, DefaultGeographicCRS.WGS84); - - Geometry pGeom = JTS.transform(geometry, toTransform); - - Geometry pBufferedGeom = pGeom.buffer(distanceInMeters, 4, BufferParameters.CAP_FLAT); - return JTS.transform(pBufferedGeom, fromTransform); - } catch (TransformException | FactoryException e) { - throw new RuntimeException(e); - } - } - private record NamedEdgeGroup(double percentInBuffer, I18NString name) { NamedEdgeGroup { Objects.requireNonNull(name); @@ -270,4 +258,49 @@ private double length(Geometry intersection) { } private record EdgeOnLevel(StreetEdge edge, Set levels) {} + + /** + * A class to cache the expensive construction of a Universal Traverse Mercator coordinate + * reference system. + * Re-using the same CRS for all edges might introduce tiny imprecisions for OTPs use cases + * but speeds up the processing enormously and is a price well worth paying. + */ + private static final class PreciseBuffer { + + private final double distanceInMeters; + private final MathTransform toTransform; + private final MathTransform fromTransform; + + private PreciseBuffer(Coordinate coordinate, double distanceInMeters) { + this.distanceInMeters = distanceInMeters; + String code = "AUTO:42001,%s,%s".formatted(coordinate.x, coordinate.y); + try { + CoordinateReferenceSystem auto = CRS.decode(code); + this.toTransform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, auto); + this.fromTransform = CRS.findMathTransform(auto, DefaultGeographicCRS.WGS84); + } catch (FactoryException e) { + throw new RuntimeException(e); + } + } + + /** + * Add a buffer around a geometry that makes sure that the buffer is the same distance (in + * meters) anywhere on earth. + *

+ * Background: If you call the regular buffer() method on a JTS geometry that uses WGS84 as the + * coordinate reference system, the buffer will be accurate at the equator but will become more + * and more elongated the farther north/south you go. + *

+ * Taken from https://stackoverflow.com/questions/36455020 + */ + private Geometry preciseBuffer(Geometry geometry) { + try { + Geometry pGeom = JTS.transform(geometry, toTransform); + Geometry pBufferedGeom = pGeom.buffer(distanceInMeters, 4, BufferParameters.CAP_FLAT); + return JTS.transform(pBufferedGeom, fromTransform); + } catch (TransformException e) { + throw new RuntimeException(e); + } + } + } } From 1d749c3d3642f4e7e4223db8af136a13bd0fbc54 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 17 Apr 2024 14:25:14 +0200 Subject: [PATCH 40/85] Always record edges in pairs in the custom namer --- .../graph_builder/module/osm/OsmModule.java | 9 ++-- .../module/osm/StreetEdgePair.java | 35 +++++++++++++ .../module/osm/naming/DefaultNamer.java | 4 +- .../osm/naming/PortlandCustomNamer.java | 49 ++++++++++--------- .../module/osm/naming/SidewalkNamer.java | 22 ++++----- .../graph_builder/services/osm/EdgeNamer.java | 6 +-- .../module/islandpruning/TestNamer.java | 4 +- .../module/osm/naming/SidewalkNamerTest.java | 3 +- 8 files changed, 85 insertions(+), 47 deletions(-) create mode 100644 src/main/java/org/opentripplanner/graph_builder/module/osm/StreetEdgePair.java diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 0cd53b70fc6..79bcacda660 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -113,8 +113,6 @@ public Map elevationDataOutput() { return elevationData; } - private record StreetEdgePair(StreetEdge main, StreetEdge back) {} - private void build() { var parkingProcessor = new ParkingProcessor( graph, @@ -390,8 +388,10 @@ private void buildBasicGraph() { geometry ); - StreetEdge street = streets.main; - StreetEdge backStreet = streets.back; + params.edgeNamer().recordEdges(way, streets); + + StreetEdge street = streets.main(); + StreetEdge backStreet = streets.back(); normalizer.applyWayProperties(street, backStreet, wayData, way); applyEdgesToTurnRestrictions(way, startNode, endNode, street, backStreet); @@ -545,7 +545,6 @@ private StreetEdge getEdgeForStreet( .withBogusName(way.needsFallbackName()); StreetEdge street = seb.buildAndConnect(); - params.edgeNamer().recordEdge(way, street); return street; } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/StreetEdgePair.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/StreetEdgePair.java new file mode 100644 index 00000000000..7e87838c4d5 --- /dev/null +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/StreetEdgePair.java @@ -0,0 +1,35 @@ +package org.opentripplanner.graph_builder.module.osm; + +import java.util.ArrayList; +import org.opentripplanner.street.model.edge.StreetEdge; + +public record StreetEdgePair(StreetEdge main, StreetEdge back) { + /** + * Return the non-null elements of this pair as an Iterable. + */ + public Iterable asIterable() { + var ret = new ArrayList(2); + if (main != null) { + ret.add(main); + } + if (back != null) { + ret.add(back); + } + return ret; + } + + /** + * Select one of the edges contained in this pair that is not null. No particular algorithm is + * guaranteed, and it may change in the future. + */ + public StreetEdge pickAny() { + if (main != null) { + return main; + } else if (back != null) { + return back; + } + throw new IllegalStateException( + "%s must not contain two null elements".formatted(getClass().getSimpleName()) + ); + } +} diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/DefaultNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/DefaultNamer.java index b1639608d4c..ff7b7d17666 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/DefaultNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/DefaultNamer.java @@ -1,9 +1,9 @@ package org.opentripplanner.graph_builder.module.osm.naming; import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.graph_builder.module.osm.StreetEdgePair; import org.opentripplanner.graph_builder.services.osm.EdgeNamer; import org.opentripplanner.openstreetmap.model.OSMWithTags; -import org.opentripplanner.street.model.edge.StreetEdge; public class DefaultNamer implements EdgeNamer { @@ -13,7 +13,7 @@ public I18NString name(OSMWithTags way) { } @Override - public void recordEdge(OSMWithTags way, StreetEdge edge) {} + public void recordEdges(OSMWithTags way, StreetEdgePair edge) {} @Override public void postprocess() {} diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/PortlandCustomNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/PortlandCustomNamer.java index 9d550d51ae6..fb0d3f583e0 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/PortlandCustomNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/PortlandCustomNamer.java @@ -3,6 +3,7 @@ import java.util.HashSet; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.NonLocalizedString; +import org.opentripplanner.graph_builder.module.osm.StreetEdgePair; import org.opentripplanner.graph_builder.services.osm.EdgeNamer; import org.opentripplanner.openstreetmap.model.OSMWithTags; import org.opentripplanner.street.model.edge.StreetEdge; @@ -69,28 +70,32 @@ public I18NString name(OSMWithTags way) { } @Override - public void recordEdge(OSMWithTags way, StreetEdge edge) { - if (!edge.hasBogusName()) { - return; // this edge already has a real name so there is nothing to do - } - String highway = way.getTag("highway"); - if ("motorway_link".equals(highway) || "trunk_link".equals(highway)) { - if (edge.isBack()) { - nameByDestination.add(edge); - } else { - nameByOrigin.add(edge); - } - } else if ( - "secondary_link".equals(highway) || - "primary_link".equals(highway) || - "tertiary_link".equals(highway) - ) { - if (edge.isBack()) { - nameByOrigin.add(edge); - } else { - nameByDestination.add(edge); - } - } + public void recordEdges(OSMWithTags way, StreetEdgePair edgePair) { + edgePair + .asIterable() + .forEach(edge -> { + if (!edge.hasBogusName()) { + return; // this edge already has a real name so there is nothing to do + } + String highway = way.getTag("highway"); + if ("motorway_link".equals(highway) || "trunk_link".equals(highway)) { + if (edge.isBack()) { + nameByDestination.add(edge); + } else { + nameByOrigin.add(edge); + } + } else if ( + "secondary_link".equals(highway) || + "primary_link".equals(highway) || + "tertiary_link".equals(highway) + ) { + if (edge.isBack()) { + nameByOrigin.add(edge); + } else { + nameByDestination.add(edge); + } + } + }); } @Override diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index d17de3f96c1..990d1d73442 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -30,6 +30,7 @@ import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.lang.DoubleUtils; import org.opentripplanner.framework.logging.ProgressTracker; +import org.opentripplanner.graph_builder.module.osm.StreetEdgePair; import org.opentripplanner.graph_builder.services.osm.EdgeNamer; import org.opentripplanner.openstreetmap.model.OSMWithTags; import org.opentripplanner.street.model.edge.StreetEdge; @@ -71,23 +72,20 @@ public I18NString name(OSMWithTags way) { } @Override - public void recordEdge(OSMWithTags way, StreetEdge edge) { + public void recordEdges(OSMWithTags way, StreetEdgePair pair) { if (way.isSidewalk() && way.needsFallbackName() && !way.isExplicitlyUnnamed()) { - unnamedSidewalks.add(new EdgeOnLevel(edge, way.getLevels())); + pair + .asIterable() + .forEach(edge -> unnamedSidewalks.add(new EdgeOnLevel(edge, way.getLevels()))); } if (way.isNamed() && !way.isLink()) { // we generate two edges for each osm way: one there and one back. since we don't do any routing // in this class we don't need the two directions and keep only one of them. - var containsReverse = streetEdges - .query(edge.getGeometry().getEnvelopeInternal()) - .stream() - .anyMatch(candidate -> candidate.edge.isReverseOf(edge)); - if (!containsReverse) { - streetEdges.insert( - edge.getGeometry().getEnvelopeInternal(), - new EdgeOnLevel(edge, way.getLevels()) - ); - } + var edge = pair.pickAny(); + streetEdges.insert( + edge.getGeometry().getEnvelopeInternal(), + new EdgeOnLevel(edge, way.getLevels()) + ); } } diff --git a/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java b/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java index 010b21ec2d1..60cf780f714 100644 --- a/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java @@ -3,13 +3,13 @@ import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.NonLocalizedString; +import org.opentripplanner.graph_builder.module.osm.StreetEdgePair; import org.opentripplanner.graph_builder.module.osm.naming.DefaultNamer; import org.opentripplanner.graph_builder.module.osm.naming.PortlandCustomNamer; import org.opentripplanner.graph_builder.module.osm.naming.SidewalkNamer; import org.opentripplanner.openstreetmap.model.OSMWithTags; import org.opentripplanner.standalone.config.framework.json.NodeAdapter; import org.opentripplanner.standalone.config.framework.json.OtpVersion; -import org.opentripplanner.street.model.edge.StreetEdge; /** * Interface responsible for naming edges of the street graph. It allows you to write your own @@ -25,11 +25,11 @@ public interface EdgeNamer { * Callback function for each way/edge combination so that more complicated names can be built * in the post-processing step. */ - void recordEdge(OSMWithTags way, StreetEdge edge); + void recordEdges(OSMWithTags way, StreetEdgePair edge); /** * Called after each edge has been named to build a more complex name out of the relationships - * tracked in {@link EdgeNamer#recordEdge(OSMWithTags, StreetEdge)}. + * tracked in {@link EdgeNamer#recordEdges(OSMWithTags, StreetEdgePair)}. */ void postprocess(); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/TestNamer.java b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/TestNamer.java index 0d2ad370541..4002da919d8 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/TestNamer.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/TestNamer.java @@ -2,9 +2,9 @@ import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.NonLocalizedString; +import org.opentripplanner.graph_builder.module.osm.StreetEdgePair; import org.opentripplanner.graph_builder.services.osm.EdgeNamer; import org.opentripplanner.openstreetmap.model.OSMWithTags; -import org.opentripplanner.street.model.edge.StreetEdge; class TestNamer implements EdgeNamer { @@ -14,7 +14,7 @@ public I18NString name(OSMWithTags way) { } @Override - public void recordEdge(OSMWithTags way, StreetEdge edge) {} + public void recordEdges(OSMWithTags way, StreetEdgePair edge) {} @Override public void postprocess() {} diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamerTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamerTest.java index 3faa77ee531..c1c3d7e5e73 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamerTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamerTest.java @@ -12,6 +12,7 @@ import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.graph_builder.module.osm.StreetEdgePair; import org.opentripplanner.graph_builder.services.osm.EdgeNamer; import org.opentripplanner.openstreetmap.model.OSMWay; import org.opentripplanner.openstreetmap.wayproperty.specifier.WayTestData; @@ -92,7 +93,7 @@ EdgePair addStreetEdge(String name, WgsCoordinate... coordinates) { } void postProcess(EdgeNamer namer) { - pairs.forEach(p -> namer.recordEdge(p.way, p.edge)); + pairs.forEach(p -> namer.recordEdges(p.way, new StreetEdgePair(p.edge, null))); namer.postprocess(); } From 6c7d95383a2fe297c4a2ca690dcc65b971b68a37 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 18 Apr 2024 16:32:04 +0200 Subject: [PATCH 41/85] Replace DurationModifier with TimePenalty --- .../DurationModifierCalculatorTest.java | 6 +-- .../flex/flexpathcalculator/FlexPathTest.java | 12 ++--- .../ext/flex/trip/DurationModifierTest.java | 20 ------- .../trip/UnscheduledDrivingDurationTest.java | 3 +- .../ext/flex/FlexTripsMapper.java | 4 +- .../DurationModifierCalculator.java | 8 +-- .../ext/flex/flexpathcalculator/FlexPath.java | 16 ++++-- .../ext/flex/trip/DurationModifier.java | 53 ------------------- .../ext/flex/trip/UnscheduledTrip.java | 5 +- .../ext/flex/trip/UnscheduledTripBuilder.java | 7 +-- .../gtfs/mapping/TripMapper.java | 10 ++-- .../model/impl/OtpTransitServiceBuilder.java | 6 +-- .../gtfs/mapping/TripMapperTest.java | 4 +- 13 files changed, 45 insertions(+), 109 deletions(-) delete mode 100644 src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java delete mode 100644 src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java index f610cbe09c4..a97460dda75 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java @@ -6,7 +6,7 @@ import java.time.Duration; import org.junit.jupiter.api.Test; import org.opentripplanner._support.geometry.LineStrings; -import org.opentripplanner.ext.flex.trip.DurationModifier; +import org.opentripplanner.routing.api.request.framework.TimePenalty; import org.opentripplanner.street.model._data.StreetModelForTest; class DurationModifierCalculatorTest { @@ -18,7 +18,7 @@ void calculate() { FlexPathCalculator delegate = (fromv, tov, fromStopIndex, toStopIndex) -> new FlexPath(10_000, THIRTY_MINS_IN_SECONDS, () -> LineStrings.SIMPLE); - var mod = new DurationModifier(Duration.ofMinutes(10), 1.5f); + var mod = TimePenalty.of(Duration.ofMinutes(10), 1.5f); var calc = new DurationModifierCalculator(delegate, mod); var path = calc.calculateFlexPath(StreetModelForTest.V1, StreetModelForTest.V2, 0, 5); assertEquals(3300, path.durationSeconds); @@ -27,7 +27,7 @@ void calculate() { @Test void nullValue() { FlexPathCalculator delegate = (fromv, tov, fromStopIndex, toStopIndex) -> null; - var mod = new DurationModifier(Duration.ofMinutes(10), 1.5f); + var mod = TimePenalty.of(Duration.ofMinutes(10), 1.5f); var calc = new DurationModifierCalculator(delegate, mod); var path = calc.calculateFlexPath(StreetModelForTest.V1, StreetModelForTest.V2, 0, 5); assertNull(path); diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java index b79093e26c5..8bd3abee785 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPathTest.java @@ -8,7 +8,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner._support.geometry.LineStrings; -import org.opentripplanner.ext.flex.trip.DurationModifier; +import org.opentripplanner.routing.api.request.framework.TimePenalty; class FlexPathTest { @@ -21,16 +21,16 @@ class FlexPathTest { static List cases() { return List.of( - Arguments.of(DurationModifier.NONE, THIRTY_MINS_IN_SECONDS), - Arguments.of(new DurationModifier(Duration.ofMinutes(10), 1), 2400), - Arguments.of(new DurationModifier(Duration.ofMinutes(10), 1.5f), 3300), - Arguments.of(new DurationModifier(Duration.ZERO, 3), 5400) + Arguments.of(TimePenalty.ZERO, THIRTY_MINS_IN_SECONDS), + Arguments.of(TimePenalty.of(Duration.ofMinutes(10), 1), 2400), + Arguments.of(TimePenalty.of(Duration.ofMinutes(10), 1.5f), 3300), + Arguments.of(TimePenalty.of(Duration.ZERO, 3), 5400) ); } @ParameterizedTest @MethodSource("cases") - void calculate(DurationModifier mod, int expectedSeconds) { + void calculate(TimePenalty mod, int expectedSeconds) { var modified = PATH.withDurationModifier(mod); assertEquals(expectedSeconds, modified.durationSeconds); assertEquals(LineStrings.SIMPLE, modified.getGeometry()); diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java deleted file mode 100644 index 05fb9965935..00000000000 --- a/src/ext-test/java/org/opentripplanner/ext/flex/trip/DurationModifierTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.opentripplanner.ext.flex.trip; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.time.Duration; -import org.junit.jupiter.api.Test; - -class DurationModifierTest { - - @Test - void doesNotModify() { - assertFalse(DurationModifier.NONE.modifies()); - } - - @Test - void modifies() { - assertTrue(new DurationModifier(Duration.ofMinutes(1), 1.5f).modifies()); - } -} diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java index 45d8c5be624..bd06a51960b 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java @@ -13,6 +13,7 @@ import org.opentripplanner.ext.flex.flexpathcalculator.FlexPath; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; import org.opentripplanner.model.StopTime; +import org.opentripplanner.routing.api.request.framework.TimePenalty; class UnscheduledDrivingDurationTest { @@ -34,7 +35,7 @@ void withModifier() { var trip = UnscheduledTrip .of(id("1")) .withStopTimes(List.of(STOP_TIME)) - .withDurationModifier(new DurationModifier(Duration.ofMinutes(2), 1.5f)) + .withDurationModifier(TimePenalty.of(Duration.ofMinutes(2), 1.5f)) .build(); var calculator = trip.flexPathCalculator(STATIC_CALCULATOR); diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index bb2ed2764f6..76a13255b67 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.List; -import org.opentripplanner.ext.flex.trip.DurationModifier; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.ext.flex.trip.ScheduledDeviatedTrip; import org.opentripplanner.ext.flex.trip.UnscheduledTrip; @@ -13,6 +12,7 @@ import org.opentripplanner.model.StopTime; import org.opentripplanner.model.TripStopTimes; import org.opentripplanner.model.impl.OtpTransitServiceBuilder; +import org.opentripplanner.routing.api.request.framework.TimePenalty; import org.opentripplanner.transit.model.timetable.Trip; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +36,7 @@ public class FlexTripsMapper { /* Fetch the stop times for this trip. Copy the list since it's immutable. */ List stopTimes = new ArrayList<>(stopTimesByTrip.get(trip)); if (UnscheduledTrip.isUnscheduledTrip(stopTimes)) { - var modifier = builder.getFlexDurationFactors().getOrDefault(trip, DurationModifier.NONE); + var modifier = builder.getFlexDurationFactors().getOrDefault(trip, TimePenalty.ZERO); result.add( UnscheduledTrip .of(trip.getId()) diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java index 158e6a933c7..0f521b55b1f 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java @@ -1,7 +1,7 @@ package org.opentripplanner.ext.flex.flexpathcalculator; import javax.annotation.Nullable; -import org.opentripplanner.ext.flex.trip.DurationModifier; +import org.opentripplanner.routing.api.request.framework.TimePenalty; import org.opentripplanner.street.model.vertex.Vertex; /** @@ -11,11 +11,11 @@ public class DurationModifierCalculator implements FlexPathCalculator { private final FlexPathCalculator delegate; - private final DurationModifier factors; + private final TimePenalty factors; - public DurationModifierCalculator(FlexPathCalculator delegate, DurationModifier factors) { + public DurationModifierCalculator(FlexPathCalculator delegate, TimePenalty penalty) { this.delegate = delegate; - this.factors = factors; + this.factors = penalty; } @Nullable diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java index 54c11198b19..3a692dff40b 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/FlexPath.java @@ -1,9 +1,11 @@ package org.opentripplanner.ext.flex.flexpathcalculator; +import java.time.Duration; import java.util.function.Supplier; import javax.annotation.concurrent.Immutable; import org.locationtech.jts.geom.LineString; -import org.opentripplanner.ext.flex.trip.DurationModifier; +import org.opentripplanner.framework.lang.IntUtils; +import org.opentripplanner.routing.api.request.framework.TimePenalty; /** * This class contains the results from a FlexPathCalculator. @@ -25,7 +27,7 @@ public class FlexPath { */ public FlexPath(int distanceMeters, int durationSeconds, Supplier geometrySupplier) { this.distanceMeters = distanceMeters; - this.durationSeconds = durationSeconds; + this.durationSeconds = IntUtils.requireNotNegative(durationSeconds); this.geometrySupplier = geometrySupplier; } @@ -39,8 +41,12 @@ public LineString getGeometry() { /** * Returns an (immutable) copy of this path with the duration modified. */ - public FlexPath withDurationModifier(DurationModifier mod) { - int updatedDuration = (int) ((durationSeconds * mod.factor()) + mod.offsetInSeconds()); - return new FlexPath(distanceMeters, updatedDuration, geometrySupplier); + public FlexPath withDurationModifier(TimePenalty mod) { + if (mod.isZero()) { + return this; + } else { + int updatedDuration = (int) mod.calculate(Duration.ofSeconds(durationSeconds)).toSeconds(); + return new FlexPath(distanceMeters, updatedDuration, geometrySupplier); + } } } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java b/src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java deleted file mode 100644 index 4832a7cc9bc..00000000000 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/DurationModifier.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.opentripplanner.ext.flex.trip; - -import java.io.Serializable; -import java.time.Duration; -import org.opentripplanner.framework.time.DurationUtils; -import org.opentripplanner.framework.tostring.ToStringBuilder; - -/** - * A modifier to influence the A*-calculated driving time of flex trips. - */ -public class DurationModifier implements Serializable { - - public static DurationModifier NONE = new DurationModifier(Duration.ZERO, 1); - private final int offset; - private final float factor; - - /** - * @param offset A fixed offset to add to the driving time. - * @param factor A factor to multiply the driving time with. - */ - public DurationModifier(Duration offset, float factor) { - if (factor < 0.1) { - throw new IllegalArgumentException("Flex duration factor must not be less than 0.1"); - } - this.offset = (int) DurationUtils.requireNonNegative(offset).toSeconds(); - this.factor = factor; - } - - public float factor() { - return factor; - } - - public int offsetInSeconds() { - return offset; - } - - /** - * Check if this instance actually modifies the duration or simply passes it back without - * change. - */ - boolean modifies() { - return offset != 0 && factor != 1.0; - } - - @Override - public String toString() { - return ToStringBuilder - .of(DurationModifier.class) - .addNum("factor", factor) - .addDurationSec("offset", offset) - .toString(); - } -} diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index bc3bd52cc4c..db662a5fd45 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -23,6 +23,7 @@ import org.opentripplanner.model.BookingInfo; import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.StopTime; +import org.opentripplanner.routing.api.request.framework.TimePenalty; import org.opentripplanner.routing.graphfinder.NearbyStop; import org.opentripplanner.standalone.config.sandbox.FlexConfig; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -52,7 +53,7 @@ public class UnscheduledTrip extends FlexTrip getFlexAccessTemplates( * If the modifier doesn't actually modify, we don't */ protected FlexPathCalculator flexPathCalculator(FlexPathCalculator calculator) { - if (durationModifier.modifies()) { + if (!durationModifier.isZero()) { return new DurationModifierCalculator(calculator, durationModifier); } else { return calculator; diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java index 7b4617048a6..cfffebe4fa7 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java @@ -2,13 +2,14 @@ import java.util.List; import org.opentripplanner.model.StopTime; +import org.opentripplanner.routing.api.request.framework.TimePenalty; import org.opentripplanner.transit.model.framework.FeedScopedId; public class UnscheduledTripBuilder extends FlexTripBuilder { private List stopTimes; - private DurationModifier durationModifier = DurationModifier.NONE; + private TimePenalty durationModifier = TimePenalty.ZERO; UnscheduledTripBuilder(FeedScopedId id) { super(id); @@ -30,12 +31,12 @@ public List stopTimes() { return stopTimes; } - public UnscheduledTripBuilder withDurationModifier(DurationModifier factors) { + public UnscheduledTripBuilder withDurationModifier(TimePenalty factors) { this.durationModifier = factors; return this; } - public DurationModifier durationModifier() { + public TimePenalty durationModifier() { return durationModifier; } diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java index ec7d1379ca0..46160fa5daa 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java @@ -5,9 +5,9 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; -import org.opentripplanner.ext.flex.trip.DurationModifier; import org.opentripplanner.framework.collection.MapUtils; import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.routing.api.request.framework.TimePenalty; import org.opentripplanner.transit.model.timetable.Trip; /** Responsible for mapping GTFS TripMapper into the OTP model. */ @@ -18,7 +18,7 @@ class TripMapper { private final TranslationHelper translationHelper; private final Map mappedTrips = new HashMap<>(); - private final Map flexSafeDurationModifiers = new HashMap<>(); + private final Map flexSafeDurationModifiers = new HashMap<>(); TripMapper( RouteMapper routeMapper, @@ -45,7 +45,7 @@ Collection getMappedTrips() { /** * The map of flex duration factors per flex trip. */ - Map flexSafeDurationModifiers() { + Map flexSafeDurationModifiers() { return flexSafeDurationModifiers; } @@ -78,12 +78,12 @@ private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { return trip; } - private Optional mapSafeDurationModifier(org.onebusaway.gtfs.model.Trip rhs) { + private Optional mapSafeDurationModifier(org.onebusaway.gtfs.model.Trip rhs) { if (rhs.getSafeDurationFactor() == null && rhs.getSafeDurationOffset() == null) { return Optional.empty(); } else { var offset = Duration.ofSeconds(rhs.getSafeDurationOffset().longValue()); - return Optional.of(new DurationModifier(offset, rhs.getSafeDurationFactor().floatValue())); + return Optional.of(TimePenalty.of(offset, rhs.getSafeDurationFactor().doubleValue())); } } } diff --git a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java index 16b7bf22388..756a1ccf2a8 100644 --- a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java +++ b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java @@ -8,7 +8,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.opentripplanner.ext.flex.trip.DurationModifier; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.gtfs.mapping.StaySeatedNotAllowed; @@ -24,6 +23,7 @@ import org.opentripplanner.model.calendar.impl.CalendarServiceDataFactoryImpl; import org.opentripplanner.model.transfer.ConstrainedTransfer; import org.opentripplanner.model.transfer.TransferPoint; +import org.opentripplanner.routing.api.request.framework.TimePenalty; import org.opentripplanner.transit.model.basic.Notice; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; import org.opentripplanner.transit.model.framework.DefaultEntityById; @@ -94,7 +94,7 @@ public class OtpTransitServiceBuilder { private final TripStopTimes stopTimesByTrip = new TripStopTimes(); - private final Map flexDurationFactors = new HashMap<>(); + private final Map flexDurationFactors = new HashMap<>(); private final EntityById fareZonesById = new DefaultEntityById<>(); @@ -213,7 +213,7 @@ public TripStopTimes getStopTimesSortedByTrip() { return stopTimesByTrip; } - public Map getFlexDurationFactors() { + public Map getFlexDurationFactors() { return flexDurationFactors; } diff --git a/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java b/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java index 141c1d5bf6b..9424e44364b 100644 --- a/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java +++ b/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java @@ -131,7 +131,7 @@ void flexDurationModifier() { var mapper = defaultTripMapper(); var mapped = mapper.map(flexTrip); var mod = mapper.flexSafeDurationModifiers().get(mapped); - assertEquals(1.5f, mod.factor()); - assertEquals(600, mod.offsetInSeconds()); + assertEquals(1.5f, mod.coefficient()); + assertEquals(600, mod.constant().toSeconds()); } } From 355b287cb844464379c592ba94f8b6ecc850282e Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 18 Apr 2024 16:56:31 +0200 Subject: [PATCH 42/85] Update documentation --- .../org/opentripplanner/ext/flex/trip/UnscheduledTrip.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index db662a5fd45..7fa31c78114 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -151,8 +151,8 @@ public Stream getFlexAccessTemplates( } /** - * Get the correct {@link FlexPathCalculator} depending on the {@code durationModified}. - * If the modifier doesn't actually modify, we don't + * Get the correct {@link FlexPathCalculator} depending on the {@code durationModifier}. + * If the modifier doesn't actually modify, we return the regular calculator. */ protected FlexPathCalculator flexPathCalculator(FlexPathCalculator calculator) { if (!durationModifier.isZero()) { From 6588a9f1887ef82c3d00f2e6f057e4f8287c2c64 Mon Sep 17 00:00:00 2001 From: Andrew Byrd Date: Tue, 23 Apr 2024 18:25:52 +0800 Subject: [PATCH 43/85] add separate Travel Time Analysis docs page moved info out of v1-v2 comparison and accessibility pages --- docs/Accessibility.md | 17 +++++------------ docs/Analysis.md | 21 +++++++++++++++++++++ docs/Version-Comparison.md | 33 ++++++++------------------------- docs/sandbox/TravelTime.md | 6 +++++- mkdocs.yml | 1 + 5 files changed, 40 insertions(+), 38 deletions(-) create mode 100644 docs/Analysis.md diff --git a/docs/Accessibility.md b/docs/Accessibility.md index c5820facc40..49373c522c2 100644 --- a/docs/Accessibility.md +++ b/docs/Accessibility.md @@ -2,24 +2,17 @@ ## Preamble -In this document and in OTP, the term "accessibility" is used with what has become its most common +In this document and in OTP, the term "accessibility" is used with its most common meaning: design of products, devices, services, vehicles, or environments to ensure they are usable by -people with disabilities. It has other meanings in other contexts (see below). +people with disabilities. If you have reached this page looking for cumulative opportunities +accessibility indicators (access to opportunities metrics) please see the [Analysis](Analysis.md) page. + While accessibility is a complex subject, at this point GTFS and Netex mostly represent it very simply as a yes/no possibility of wheelchair use. While OTP developers hope to broaden the scope and nuance of accessibility support in OTP, the lack of detailed data from data producers -currently limits implementation and discussion in this document to this binary +currently limits implementation and discussion in this document to this binary "wheelchair accessible" definition. -The term "accessibility" has a completely separate, unrelated definition in the fields of -spatial analysis, urban transportation planning, and associated social sciences, where it refers to -quantitative indicators of how well-connected a particular location is to people or opportunities. -OpenTripPlanner has been widely used in research settings for the calculation of such accessibility -indicators. If this is what you're looking for, see the documentation sections [on isochrones](sandbox/TravelTime.md) and [analysis](Version-Comparison.md/#analysis). -Although this meaning of the term dates back many decades, it is less well known and has become a -source of confusion, so the academic and planning communities are gradually shifting to the -expression "access to opportunities", often shortened to "access". - ## Unknown data Many agencies have the same problem: data on wheelchair-accessibility is, if it exists at all, diff --git a/docs/Analysis.md b/docs/Analysis.md new file mode 100644 index 00000000000..867e90869c4 --- /dev/null +++ b/docs/Analysis.md @@ -0,0 +1,21 @@ +# Travel Time Analysis + +## Background + +Since the beginning of the project, many OTP contributors and users have been primarily interested in research, spatial analysis, and urban planning use cases. They have prototyped many ideas within or on top of the OTP codebase, including one-to-many searches producing travel time grids, isochrones, and access-to-opportunities indicators (see Terminology Note below). This has historically been a major area of application for OpenTripPlanner and has helped popularize cumulative opportunities metrics in urban planning. For example, the University of Minnesota Accessibility Observatory used OpenTripPlanner for [Access Across America](https://www.cts.umn.edu/programs/ao/aaa). + +Although we consider these use cases quite important, most work of this kind has long since shifted to separate projects focused on urban planning and analytics. As of version 2, OTP has chosen to focus entirely on passenger information rather than analytics. + +## Travel Time Analysis in OTP1 + +Much of the analysis code present in the v1.x legacy branch of OTP is essentially an unmaintained and unsupported early prototype for later projects, specifically [R5](https://github.com/conveyal/r5/) and the [Conveyal Analysis](https://conveyal.com/learn) system built upon it. OTP1 seems to have gained popularity for analysis uses due to the existence of documentation and an active user community, but has significant technical shortcomings. One of these is simply speed: OTP1 can be orders of magnitude slower (and more memory-intensive) than the approaches used in R5. The other is the requirement to search at a single specific time. Travel times and especially wait times on scheduled transit vary greatly depending on when you depart. Accounting for variation over a time window requires repeated independent searches at each possible departure time, which is very inefficient. R5 is highly optimized to capture variations in travel time across time windows and account for uncertainty in waiting times on frequency-based routes. + +## Travel Time Analysis in OTP2 + +OTP2's new transit router is quite similar to R5 (indeed it was directly influenced by R5) and would not face the same technical problems. Nonetheless, we have decided not to port the OTP1 analysis features over to OTP2 since it would broaden the scope of OTP2 away from passenger information and draw the finite amount of available attention and resources away from existing open source analytics tools. If you would like to apply the routing innovations present in OTP2 in analytics situations, we recommend taking a look at projects like R5 or the R and Python language wrappers for it created by the community. + +Some analytics features may still be available as optional "sandbox" features in OTP2 (such as [Travel Time](sandbox/TravelTime.md)), but these do not work in the same way as the features you may have used or read about in OTP1. They are unmaintained and unsupported, and may be removed in the near future. + +## Terminology Note + +In OpenTripPlanner, we usually use the term "accessibility" with its most common meaning: design of products, devices, services, vehicles, or environments to ensure they are usable by people with disabilities. The term "accessibility" has a completely separate, unrelated definition in the fields of spatial analysis, urban transportation planning, and associated social sciences, where it refers to quantitative indicators of how well-connected a particular location is to people or opportunities. OTP has been widely used in research and planning settings for the calculation of such indicators. Although this meaning of the term dates back many decades, it is less well known and has become a source of confusion, so the academic and planning communities are gradually shifting to the expression "access to opportunities", often shortened to "access". diff --git a/docs/Version-Comparison.md b/docs/Version-Comparison.md index 454188d022e..48257bdbd54 100644 --- a/docs/Version-Comparison.md +++ b/docs/Version-Comparison.md @@ -81,31 +81,14 @@ other projects where they are more actively developed. ### Analysis -Many OpenTripPlanner contributors have been primarily interested in transportation and urban -planning use cases. We consider these use cases quite important. This has been a major area of -application for OpenTripPlanner and has helped popularize cumulative opportunities accessibility -metrics. For example, the University of Minnesota Accessibility Observatory used OpenTripPlanner -for [Access Across America](http://access.umn.edu/research/america/). Nonetheless, the analysis code -in OTP1 is essentially an unmaintained and unsupported early prototype for later projects, -specifically Conveyal's R5 (and the Conveyal Analysis system built upon it). OTP1 seems to have -gained popularity for analysis uses due to the existence of documentation and an active user -community, but has significant technical shortcomings. One of these is simply speed: OTP1 can be -orders of magnitude slower (and more memory-intensive) than the approaches exemplified in R5. The -other is the requirement to search at a single specific time. Travel times and especially wait times -on scheduled transit vary greatly depending on when you depart. Accounting for variation over a time -window requires repeated independent searches at each possible departure time, which is very -inefficient. R5 is highly optimized to capture variations in travel time across time windows and -account for uncertainty in waiting times on frequency-based routes. - -Due to its similarity to the R5 approach, OTP2's transit router would not have these same problems. -Nonetheless, we have decided not to port the OTP1 analysis features over to OTP2 since it would -broaden the focus away from passenger information and draw finite attention away from existing -projects like R5 and Conveyal Analysis. - -Accordingly, we have made an effort to clean up and augment OTP1 analysis documentation for -researchers who will continue to need it. It should remain possible for people to continue using -OTP1 if they prefer. If you would instead like to apply the innovations present in OTP2, we -recommend looking into R5 or Conveyal Analysis. +From the beginning of the project, many OTP contributors and users have used OTP in research, +analysis, and planning applications. They have prototyped many ideas within the OTP codebase, +including one-to-many searches producing travel time grids, isochrones, and access-to-opportunities +metrics. While some of these features may still be available as optional "sandbox" features in OTP2, they are unsupported and may be removed in the near future. + +Most work of this kind moved over separate projects focused on urban planning and analytics. As of +version 2, OTP has chosen to focus entirely on passenger information rather than analytics +applications. See the [Analysis](Analysis.md) page for more details. ### Routers API and Hot Reloading diff --git a/docs/sandbox/TravelTime.md b/docs/sandbox/TravelTime.md index 335d454c529..817f71506f3 100644 --- a/docs/sandbox/TravelTime.md +++ b/docs/sandbox/TravelTime.md @@ -11,7 +11,11 @@ ## Documentation The API produces a snapshot of travel time form a single place to places around it. The results can -be fetched either as a set of isochrones or a raster map. +be fetched either as a set of isochrones or a raster map. Please note that as a sandbox feature this +functionality is UNSUPPORTED and neither maintained nor well-understood by most current OTP +developers, and may not be accurate or reliable. Travel time analytics work that began within OTP +has moved years ago to other projects, where it actively continues. See the +[Analysis](../Analysis.md) page for further details. ### Configuration diff --git a/mkdocs.yml b/mkdocs.yml index c14e451a906..da976615d1b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -84,6 +84,7 @@ nav: - "Stop Area Relations": 'StopAreas.md' - "Street Graph Pruning": 'IslandPruning.md' - Accessibility: 'Accessibility.md' + - "Travel Time Analysis": 'Analysis.md' - "Logging": "Logging.md" - Development: - "Developers' Guide": 'Developers-Guide.md' From 2edd276ae23d3ac5f3e199f46095b74839776105 Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Wed, 24 Apr 2024 23:15:43 +0300 Subject: [PATCH 44/85] Create factory for OtpHttpClient and some refactoring --- .../ridehailing/service/uber/UberService.java | 3 +- .../updater/SiriETGooglePubsubUpdater.java | 5 +- .../ext/siri/updater/SiriHttpLoader.java | 3 +- .../azure/AbstractAzureSiriUpdater.java | 5 +- .../SmooveBikeRentalDataSource.java | 7 +- .../vehicleparking/bikely/BikelyUpdater.java | 3 +- .../hslpark/HslFacilitiesDownloader.java | 6 +- .../hslpark/HslHubsDownloader.java | 6 +- .../hslpark/HslParkUpdater.java | 15 +- .../VehicleRentalServiceDirectoryFetcher.java | 17 ++- .../https/HttpsDataSourceRepository.java | 5 +- .../datastore/https/HttpsFileDataSource.java | 3 +- .../framework/io/JsonDataListDownloader.java | 2 +- .../framework/io/OtpHttpClient.java | 113 +-------------- .../framework/io/OtpHttpClientFactory.java | 137 ++++++++++++++++++ .../alert/GtfsRealtimeAlertsUpdater.java | 3 +- .../configure/UpdaterConfigurator.java | 6 +- .../updater/spi/GenericJsonDataSource.java | 3 +- .../trip/GtfsRealtimeTripUpdateSource.java | 3 +- ...GtfsRealtimeHttpVehiclePositionSource.java | 3 +- .../datasources/GbfsFeedLoader.java | 3 +- .../GbfsVehicleRentalDataSource.java | 5 +- .../VehicleRentalDataSourceFactory.java | 8 +- .../datasources/GbfsFeedLoaderTest.java | 10 +- .../GbfsVehicleRentalDataSourceTest.java | 12 +- 25 files changed, 223 insertions(+), 163 deletions(-) create mode 100644 src/main/java/org/opentripplanner/framework/io/OtpHttpClientFactory.java diff --git a/src/ext/java/org/opentripplanner/ext/ridehailing/service/uber/UberService.java b/src/ext/java/org/opentripplanner/ext/ridehailing/service/uber/UberService.java index 42ec65716de..9b276fc053e 100644 --- a/src/ext/java/org/opentripplanner/ext/ridehailing/service/uber/UberService.java +++ b/src/ext/java/org/opentripplanner/ext/ridehailing/service/uber/UberService.java @@ -25,6 +25,7 @@ import org.opentripplanner.ext.ridehailing.service.oauth.UrlEncodedOAuthService; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.io.OtpHttpClient; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.framework.json.ObjectMappers; import org.opentripplanner.transit.model.basic.Money; import org.slf4j.Logger; @@ -79,7 +80,7 @@ public UberService(RideHailingServiceParameters config) { this.timeEstimateUri = timeEstimateUri; this.bannedTypes = bannedTypes; this.wheelchairAccessibleProductId = wheelchairAccessibleProductId; - this.otpHttpClient = new OtpHttpClient(LOG); + this.otpHttpClient = new OtpHttpClientFactory().create(LOG); } @Override diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETGooglePubsubUpdater.java b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETGooglePubsubUpdater.java index 84c6c9abab1..9a1c5690c58 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETGooglePubsubUpdater.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETGooglePubsubUpdater.java @@ -28,7 +28,7 @@ import org.opentripplanner.ext.siri.SiriFuzzyTripMatcher; import org.opentripplanner.ext.siri.SiriTimetableSnapshotSource; import org.opentripplanner.framework.application.ApplicationShutdownSupport; -import org.opentripplanner.framework.io.OtpHttpClient; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.framework.retry.OtpRetry; import org.opentripplanner.framework.retry.OtpRetryBuilder; import org.opentripplanner.framework.text.FileSizeToTextConverter; @@ -312,7 +312,8 @@ private void initializeData() { } private ByteString fetchInitialData() { - try (OtpHttpClient otpHttpClient = new OtpHttpClient(LOG)) { + try (OtpHttpClientFactory otpHttpClientFactory = new OtpHttpClientFactory()) { + var otpHttpClient = otpHttpClientFactory.create(LOG); return otpHttpClient.getAndMap( dataInitializationUrl, initialGetDataTimeout, diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriHttpLoader.java b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriHttpLoader.java index a94580a4ce5..daf3d0397cc 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriHttpLoader.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriHttpLoader.java @@ -4,6 +4,7 @@ import java.time.Duration; import java.util.Optional; import org.opentripplanner.framework.io.OtpHttpClient; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.updater.spi.HttpHeaders; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,7 +36,7 @@ public SiriHttpLoader( this.timeout = timeout; this.requestHeaders = requestHeaders; this.previewInterval = previewInterval; - this.otpHttpClient = new OtpHttpClient(timeout, timeout, LOG); + this.otpHttpClient = new OtpHttpClientFactory(timeout, timeout).create(LOG); } /** diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdater.java b/src/ext/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdater.java index 9aa2e1d9a66..f211468cc66 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdater.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdater.java @@ -25,7 +25,7 @@ import org.opentripplanner.ext.siri.EntityResolver; import org.opentripplanner.ext.siri.SiriFuzzyTripMatcher; import org.opentripplanner.framework.application.ApplicationShutdownSupport; -import org.opentripplanner.framework.io.OtpHttpClient; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.transit.service.TransitService; @@ -206,7 +206,8 @@ public String getConfigRef() { protected Optional fetchInitialSiriData(URI uri) { var headers = HttpHeaders.of().acceptApplicationXML().build().asMap(); - try (OtpHttpClient otpHttpClient = new OtpHttpClient(LOG)) { + try (OtpHttpClientFactory otpHttpClientFactory = new OtpHttpClientFactory()) { + var otpHttpClient = otpHttpClientFactory.create(LOG); var t1 = System.currentTimeMillis(); var siriOptional = otpHttpClient.executeAndMapOptional( new HttpGet(uri), diff --git a/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSource.java b/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSource.java index 73f46ecd501..3876ff44651 100644 --- a/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSource.java +++ b/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSource.java @@ -4,6 +4,7 @@ import java.util.Map; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.framework.io.OtpHttpClient; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.service.vehiclerental.model.RentalVehicleType; import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation; @@ -33,14 +34,14 @@ public class SmooveBikeRentalDataSource private final RentalVehicleType vehicleType; public SmooveBikeRentalDataSource(SmooveBikeRentalDataSourceParameters config) { - this(config, new OtpHttpClient(LOG)); + this(config, new OtpHttpClientFactory()); } public SmooveBikeRentalDataSource( SmooveBikeRentalDataSourceParameters config, - OtpHttpClient otpHttpClient + OtpHttpClientFactory otpHttpClientFactory ) { - super(config.url(), "result", config.httpHeaders(), otpHttpClient); + super(config.url(), "result", config.httpHeaders(), otpHttpClientFactory.create(LOG)); networkName = config.getNetwork(DEFAULT_NETWORK_NAME); vehicleType = RentalVehicleType.getDefaultType(networkName); overloadingAllowed = config.overloadingAllowed(); diff --git a/src/ext/java/org/opentripplanner/ext/vehicleparking/bikely/BikelyUpdater.java b/src/ext/java/org/opentripplanner/ext/vehicleparking/bikely/BikelyUpdater.java index 1c711d6d347..2bed1121913 100644 --- a/src/ext/java/org/opentripplanner/ext/vehicleparking/bikely/BikelyUpdater.java +++ b/src/ext/java/org/opentripplanner/ext/vehicleparking/bikely/BikelyUpdater.java @@ -15,6 +15,7 @@ import org.opentripplanner.framework.i18n.LocalizedString; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.framework.io.OtpHttpClient; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.framework.json.ObjectMappers; import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces; @@ -44,7 +45,7 @@ public class BikelyUpdater implements DataSource { .put("lonMax", 0) .put("latMin", 0) .put("latMax", 0); - private final OtpHttpClient httpClient = new OtpHttpClient(LOG); + private final OtpHttpClient httpClient = new OtpHttpClientFactory().create(LOG); private final BikelyUpdaterParameters parameters; private List lots; diff --git a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslFacilitiesDownloader.java b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslFacilitiesDownloader.java index 4b89eabe19a..dcf4ffe29d9 100644 --- a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslFacilitiesDownloader.java +++ b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslFacilitiesDownloader.java @@ -12,6 +12,7 @@ import java.util.function.BiFunction; import org.opentripplanner.framework.io.OtpHttpClient; import org.opentripplanner.framework.io.OtpHttpClientException; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingGroup; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -31,12 +32,13 @@ public class HslFacilitiesDownloader { public HslFacilitiesDownloader( String url, String jsonParsePath, - BiFunction, VehicleParking> facilitiesParser + BiFunction, VehicleParking> facilitiesParser, + OtpHttpClientFactory otpHttpClientFactory ) { this.url = url; this.jsonParsePath = jsonParsePath; this.facilitiesParser = facilitiesParser; - this.otpHttpClient = new OtpHttpClient(LOG); + this.otpHttpClient = otpHttpClientFactory.create(LOG); } public List downloadFacilities( diff --git a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslHubsDownloader.java b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslHubsDownloader.java index 102551705df..f1f98b6139c 100644 --- a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslHubsDownloader.java +++ b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslHubsDownloader.java @@ -11,6 +11,7 @@ import java.util.function.Function; import org.opentripplanner.framework.io.OtpHttpClient; import org.opentripplanner.framework.io.OtpHttpClientException; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.routing.vehicle_parking.VehicleParkingGroup; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.slf4j.Logger; @@ -29,12 +30,13 @@ public class HslHubsDownloader { public HslHubsDownloader( String url, String jsonParsePath, - Function> hubsParser + Function> hubsParser, + OtpHttpClientFactory otpHttpClientFactory ) { this.url = url; this.jsonParsePath = jsonParsePath; this.hubsParser = hubsParser; - otpHttpClient = new OtpHttpClient(LOG); + otpHttpClient = otpHttpClientFactory.create(LOG); } public Map downloadHubs() { diff --git a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslParkUpdater.java b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslParkUpdater.java index e5086630941..f5c5b33ef29 100644 --- a/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslParkUpdater.java +++ b/src/ext/java/org/opentripplanner/ext/vehicleparking/hslpark/HslParkUpdater.java @@ -4,6 +4,7 @@ import java.util.Map; import java.util.stream.Collectors; import org.opentripplanner.framework.io.JsonDataListDownloader; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.model.calendar.openinghours.OpeningHoursCalendarService; import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingGroup; @@ -11,6 +12,8 @@ import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces.VehicleParkingSpacesBuilder; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.updater.spi.DataSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Vehicle parking updater class for https://github.com/HSLdevcom/parkandrideAPI format APIs. There @@ -19,6 +22,8 @@ */ public class HslParkUpdater implements DataSource { + private static final Logger LOG = LoggerFactory.getLogger(HslParkUpdater.class); + private static final String JSON_PARSE_PATH = "results"; private final HslFacilitiesDownloader facilitiesDownloader; @@ -43,24 +48,28 @@ public HslParkUpdater( new HslParkToVehicleParkingMapper(feedId, openingHoursCalendarService, parameters.timeZone()); vehicleParkingGroupMapper = new HslHubToVehicleParkingGroupMapper(feedId); parkPatchMapper = new HslParkUtilizationToPatchMapper(feedId); + var otpHttpClientFactory = new OtpHttpClientFactory(); facilitiesDownloader = new HslFacilitiesDownloader( parameters.facilitiesUrl(), JSON_PARSE_PATH, - vehicleParkingMapper::parsePark + vehicleParkingMapper::parsePark, + otpHttpClientFactory ); hubsDownloader = new HslHubsDownloader( parameters.hubsUrl(), JSON_PARSE_PATH, - vehicleParkingGroupMapper::parseHub + vehicleParkingGroupMapper::parseHub, + otpHttpClientFactory ); utilizationsDownloader = new JsonDataListDownloader<>( parameters.utilizationsUrl(), "", parkPatchMapper::parseUtilization, - Map.of() + Map.of(), + otpHttpClientFactory.create(LOG) ); this.facilitiesFrequencySec = parameters.facilitiesFrequencySec(); } diff --git a/src/ext/java/org/opentripplanner/ext/vehiclerentalservicedirectory/VehicleRentalServiceDirectoryFetcher.java b/src/ext/java/org/opentripplanner/ext/vehiclerentalservicedirectory/VehicleRentalServiceDirectoryFetcher.java index 05c54178c7b..9b350dec345 100644 --- a/src/ext/java/org/opentripplanner/ext/vehiclerentalservicedirectory/VehicleRentalServiceDirectoryFetcher.java +++ b/src/ext/java/org/opentripplanner/ext/vehiclerentalservicedirectory/VehicleRentalServiceDirectoryFetcher.java @@ -10,8 +10,8 @@ import java.util.Map; import java.util.Optional; import org.opentripplanner.ext.vehiclerentalservicedirectory.api.VehicleRentalServiceDirectoryFetcherParameters; -import org.opentripplanner.framework.io.OtpHttpClient; import org.opentripplanner.framework.io.OtpHttpClientException; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.framework.json.JsonUtils; import org.opentripplanner.routing.linking.VertexLinker; import org.opentripplanner.service.vehiclerental.VehicleRentalRepository; @@ -35,16 +35,16 @@ public class VehicleRentalServiceDirectoryFetcher { private final VertexLinker vertexLinker; private final VehicleRentalRepository repository; - private final OtpHttpClient otpHttpClient; + private final OtpHttpClientFactory otpHttpClientFactory; public VehicleRentalServiceDirectoryFetcher( VertexLinker vertexLinker, VehicleRentalRepository repository, - OtpHttpClient otpHttpClient + OtpHttpClientFactory otpHttpClientFactory ) { this.vertexLinker = vertexLinker; this.repository = repository; - this.otpHttpClient = otpHttpClient; + this.otpHttpClientFactory = otpHttpClientFactory; } public static List createUpdatersFromEndpoint( @@ -61,12 +61,12 @@ public static List createUpdatersFromEndpoint( } int maxHttpConnections = sources.size(); - var otpHttpClient = new OtpHttpClient(maxHttpConnections, LOG); + var otpHttpClientFactory = new OtpHttpClientFactory(maxHttpConnections); var serviceDirectory = new VehicleRentalServiceDirectoryFetcher( vertexLinker, repository, - otpHttpClient + otpHttpClientFactory ); return serviceDirectory.createUpdatersFromEndpoint(parameters, sources); } @@ -146,7 +146,7 @@ private VehicleRentalUpdater fetchAndCreateUpdater( var dataSource = VehicleRentalDataSourceFactory.create( vehicleRentalParameters.sourceParameters(), - otpHttpClient + otpHttpClientFactory ); return new VehicleRentalUpdater(vehicleRentalParameters, dataSource, vertexLinker, repository); } @@ -154,7 +154,8 @@ private VehicleRentalUpdater fetchAndCreateUpdater( private static JsonNode listSources(VehicleRentalServiceDirectoryFetcherParameters parameters) { JsonNode node; URI url = parameters.getUrl(); - try (OtpHttpClient otpHttpClient = new OtpHttpClient(LOG)) { + try { + var otpHttpClient = new OtpHttpClientFactory().create(LOG); node = otpHttpClient.getAndMapAsJsonNode(url, Map.of(), new ObjectMapper()); } catch (OtpHttpClientException e) { LOG.warn("Error fetching list of vehicle rental endpoints from {}", url, e); diff --git a/src/main/java/org/opentripplanner/datastore/https/HttpsDataSourceRepository.java b/src/main/java/org/opentripplanner/datastore/https/HttpsDataSourceRepository.java index 402007ea394..56afc888c73 100644 --- a/src/main/java/org/opentripplanner/datastore/https/HttpsDataSourceRepository.java +++ b/src/main/java/org/opentripplanner/datastore/https/HttpsDataSourceRepository.java @@ -11,7 +11,7 @@ import org.opentripplanner.datastore.api.FileType; import org.opentripplanner.datastore.base.DataSourceRepository; import org.opentripplanner.datastore.file.ZipStreamDataSourceDecorator; -import org.opentripplanner.framework.io.OtpHttpClient; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -79,7 +79,8 @@ private CompositeDataSource createCompositeSource(URI uri, FileType type) { } protected List

getHttpHeaders(URI uri) { - try (OtpHttpClient otpHttpClient = new OtpHttpClient(LOG)) { + try (OtpHttpClientFactory otpHttpClientFactory = new OtpHttpClientFactory()) { + var otpHttpClient = otpHttpClientFactory.create(LOG); return otpHttpClient.getHeaders(uri, HTTP_HEAD_REQUEST_TIMEOUT, Map.of()); } } diff --git a/src/main/java/org/opentripplanner/datastore/https/HttpsFileDataSource.java b/src/main/java/org/opentripplanner/datastore/https/HttpsFileDataSource.java index 9b55019332f..f81f42f9c77 100644 --- a/src/main/java/org/opentripplanner/datastore/https/HttpsFileDataSource.java +++ b/src/main/java/org/opentripplanner/datastore/https/HttpsFileDataSource.java @@ -13,6 +13,7 @@ import org.opentripplanner.datastore.file.DirectoryDataSource; import org.opentripplanner.datastore.file.ZipFileDataSource; import org.opentripplanner.framework.io.OtpHttpClient; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,7 +40,7 @@ final class HttpsFileDataSource implements DataSource { this.uri = uri; this.type = type; this.httpsDataSourceMetadata = httpsDataSourceMetadata; - otpHttpClient = new OtpHttpClient(LOG); + otpHttpClient = new OtpHttpClientFactory().create(LOG); } /** diff --git a/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java b/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java index b45604b26aa..3577a7c3646 100644 --- a/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java +++ b/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java @@ -30,7 +30,7 @@ public JsonDataListDownloader( @Nonnull Function elementParser, @Nonnull Map headers ) { - this(url, jsonParsePath, elementParser, headers, new OtpHttpClient(LOG)); + this(url, jsonParsePath, elementParser, headers, new OtpHttpClientFactory().create(LOG)); } public JsonDataListDownloader( diff --git a/src/main/java/org/opentripplanner/framework/io/OtpHttpClient.java b/src/main/java/org/opentripplanner/framework/io/OtpHttpClient.java index 959f2cbb496..678eb4754bd 100644 --- a/src/main/java/org/opentripplanner/framework/io/OtpHttpClient.java +++ b/src/main/java/org/opentripplanner/framework/io/OtpHttpClient.java @@ -20,14 +20,9 @@ import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; -import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; -import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; -import org.apache.hc.client5.http.impl.classic.HttpClients; -import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; -import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; @@ -35,11 +30,7 @@ import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.io.HttpClientResponseHandler; -import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.io.entity.StringEntity; -import org.apache.hc.core5.pool.PoolConcurrencyPolicy; -import org.apache.hc.core5.pool.PoolReusePolicy; -import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; @@ -52,107 +43,26 @@ *

Exception management

* Exceptions thrown during network operations or response mapping are wrapped in * {@link OtpHttpClientException} - *

Timeout configuration

- * The same timeout value is applied to the following parameters: - *
    - *
  • Connection request timeout: the maximum waiting time for leasing a connection in the - * connection pool. - *
  • Connect timeout: the maximum waiting time for the first packet received from the server. - *
  • Socket timeout: the maximum waiting time between two packets received from the server. - *
- * The default timeout is set to 5 seconds. - *

Connection time-to-live

- * Maximum time an HTTP connection can stay in the connection pool before being closed. - * Note that HTTP 1.1 and HTTP/2 rely on persistent connections and the HTTP server is allowed to - * close idle connections at any time. - * The default connection time-to-live is set to 1 minute. *

Resource management

- * It is recommended to use the getAndMapXXX and postAndMapXXX methods - * in this class since they - * ensure that the underlying network resources are properly released. - * The method {@link #getAsInputStream} gives access to an input stream on the body response but - * requires the caller to close this stream. For most use cases, this method is not recommended . - *

Connection Pooling

- * The connection pool holds by default a maximum of 25 connections, with maximum 5 connections - * per host. + * It is recommended to use the getAndMapXXX and postAndMapXXX methods in + * this class since they ensure that the underlying network resources are properly released. The + * method {@link #getAsInputStream} gives access to an input stream on the body response but + * requires the caller to close this stream. For most use cases, this method is not recommended. * *

Thread-safety

* Instances of this class are thread-safe. */ -public class OtpHttpClient implements AutoCloseable { - - private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(5); - - private static final Duration DEFAULT_TTL = Duration.ofMinutes(1); - - /** - * see {@link PoolingHttpClientConnectionManager#DEFAULT_MAX_TOTAL_CONNECTIONS} - */ - public static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 25; +public class OtpHttpClient { private final CloseableHttpClient httpClient; private final Logger log; - /** - * Creates an HTTP client with default timeout, default connection time-to-live and default max - * number of connections. - */ - public OtpHttpClient(Logger logger) { - this(DEFAULT_TIMEOUT, DEFAULT_TTL, logger); - } - - /** - * Creates an HTTP client with default timeout, default connection time-to-live and the given max - * number of connections. - */ - public OtpHttpClient(int maxConnections, Logger logger) { - this(DEFAULT_TIMEOUT, DEFAULT_TTL, maxConnections, logger); - } - - /** - * Creates an HTTP client the given timeout and connection time-to-live and the default max - * number of connections. - */ - public OtpHttpClient(Duration timeout, Duration connectionTtl, Logger logger) { - this(timeout, connectionTtl, DEFAULT_MAX_TOTAL_CONNECTIONS, logger); - } - /** * Creates an HTTP client with custom configuration. */ - private OtpHttpClient( - Duration timeout, - Duration connectionTtl, - int maxConnections, - Logger logger - ) { - Objects.requireNonNull(timeout); - Objects.requireNonNull(connectionTtl); - - PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder - .create() - .setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(Timeout.of(timeout)).build()) - .setPoolConcurrencyPolicy(PoolConcurrencyPolicy.STRICT) - .setConnPoolPolicy(PoolReusePolicy.LIFO) - .setMaxConnTotal(maxConnections) - .setDefaultConnectionConfig( - ConnectionConfig - .custom() - .setSocketTimeout(Timeout.of(timeout)) - .setConnectTimeout(Timeout.of(timeout)) - .setTimeToLive(TimeValue.of(connectionTtl)) - .build() - ) - .build(); - - HttpClientBuilder httpClientBuilder = HttpClients - .custom() - .setUserAgent("OpenTripPlanner") - .setConnectionManager(connectionManager) - .setDefaultRequestConfig(requestConfig(timeout)); - - httpClient = httpClientBuilder.build(); + OtpHttpClient(CloseableHttpClient httpClient, Logger logger) { + this.httpClient = httpClient; log = logger; } @@ -357,15 +267,6 @@ public Optional executeAndMapOptional( ); } - @Override - public void close() { - try { - httpClient.close(); - } catch (IOException e) { - throw new OtpHttpClientException(e); - } - } - /** * Executes an HTTP GET request and returns an input stream on the response body. The caller must * close the stream in order to release resources. Use preferably the provided getAndMapXXX diff --git a/src/main/java/org/opentripplanner/framework/io/OtpHttpClientFactory.java b/src/main/java/org/opentripplanner/framework/io/OtpHttpClientFactory.java new file mode 100644 index 00000000000..a6436168541 --- /dev/null +++ b/src/main/java/org/opentripplanner/framework/io/OtpHttpClientFactory.java @@ -0,0 +1,137 @@ +package org.opentripplanner.framework.io; + +import java.io.IOException; +import java.time.Duration; +import java.util.Objects; +import org.apache.hc.client5.http.config.ConnectionConfig; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; +import org.apache.hc.core5.http.io.SocketConfig; +import org.apache.hc.core5.pool.PoolConcurrencyPolicy; +import org.apache.hc.core5.pool.PoolReusePolicy; +import org.apache.hc.core5.util.TimeValue; +import org.apache.hc.core5.util.Timeout; +import org.slf4j.Logger; + +/** + * Factory for creating {@link OtpHttpClient} instances. These instances will share the same + * {@link CloseableHttpClient} instance. + * + *

Timeout configuration

+ * The same timeout value is applied to the following parameters: + *
    + *
  • Connection request timeout: the maximum waiting time for leasing a connection in the + * connection pool. + *
  • Connect timeout: the maximum waiting time for the first packet received from the server. + *
  • Socket timeout: the maximum waiting time between two packets received from the server. + *
+ * The default timeout is set to 5 seconds. + *

Connection time-to-live

+ * Maximum time an HTTP connection can stay in the connection pool before being closed. + * Note that HTTP 1.1 and HTTP/2 rely on persistent connections and the HTTP server is allowed to + * close idle connections at any time. + * The default connection time-to-live is set to 1 minute. + *

Connection Pooling

+ * The connection pool holds by default a maximum of 25 connections, with maximum 5 connections + * per host. + * + *

Thread-safety

+ * Instances of this class are thread-safe. + */ +public class OtpHttpClientFactory implements AutoCloseable { + + private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(5); + + private static final Duration DEFAULT_TTL = Duration.ofMinutes(1); + + /** + * see {@link PoolingHttpClientConnectionManager#DEFAULT_MAX_TOTAL_CONNECTIONS} + */ + public static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 25; + + private final CloseableHttpClient httpClient; + + /** + * Creates an HTTP client with default timeout, default connection time-to-live and default max + * number of connections. + */ + public OtpHttpClientFactory() { + this(DEFAULT_TIMEOUT, DEFAULT_TTL); + } + + /** + * Creates an HTTP client with default timeout, default connection time-to-live and the given max + * number of connections. + */ + public OtpHttpClientFactory(int maxConnections) { + this(DEFAULT_TIMEOUT, DEFAULT_TTL, maxConnections); + } + + /** + * Creates an HTTP client the given timeout and connection time-to-live and the default max + * number of connections. + */ + public OtpHttpClientFactory(Duration timeout, Duration connectionTtl) { + this(timeout, connectionTtl, DEFAULT_MAX_TOTAL_CONNECTIONS); + } + + /** + * Creates an HTTP client with custom configuration. + */ + private OtpHttpClientFactory(Duration timeout, Duration connectionTtl, int maxConnections) { + Objects.requireNonNull(timeout); + Objects.requireNonNull(connectionTtl); + + PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder + .create() + .setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(Timeout.of(timeout)).build()) + .setPoolConcurrencyPolicy(PoolConcurrencyPolicy.STRICT) + .setConnPoolPolicy(PoolReusePolicy.LIFO) + .setMaxConnTotal(maxConnections) + .setDefaultConnectionConfig( + ConnectionConfig + .custom() + .setSocketTimeout(Timeout.of(timeout)) + .setConnectTimeout(Timeout.of(timeout)) + .setTimeToLive(TimeValue.of(connectionTtl)) + .build() + ) + .build(); + + HttpClientBuilder httpClientBuilder = HttpClients + .custom() + .setUserAgent("OpenTripPlanner") + .setConnectionManager(connectionManager) + .setDefaultRequestConfig(requestConfig(timeout)); + + httpClient = httpClientBuilder.build(); + } + + public OtpHttpClient create(Logger logger) { + return new OtpHttpClient(httpClient, logger); + } + + @Override + public void close() { + try { + httpClient.close(); + } catch (IOException e) { + throw new OtpHttpClientException(e); + } + } + + /** + * Configures the request with a custom timeout. + */ + private static RequestConfig requestConfig(Duration timeout) { + return RequestConfig + .custom() + .setResponseTimeout(Timeout.of(timeout)) + .setConnectionRequestTimeout(Timeout.of(timeout)) + .build(); + } +} diff --git a/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java b/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java index ca5434d2e7b..364972531c2 100644 --- a/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java +++ b/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java @@ -3,6 +3,7 @@ import com.google.transit.realtime.GtfsRealtime.FeedMessage; import java.net.URI; import org.opentripplanner.framework.io.OtpHttpClient; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.routing.impl.TransitAlertServiceImpl; import org.opentripplanner.routing.services.TransitAlertService; @@ -50,7 +51,7 @@ public GtfsRealtimeAlertsUpdater( this.updateHandler.setFeedId(config.feedId()); this.updateHandler.setTransitAlertService(transitAlertService); this.updateHandler.setFuzzyTripMatcher(fuzzyTripMatcher); - this.otpHttpClient = new OtpHttpClient(LOG); + this.otpHttpClient = new OtpHttpClientFactory().create(LOG); LOG.info("Creating real-time alert updater running every {}: {}", pollingPeriod(), url); } diff --git a/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java b/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java index 5286173041c..3b6f7f52279 100644 --- a/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java +++ b/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java @@ -10,7 +10,7 @@ import org.opentripplanner.ext.siri.updater.azure.SiriAzureSXUpdater; import org.opentripplanner.ext.vehiclerentalservicedirectory.VehicleRentalServiceDirectoryFetcher; import org.opentripplanner.ext.vehiclerentalservicedirectory.api.VehicleRentalServiceDirectoryFetcherParameters; -import org.opentripplanner.framework.io.OtpHttpClient; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.model.calendar.openinghours.OpeningHoursCalendarService; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.service.realtimevehicles.RealtimeVehicleRepository; @@ -141,11 +141,11 @@ private List createUpdatersFromConfig() { if (!updatersParameters.getVehicleRentalParameters().isEmpty()) { int maxHttpConnections = updatersParameters.getVehicleRentalParameters().size(); - OtpHttpClient otpHttpClient = new OtpHttpClient(maxHttpConnections, LOG); + var otpHttpClientFactory = new OtpHttpClientFactory(maxHttpConnections); for (var configItem : updatersParameters.getVehicleRentalParameters()) { var source = VehicleRentalDataSourceFactory.create( configItem.sourceParameters(), - otpHttpClient + otpHttpClientFactory ); updaters.add( new VehicleRentalUpdater(configItem, source, graph.getLinker(), vehicleRentalRepository) diff --git a/src/main/java/org/opentripplanner/updater/spi/GenericJsonDataSource.java b/src/main/java/org/opentripplanner/updater/spi/GenericJsonDataSource.java index 11e4717df26..894c0a0466f 100644 --- a/src/main/java/org/opentripplanner/updater/spi/GenericJsonDataSource.java +++ b/src/main/java/org/opentripplanner/updater/spi/GenericJsonDataSource.java @@ -4,6 +4,7 @@ import java.util.List; import org.opentripplanner.framework.io.JsonDataListDownloader; import org.opentripplanner.framework.io.OtpHttpClient; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,7 +16,7 @@ public abstract class GenericJsonDataSource implements DataSource { protected List updates = List.of(); protected GenericJsonDataSource(String url, String jsonParsePath, HttpHeaders headers) { - this(url, jsonParsePath, headers, new OtpHttpClient(LOG)); + this(url, jsonParsePath, headers, new OtpHttpClientFactory().create(LOG)); } protected GenericJsonDataSource( diff --git a/src/main/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSource.java b/src/main/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSource.java index 3fa9fc6e779..12a044424ca 100644 --- a/src/main/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSource.java +++ b/src/main/java/org/opentripplanner/updater/trip/GtfsRealtimeTripUpdateSource.java @@ -10,6 +10,7 @@ import java.util.ArrayList; import java.util.List; import org.opentripplanner.framework.io.OtpHttpClient; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.updater.spi.HttpHeaders; import org.slf4j.Logger; @@ -33,7 +34,7 @@ public GtfsRealtimeTripUpdateSource(PollingTripUpdaterParameters config) { this.url = config.url(); this.headers = HttpHeaders.of().acceptProtobuf().add(config.headers()).build(); MfdzRealtimeExtensions.registerAllExtensions(registry); - otpHttpClient = new OtpHttpClient(LOG); + otpHttpClient = new OtpHttpClientFactory().create(LOG); } public List getUpdates() { diff --git a/src/main/java/org/opentripplanner/updater/vehicle_position/GtfsRealtimeHttpVehiclePositionSource.java b/src/main/java/org/opentripplanner/updater/vehicle_position/GtfsRealtimeHttpVehiclePositionSource.java index d68a67eaa9a..ec0be6822e7 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_position/GtfsRealtimeHttpVehiclePositionSource.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_position/GtfsRealtimeHttpVehiclePositionSource.java @@ -9,6 +9,7 @@ import java.util.List; import org.opentripplanner.framework.io.OtpHttpClient; import org.opentripplanner.framework.io.OtpHttpClientException; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.updater.spi.HttpHeaders; import org.slf4j.Logger; @@ -33,7 +34,7 @@ public class GtfsRealtimeHttpVehiclePositionSource { public GtfsRealtimeHttpVehiclePositionSource(URI url, HttpHeaders headers) { this.url = url; this.headers = HttpHeaders.of().acceptProtobuf().add(headers).build(); - this.otpHttpClient = new OtpHttpClient(LOG); + this.otpHttpClient = new OtpHttpClientFactory().create(LOG); } /** diff --git a/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFeedLoader.java b/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFeedLoader.java index 87f58f24f85..d4e5cbd4952 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFeedLoader.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFeedLoader.java @@ -13,6 +13,7 @@ import org.entur.gbfs.v2_3.gbfs.GBFSFeeds; import org.opentripplanner.framework.io.OtpHttpClient; import org.opentripplanner.framework.io.OtpHttpClientException; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.updater.spi.HttpHeaders; import org.opentripplanner.updater.spi.UpdaterConstructionException; import org.slf4j.Logger; @@ -98,7 +99,7 @@ public GbfsFeedLoader( } GbfsFeedLoader(String url, HttpHeaders httpHeaders, String languageCode) { - this(url, httpHeaders, languageCode, new OtpHttpClient(LOG)); + this(url, httpHeaders, languageCode, new OtpHttpClientFactory().create(LOG)); } /** diff --git a/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSource.java b/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSource.java index 8cc8897f0de..aed214102ff 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSource.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSource.java @@ -16,6 +16,7 @@ import org.entur.gbfs.v2_3.vehicle_types.GBFSVehicleTypes; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.framework.io.OtpHttpClient; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.service.vehiclerental.model.GeofencingZone; import org.opentripplanner.service.vehiclerental.model.RentalVehicleType; @@ -46,10 +47,10 @@ class GbfsVehicleRentalDataSource implements VehicleRentalDatasource { public GbfsVehicleRentalDataSource( GbfsVehicleRentalDataSourceParameters parameters, - OtpHttpClient otpHttpClient + OtpHttpClientFactory otpHttpClientFactory ) { this.params = parameters; - this.otpHttpClient = otpHttpClient; + this.otpHttpClient = otpHttpClientFactory.create(LOG); } @Override diff --git a/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/VehicleRentalDataSourceFactory.java b/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/VehicleRentalDataSourceFactory.java index 50e26d82459..bd921a70edb 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/VehicleRentalDataSourceFactory.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/VehicleRentalDataSourceFactory.java @@ -2,7 +2,7 @@ import org.opentripplanner.ext.smoovebikerental.SmooveBikeRentalDataSource; import org.opentripplanner.ext.smoovebikerental.SmooveBikeRentalDataSourceParameters; -import org.opentripplanner.framework.io.OtpHttpClient; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.updater.vehicle_rental.datasources.params.GbfsVehicleRentalDataSourceParameters; import org.opentripplanner.updater.vehicle_rental.datasources.params.VehicleRentalDataSourceParameters; @@ -10,7 +10,7 @@ public class VehicleRentalDataSourceFactory { public static VehicleRentalDatasource create( VehicleRentalDataSourceParameters source, - OtpHttpClient otpHttpClient + OtpHttpClientFactory otpHttpClientFactory ) { return switch (source.sourceType()) { // There used to be a lot more updaters and corresponding config variables here, but since @@ -20,11 +20,11 @@ public static VehicleRentalDatasource create( // and become the point of contact for the community. case GBFS -> new GbfsVehicleRentalDataSource( (GbfsVehicleRentalDataSourceParameters) source, - otpHttpClient + otpHttpClientFactory ); case SMOOVE -> new SmooveBikeRentalDataSource( (SmooveBikeRentalDataSourceParameters) source, - otpHttpClient + otpHttpClientFactory ); }; } diff --git a/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFeedLoaderTest.java b/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFeedLoaderTest.java index 2c5c36b29e7..9a0dbcdfe87 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFeedLoaderTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFeedLoaderTest.java @@ -28,9 +28,8 @@ import org.entur.gbfs.v2_3.vehicle_types.GBFSVehicleTypes; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.opentripplanner.framework.io.OtpHttpClient; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.updater.spi.HttpHeaders; -import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** @@ -92,11 +91,10 @@ void getV10FeedWithExplicitLanguage() { @Test @Disabled void fetchAllPublicFeeds() { - try ( - OtpHttpClient otpHttpClient = new OtpHttpClient( + try (OtpHttpClientFactory otpHttpClientFactory = new OtpHttpClientFactory()) { + var otpHttpClient = otpHttpClientFactory.create( LoggerFactory.getLogger(GbfsFeedLoaderTest.class) - ) - ) { + ); List exceptions = otpHttpClient.getAndMap( URI.create("https://raw.githubusercontent.com/NABSA/gbfs/master/systems.csv"), Map.of(), diff --git a/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSourceTest.java b/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSourceTest.java index 9ac5f2d44d2..018a3d06e5b 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSourceTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsVehicleRentalDataSourceTest.java @@ -10,22 +10,18 @@ import java.util.Map; import org.entur.gbfs.v2_3.vehicle_types.GBFSVehicleType; import org.junit.jupiter.api.Test; -import org.opentripplanner.framework.io.OtpHttpClient; +import org.opentripplanner.framework.io.OtpHttpClientFactory; import org.opentripplanner.service.vehiclerental.model.GeofencingZone; import org.opentripplanner.service.vehiclerental.model.RentalVehicleType; import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; import org.opentripplanner.updater.spi.HttpHeaders; import org.opentripplanner.updater.vehicle_rental.datasources.params.GbfsVehicleRentalDataSourceParameters; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * This tests the mapping between data coming from a {@link GbfsFeedLoader} to OTP station models. */ class GbfsVehicleRentalDataSourceTest { - private static final Logger LOG = LoggerFactory.getLogger(GbfsVehicleRentalDataSourceTest.class); - @Test void makeStationFromV22() { var dataSource = new GbfsVehicleRentalDataSource( @@ -38,7 +34,7 @@ void makeStationFromV22() { false, false ), - new OtpHttpClient(LOG) + new OtpHttpClientFactory() ); dataSource.setup(); @@ -129,7 +125,7 @@ void geofencing() { true, false ), - new OtpHttpClient(LOG) + new OtpHttpClientFactory() ); dataSource.setup(); @@ -171,7 +167,7 @@ void makeStationFromV10() { false, true ), - new OtpHttpClient(LOG) + new OtpHttpClientFactory() ); dataSource.setup(); From 576326b53e7597f7c709a06abcf751888103e116 Mon Sep 17 00:00:00 2001 From: Johan Torin Date: Wed, 24 Apr 2024 19:10:33 +0200 Subject: [PATCH 45/85] Fix handling of implicit access and egress mode parameters. If the 'modes' section is present but the nested 'access' or 'egress' parameters are not, then only transit that originates from/arrives directly at the stop should be used for the trip, just as if the parameter was present and set to null. Fix this by setting the corresponding mode to NOT_SET in the cases where 'modes' is specified but the parameter is not. Adapt and add tests to cover these cases better. --- .../mapping/RequestModesMapper.java | 9 ++- .../mapping/RequestModesMapperTest.java | 37 +++++++--- .../mapping/TripRequestMapperTest.java | 70 +++++++++++++++++-- 3 files changed, 99 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java b/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java index e7a13dcacc9..469d6868658 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java @@ -15,7 +15,6 @@ class RequestModesMapper { * Maps GraphQL Modes input type to RequestModes. *

* This only maps access, egress, direct & transfer modes. Transport modes are set using filters. - * Default modes are WALK for access, egress & transfer. */ static RequestModes mapRequestModes(Map modesInput) { RequestModesBuilder mBuilder = RequestModes.of(); @@ -24,11 +23,11 @@ static RequestModes mapRequestModes(Map modesInput) { StreetMode accessMode = (StreetMode) modesInput.get(accessModeKey); mBuilder.withAccessMode(accessMode); mBuilder.withTransferMode(accessMode == StreetMode.BIKE ? StreetMode.BIKE : StreetMode.WALK); + } else { + mBuilder.withAccessMode(StreetMode.NOT_SET); } - if (modesInput.containsKey(egressModeKey)) { - mBuilder.withEgressMode((StreetMode) modesInput.get(egressModeKey)); - } - // An unset directMode should overwrite the walk default, so we don't check for existence first. + // Non-existing values and null are later translated into NOT_SET. + mBuilder.withEgressMode((StreetMode) modesInput.get(egressModeKey)); mBuilder.withDirectMode((StreetMode) modesInput.get(directModeKey)); return mBuilder.build(); diff --git a/src/test/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapperTest.java b/src/test/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapperTest.java index b8d08c7d7f5..d897fc808c2 100644 --- a/src/test/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapperTest.java @@ -1,6 +1,6 @@ package org.opentripplanner.apis.transmodel.mapping; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Map; import org.junit.jupiter.api.Test; @@ -9,11 +9,32 @@ class RequestModesMapperTest { + private static final RequestModes MODES_NOT_SET = RequestModes + .of() + .withAccessMode(StreetMode.NOT_SET) + .withEgressMode(StreetMode.NOT_SET) + .withDirectMode(StreetMode.NOT_SET) + .build(); + @Test void testMapRequestModesEmptyMapReturnsDefaults() { Map inputModes = Map.of(); - RequestModes wantModes = RequestModes.of().withDirectMode(null).build(); + RequestModes mappedModes = RequestModesMapper.mapRequestModes(inputModes); + + assertEquals(MODES_NOT_SET, mappedModes); + } + + @Test + void testMapRequestModesScooterRentalAccessSetReturnsDefaultsForOthers() { + Map inputModes = Map.of("accessMode", StreetMode.SCOOTER_RENTAL); + + RequestModes wantModes = MODES_NOT_SET + .copyOf() + .withAccessMode(StreetMode.SCOOTER_RENTAL) + .withTransferMode(StreetMode.WALK) + .withDirectMode(null) + .build(); RequestModes mappedModes = RequestModesMapper.mapRequestModes(inputModes); @@ -21,11 +42,11 @@ void testMapRequestModesEmptyMapReturnsDefaults() { } @Test - void testMapRequestModesAccessSetReturnsDefaultsForOthers() { + void testMapRequestModesBikeAccessSetReturnsDefaultsForOthers() { Map inputModes = Map.of("accessMode", StreetMode.BIKE); - RequestModes wantModes = RequestModes - .of() + RequestModes wantModes = MODES_NOT_SET + .copyOf() .withAccessMode(StreetMode.BIKE) .withTransferMode(StreetMode.BIKE) .withDirectMode(null) @@ -40,8 +61,8 @@ void testMapRequestModesAccessSetReturnsDefaultsForOthers() { void testMapRequestModesEgressSetReturnsDefaultsForOthers() { Map inputModes = Map.of("egressMode", StreetMode.CAR); - RequestModes wantModes = RequestModes - .of() + RequestModes wantModes = MODES_NOT_SET + .copyOf() .withEgressMode(StreetMode.CAR) .withDirectMode(null) .build(); @@ -55,7 +76,7 @@ void testMapRequestModesEgressSetReturnsDefaultsForOthers() { void testMapRequestModesDirectSetReturnsDefaultsForOthers() { Map inputModes = Map.of("directMode", StreetMode.CAR); - RequestModes wantModes = RequestModes.of().withDirectMode(StreetMode.CAR).build(); + RequestModes wantModes = MODES_NOT_SET.copyOf().withDirectMode(StreetMode.CAR).build(); RequestModes mappedModes = RequestModesMapper.mapRequestModes(inputModes); diff --git a/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java b/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java index e35f198d16e..22524a0297b 100644 --- a/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java @@ -15,11 +15,13 @@ import io.micrometer.core.instrument.Metrics; import java.time.Duration; import java.time.LocalDate; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.function.Function; import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -64,7 +66,6 @@ public class TripRequestMapperTest implements PlanTestConstants { private static TransitModelForTest TEST_MODEL = TransitModelForTest.of(); - static final TransmodelRequestContext context; private static final Duration MAX_FLEXIBLE = Duration.ofMinutes(20); private static final Function STOP_TO_ID = s -> s.getId().toString(); @@ -76,8 +77,12 @@ public class TripRequestMapperTest implements PlanTestConstants { private static final RegularStop stop2 = TEST_MODEL.stop("ST:stop2", 2, 1).build(); private static final RegularStop stop3 = TEST_MODEL.stop("ST:stop3", 3, 1).build(); + private static final Graph graph = new Graph(); + private static final DefaultTransitService transitService; + + private TransmodelRequestContext context; + static { - var graph = new Graph(); var itinerary = newItinerary(Place.forStop(stop1), time("11:00")) .bus(route1, 1, time("11:05"), time("11:20"), Place.forStop(stop2)) .bus(route2, 2, time("11:20"), time("11:40"), Place.forStop(stop3)) @@ -103,8 +108,12 @@ public class TripRequestMapperTest implements PlanTestConstants { transitModel.updateCalendarServiceData(true, calendarServiceData, DataImportIssueStore.NOOP); transitModel.index(); - final var transitService = new DefaultTransitService(transitModel); - var defaultRequest = new RouteRequest(); + transitService = new DefaultTransitService(transitModel); + } + + @BeforeEach + void setup() { + final RouteRequest defaultRequest = new RouteRequest(); // Change defaults for FLEXIBLE to a lower value than the default 45m. This should restrict the // input to be less than 20m, not 45m. @@ -335,6 +344,59 @@ void testPassThroughPointsNoMatch() { assertEquals("No match for F:XX:NonExisting.", ex.getMessage()); } + @Test + public void testNoModes() { + var req = TripRequestMapper.createRequest(executionContext(Map.of())); + + assertEquals(StreetMode.WALK, req.journey().access().mode()); + assertEquals(StreetMode.WALK, req.journey().egress().mode()); + assertEquals(StreetMode.WALK, req.journey().direct().mode()); + assertEquals(StreetMode.WALK, req.journey().transfer().mode()); + } + + @Test + public void testEmptyModes() { + Map arguments = Map.of("modes", Map.of()); + var req = TripRequestMapper.createRequest(executionContext(arguments)); + + assertEquals(StreetMode.NOT_SET, req.journey().access().mode()); + assertEquals(StreetMode.NOT_SET, req.journey().egress().mode()); + assertEquals(StreetMode.NOT_SET, req.journey().direct().mode()); + assertEquals(StreetMode.WALK, req.journey().transfer().mode()); + } + + @Test + public void testExplicitModes() { + Map arguments = Map.of( + "modes", + Map.of( + "accessMode", + StreetMode.SCOOTER_RENTAL, + "egressMode", + StreetMode.BIKE_RENTAL, + "directMode", + StreetMode.BIKE_TO_PARK + ) + ); + var req = TripRequestMapper.createRequest(executionContext(arguments)); + + assertEquals(StreetMode.SCOOTER_RENTAL, req.journey().access().mode()); + assertEquals(StreetMode.BIKE_RENTAL, req.journey().egress().mode()); + assertEquals(StreetMode.BIKE_TO_PARK, req.journey().direct().mode()); + assertEquals(StreetMode.WALK, req.journey().transfer().mode()); + } + + @Test + public void testExplicitModesBikeAccess() { + Map arguments = Map.of("modes", Map.of("accessMode", StreetMode.BIKE)); + var req = TripRequestMapper.createRequest(executionContext(arguments)); + + assertEquals(StreetMode.BIKE, req.journey().access().mode()); + assertEquals(StreetMode.NOT_SET, req.journey().egress().mode()); + assertEquals(StreetMode.NOT_SET, req.journey().direct().mode()); + assertEquals(StreetMode.BIKE, req.journey().transfer().mode()); + } + private DataFetchingEnvironment executionContext(Map arguments) { ExecutionInput executionInput = ExecutionInput .newExecutionInput() From fd8b26c75945284cf0b8c760965af21977ada59f Mon Sep 17 00:00:00 2001 From: Joel Lappalainen Date: Thu, 25 Apr 2024 16:47:18 +0300 Subject: [PATCH 46/85] Remove references to bike rental from generic downloader --- .../framework/io/JsonDataListDownloader.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java b/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java index 3577a7c3646..3affa734605 100644 --- a/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java +++ b/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java @@ -60,11 +60,11 @@ public List download() { try { return parseJSON(is); } catch (IllegalArgumentException e) { - LOG.warn("Error parsing bike rental feed from {}", url, e); + LOG.warn("Error parsing feed from {}", url, e); } catch (JsonProcessingException e) { - LOG.warn("Error parsing bike rental feed from {} (bad JSON of some sort)", url, e); + LOG.warn("Error parsing feed from {} (bad JSON of some sort)", url, e); } catch (IOException e) { - LOG.warn("Error reading bike rental feed from {}", url, e); + LOG.warn("Error reading feed from {}", url, e); } return null; } From 7a088e0a5580990e6e28d42bac034a1c095b8a71 Mon Sep 17 00:00:00 2001 From: Henrik Abrahamsson Date: Fri, 26 Apr 2024 11:55:40 +0200 Subject: [PATCH 47/85] Make naming of stopTransferCost/stopBoardAlightCost consistent --- docs/BuildConfiguration.md | 4 +- docs/RouteRequest.md | 2 +- docs/RouterConfiguration.md | 18 ++++---- docs/examples/entur/router-config.json | 2 +- .../examples/skanetrafiken/router-config.json | 2 +- .../org/opentripplanner/model/Timetable.java | 1 + .../raptoradapter/router/TransitRouter.java | 2 +- .../raptoradapter/transit/TransitLayer.java | 21 ++++++--- .../transit/TransitTuningParameters.java | 11 +++-- .../transit/cost/CostCalculatorFactory.java | 5 +- .../transit/cost/DefaultCostCalculator.java | 46 ++++++++++++------- .../transit/mappers/TransitLayerMapper.java | 19 +++++--- .../RaptorRoutingRequestTransitData.java | 2 +- .../api/TransferOptimizationParameters.java | 7 +-- ...ansferOptimizationServiceConfigurator.java | 16 ++++--- .../model/OptimizedPathTail.java | 10 ++-- .../model/StopPriorityCostCalculator.java | 23 ++++++---- .../services/OptimizePathDomainService.java | 13 ++++-- .../config/buildconfig/GtfsConfig.java | 2 +- .../routerconfig/TransitRoutingConfig.java | 30 +++++++----- .../config/routerequest/TransferConfig.java | 2 +- .../raptor/_data/transit/TestTransitData.java | 12 ++--- ...gressStopBoardAlightTransferCostTest.java} | 10 ++-- .../mappers/TransitLayerMapperTest.java | 4 +- .../model/OptimizedPathTailTest.java | 4 +- .../standalone/config/router-config.json | 2 +- .../performance/norway/speed-test-config.json | 2 +- .../skanetrafiken/speed-test-config.json | 2 +- .../switzerland/speed-test-config.json | 2 +- 29 files changed, 166 insertions(+), 110 deletions(-) rename src/test/java/org/opentripplanner/raptor/moduletests/{B05_EgressStopTransferCostTest.java => B05_EgressStopBoardAlightTransferCostTest.java} (86%) diff --git a/docs/BuildConfiguration.md b/docs/BuildConfiguration.md index 3a3a6ff7ee4..dd60059a209 100644 --- a/docs/BuildConfiguration.md +++ b/docs/BuildConfiguration.md @@ -744,7 +744,7 @@ but we want to calculate the transfers always from OSM data. Should there be some preference or aversion for transfers at stops that are part of a station. This parameter sets the generic level of preference. What is the actual cost can be changed -with the `stopTransferCost` parameter in the router configuration. +with the `stopBoardAlightDuringTransferCost` parameter in the router configuration.

adaptivePruningDistance

@@ -993,7 +993,7 @@ but we want to calculate the transfers always from OSM data. Should there be some preference or aversion for transfers at stops that are part of a station. Overrides the value specified in `gtfsDefaults`. This parameter sets the generic level of preference. What is the actual cost can be changed -with the `stopTransferCost` parameter in the router configuration. +with the `stopBoardAlightDuringTransferCost` parameter in the router configuration.

groupFilePattern

diff --git a/docs/RouteRequest.md b/docs/RouteRequest.md index ad7dc366bcf..52fd177fbf6 100644 --- a/docs/RouteRequest.md +++ b/docs/RouteRequest.md @@ -947,7 +947,7 @@ The wait time is used to prevent *back-travel*, the `backTravelWaitTimeFactor` i Add an extra board- and alight-cost for prioritized stops. -A stopBoardAlightCosts is added to the generalized-cost during routing. But this cost +A stopBoardAlightTransferCosts is added to the generalized-cost during routing. But this cost cannot be too high, because that would add extra cost to the transfer, and favor other alternative paths. But, when optimizing transfers, we do not have to take other paths into consideration and can *boost* the stop-priority-cost to allow transfers to diff --git a/docs/RouterConfiguration.md b/docs/RouterConfiguration.md index e0f1d81b590..e948a23dd54 100644 --- a/docs/RouterConfiguration.md +++ b/docs/RouterConfiguration.md @@ -61,7 +61,7 @@ A full list of them can be found in the [RouteRequest](RouteRequest.md). |       [minWindow](#transit_dynamicSearchWindow_minWindow) | `duration` | The constant minimum duration for a raptor-search-window. | *Optional* | `"PT40M"` | 2.2 | |       [stepMinutes](#transit_dynamicSearchWindow_stepMinutes) | `integer` | Used to set the steps the search-window is rounded to. | *Optional* | `10` | 2.1 | |    [pagingSearchWindowAdjustments](#transit_pagingSearchWindowAdjustments) | `duration[]` | The provided array of durations is used to increase the search-window for the next/previous page. | *Optional* | | na | -|    [stopTransferCost](#transit_stopTransferCost) | `enum map of integer` | Use this to set a stop transfer cost for the given transfer priority | *Optional* | | 2.0 | +|    [stopBoardAlightDuringTransferCost](#transit_stopBoardAlightDuringTransferCost) | `enum map of integer` | Costs for boarding and alighting during transfers at stops with a given transfer priority. | *Optional* | | 2.0 | |    [transferCacheRequests](#transit_transferCacheRequests) | `object[]` | Routing requests to use for pre-filling the stop-to-stop transfer cache. | *Optional* | | 2.3 | | transmodelApi | `object` | Configuration for the Transmodel GraphQL API. | *Optional* | | 2.1 | |    [hideFeedId](#transmodelApi_hideFeedId) | `boolean` | Hide the FeedId in all API output, and add it to input. | *Optional* | `false` | na | @@ -358,19 +358,21 @@ previous page cursor. See JavaDoc for [TransitTuningParameters#pagingSearchWindo for more info." -

stopTransferCost

+

stopBoardAlightDuringTransferCost

**Since version:** `2.0` ∙ **Type:** `enum map of integer` ∙ **Cardinality:** `Optional` **Path:** /transit **Enum keys:** `discouraged` | `allowed` | `recommended` | `preferred` -Use this to set a stop transfer cost for the given transfer priority +Costs for boarding and alighting during transfers at stops with a given transfer priority. -The cost is applied to boarding and alighting at all stops. All stops have a transfer cost priority -set, the default is `allowed`. The `stopTransferCost` parameter is optional, but if listed all +This cost is applied **both to boarding and alighting** at stops during transfers. All stops have a transfer cost priority +set, the default is `allowed`. The `stopBoardAlightDuringTransferCost` parameter is optional, but if listed all values must be set. -If not set the `stopTransferCost` is ignored. This is only available for NeTEx imported Stops. +When a transfer occurs at the same stop, the cost will be applied twice since the cost is both for boarding and alighting, + +If not set the `stopBoardAlightDuringTransferCost` is ignored. This is only available for NeTEx imported Stops. The cost is a scalar, but is equivalent to the felt cost of riding a transit trip for 1 second. @@ -382,7 +384,7 @@ The cost is a scalar, but is equivalent to the felt cost of riding a transit tri | `preferred` | The best place to do transfers. Should be set to `0`(zero). | int | Use values in a range from `0` to `100 000`. **All key/value pairs are required if the -`stopTransferCost` is listed.** +`stopBoardAlightDuringTransferCost` is listed.**

transferCacheRequests

@@ -608,7 +610,7 @@ Used to group requests when monitoring OTP. "minWindow" : "1h", "maxWindow" : "5h" }, - "stopTransferCost" : { + "stopBoardAlightDuringTransferCost" : { "DISCOURAGED" : 1500, "ALLOWED" : 75, "RECOMMENDED" : 30, diff --git a/docs/examples/entur/router-config.json b/docs/examples/entur/router-config.json index 652f43f74bc..0cbded8bc85 100644 --- a/docs/examples/entur/router-config.json +++ b/docs/examples/entur/router-config.json @@ -91,7 +91,7 @@ "minWindow" : "1h", "maxWindow" : "5h" }, - "stopTransferCost" : { + "stopBoardAlightDuringTransferCost" : { "DISCOURAGED" : 1500, "ALLOWED" : 75, "RECOMMENDED" : 30, diff --git a/docs/examples/skanetrafiken/router-config.json b/docs/examples/skanetrafiken/router-config.json index 74042d68e31..6d782879a52 100644 --- a/docs/examples/skanetrafiken/router-config.json +++ b/docs/examples/skanetrafiken/router-config.json @@ -15,7 +15,7 @@ "24h", "0h" ], - "stopTransferCost": { + "stopBoardAlightDuringTransferCost": { "DISCOURAGED": 3000, "ALLOWED": 150, "RECOMMENDED": 60, diff --git a/src/main/java/org/opentripplanner/model/Timetable.java b/src/main/java/org/opentripplanner/model/Timetable.java index a8f0f1bbf44..031e95e8870 100644 --- a/src/main/java/org/opentripplanner/model/Timetable.java +++ b/src/main/java/org/opentripplanner/model/Timetable.java @@ -61,6 +61,7 @@ public class Timetable implements Serializable { private final List frequencyEntries = new ArrayList<>(); + @Nullable private final LocalDate serviceDate; /** Construct an empty Timetable. */ diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java index b58af3c8b23..0a9e46d2fe3 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java @@ -146,7 +146,7 @@ private TransitRouterResult route() { requestTransitDataProvider.stopNameResolver(), serverContext.transitService().getTransferService(), requestTransitDataProvider, - transitLayer.getStopBoardAlightCosts(), + transitLayer.getStopBoardAlightTransferCosts(), request.preferences().transfer().optimization(), raptorRequest.multiCriteria() ); diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayer.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayer.java index 288f429a861..0afcd97450a 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayer.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayer.java @@ -48,7 +48,11 @@ public class TransitLayer { private final TransferIndexGenerator transferIndexGenerator; - private final int[] stopBoardAlightCosts; + /** + * @see #getStopBoardAlightTransferCosts() + */ + @Nullable + private final int[] stopBoardAlightTransferCosts; /** * Makes a shallow copy of the TransitLayer, except for the tripPatternsForDate, where a shallow @@ -65,7 +69,7 @@ public TransitLayer(TransitLayer transitLayer) { transitLayer.transferCache, transitLayer.constrainedTransfers, transitLayer.transferIndexGenerator, - transitLayer.stopBoardAlightCosts + transitLayer.stopBoardAlightTransferCosts ); } @@ -78,7 +82,7 @@ public TransitLayer( RaptorRequestTransferCache transferCache, ConstrainedTransfersForPatterns constrainedTransfers, TransferIndexGenerator transferIndexGenerator, - int[] stopBoardAlightCosts + @Nullable int[] stopBoardAlightTransferCosts ) { this.tripPatternsRunningOnDate = new HashMap<>(tripPatternsRunningOnDate); this.transfersByStopIndex = transfersByStopIndex; @@ -88,7 +92,7 @@ public TransitLayer( this.transferCache = transferCache; this.constrainedTransfers = constrainedTransfers; this.transferIndexGenerator = transferIndexGenerator; - this.stopBoardAlightCosts = stopBoardAlightCosts; + this.stopBoardAlightTransferCosts = stopBoardAlightTransferCosts; } @Nullable @@ -166,8 +170,13 @@ public TransferIndexGenerator getTransferIndexGenerator() { return transferIndexGenerator; } - public int[] getStopBoardAlightCosts() { - return stopBoardAlightCosts; + /** + * Costs for both boarding and alighting at a given stop during transfer. Note that this is in + * raptor centi-second units. + */ + @Nullable + public int[] getStopBoardAlightTransferCosts() { + return stopBoardAlightTransferCosts; } /** diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitTuningParameters.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitTuningParameters.java index 31c602b6133..b62459db679 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitTuningParameters.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitTuningParameters.java @@ -13,7 +13,7 @@ public interface TransitTuningParameters { * These tuning parameters are typically used in unit tests. The values are: *
    * enableStopTransferPriority : true
-   * stopTransferCost : {
+   * stopBoardAlightDuringTransferCost : {
    *   DISCOURAGED:  3600  (equivalent of 1 hour penalty)
    *   ALLOWED:        60  (60 seconds penalty)
    *   RECOMMENDED:    20  (20 seconds penalty)
@@ -28,7 +28,7 @@ public boolean enableStopTransferPriority() {
     }
 
     @Override
-    public Integer stopTransferCost(StopTransferPriority key) {
+    public Integer stopBoardAlightDuringTransferCost(StopTransferPriority key) {
       switch (key) {
         case DISCOURAGED:
           return 3600;
@@ -70,10 +70,11 @@ public List transferCacheRequests() {
   boolean enableStopTransferPriority();
 
   /**
-   * The stop transfer cost for the given {@link StopTransferPriority}. The cost applied to boarding
-   * and alighting all stops with the given priority.
+   * The stop board alight transfer cost for the given {@link StopTransferPriority}. The cost
+   * applied during transfers to both boarding and alighting of stops with the given
+   * priority.
    */
-  Integer stopTransferCost(StopTransferPriority key);
+  Integer stopBoardAlightDuringTransferCost(StopTransferPriority key);
 
   /**
    * The maximum number of transfer RouteRequests for which the pre-calculated transfers should be
diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/CostCalculatorFactory.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/CostCalculatorFactory.java
index fef7b3509e3..a10cf828b45 100644
--- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/CostCalculatorFactory.java
+++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/CostCalculatorFactory.java
@@ -1,16 +1,17 @@
 package org.opentripplanner.routing.algorithm.raptoradapter.transit.cost;
 
+import javax.annotation.Nullable;
 import org.opentripplanner.raptor.spi.RaptorCostCalculator;
 
 public class CostCalculatorFactory {
 
   public static  RaptorCostCalculator createCostCalculator(
     GeneralizedCostParameters generalizedCostParameters,
-    int[] stopBoardAlightCosts
+    @Nullable int[] stopBoardAlightTransferCosts
   ) {
     RaptorCostCalculator calculator = new DefaultCostCalculator<>(
       generalizedCostParameters,
-      stopBoardAlightCosts
+      stopBoardAlightTransferCosts
     );
 
     if (generalizedCostParameters.wheelchairEnabled()) {
diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculator.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculator.java
index ee2f1282625..43faa3f0c40 100644
--- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculator.java
+++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculator.java
@@ -19,22 +19,28 @@ public final class DefaultCostCalculator
   private final int boardAndTransferCost;
   private final int waitFactor;
   private final FactorStrategy transitFactors;
-  private final int[] stopTransferCost;
+
+  /**
+   * Costs for boarding and alighting at a given stop during transfer.
+   * See TransitLayer.getStopBoardAlightTransferCosts()
+   */
+  @Nullable
+  private final int[] stopBoardAlightTransferCosts;
 
   /**
    * Cost unit: SECONDS - The unit for all input parameters are in the OTP TRANSIT model cost unit
    * (in Raptor the unit for cost is centi-seconds).
    *
-   * @param stopTransferCost Unit centi-seconds. This parameter is used "as-is" and not transformed
-   *                      into the Raptor cast unit to avoid the transformation for each request.
-   *                      Use {@code null} to ignore stop cost.
+   * @param stopBoardAlightTransferCosts Unit centi-seconds. This parameter is used "as-is" and not
+   *                      transformed into the Raptor cast unit to avoid the transformation for each
+   *                      request. Use {@code null} to ignore stop cost.
    */
   public DefaultCostCalculator(
     int boardCost,
     int transferCost,
     double waitReluctanceFactor,
     @Nullable double[] transitReluctanceFactors,
-    @Nullable int[] stopTransferCost
+    @Nullable int[] stopBoardAlightTransferCosts
   ) {
     this.boardCostOnly = RaptorCostConverter.toRaptorCost(boardCost);
     this.transferCostOnly = RaptorCostConverter.toRaptorCost(transferCost);
@@ -46,16 +52,19 @@ public DefaultCostCalculator(
         ? new SingleValueFactorStrategy(GeneralizedCostParameters.DEFAULT_TRANSIT_RELUCTANCE)
         : new IndexBasedFactorStrategy(transitReluctanceFactors);
 
-    this.stopTransferCost = stopTransferCost;
+    this.stopBoardAlightTransferCosts = stopBoardAlightTransferCosts;
   }
 
-  public DefaultCostCalculator(GeneralizedCostParameters params, int[] stopTransferCost) {
+  public DefaultCostCalculator(
+    GeneralizedCostParameters params,
+    @Nullable int[] stopBoardAlightTransferCosts
+  ) {
     this(
       params.boardCost(),
       params.transferCost(),
       params.waitReluctanceFactor(),
       params.transitReluctanceFactors(),
-      stopTransferCost
+      stopBoardAlightTransferCosts
     );
   }
 
@@ -108,8 +117,8 @@ public int transitArrivalCost(
 
     // Add transfer cost on all alighting events.
     // If it turns out to be the last one this cost will be removed during costEgress phase.
-    if (stopTransferCost != null) {
-      cost += stopTransferCost[toStop];
+    if (stopBoardAlightTransferCosts != null) {
+      cost += stopBoardAlightTransferCosts[toStop];
     }
 
     return cost;
@@ -134,7 +143,9 @@ public int calculateRemainingMinCost(int minTravelTime, int minNumTransfers, int
       // Remove cost that was added during alighting similar as we do in the costEgress() method
       int fixedCost = transitFactors.minFactor() * minTravelTime;
 
-      return stopTransferCost == null ? fixedCost : fixedCost - stopTransferCost[fromStop];
+      return stopBoardAlightTransferCosts == null
+        ? fixedCost
+        : fixedCost - stopBoardAlightTransferCosts[fromStop];
     }
   }
 
@@ -142,12 +153,12 @@ public int calculateRemainingMinCost(int minTravelTime, int minNumTransfers, int
   public int costEgress(RaptorAccessEgress egress) {
     if (egress.hasRides()) {
       return egress.c1() + transferCostOnly;
-    } else if (stopTransferCost != null) {
+    } else if (stopBoardAlightTransferCosts != null) {
       // Remove cost that was added during alighting.
       // We do not want to add this cost on last alighting since it should only be applied on transfers
       // It has to be done here because during alighting we do not know yet if it will be
       // a transfer or not.
-      return egress.c1() - stopTransferCost[egress.stop()];
+      return egress.c1() - stopBoardAlightTransferCosts[egress.stop()];
     } else {
       return egress.c1();
     }
@@ -171,8 +182,8 @@ public int boardingCostRegularTransfer(
     cost += firstBoarding ? boardCostOnly : boardAndTransferCost;
 
     // If it's first boarding event then it is not a transfer
-    if (stopTransferCost != null && !firstBoarding) {
-      cost += stopTransferCost[boardStop];
+    if (stopBoardAlightTransferCosts != null && !firstBoarding) {
+      cost += stopBoardAlightTransferCosts[boardStop];
     }
     return cost;
   }
@@ -210,8 +221,9 @@ private int boardingCostConstrainedTransfer(
       // For a guaranteed transfer we skip board- and transfer-cost
       final int boardWaitTime = boardTime - prevArrivalTime;
 
-      // StopTransferCost is NOT added to the cost here. This is because a trip-to-trip constrained transfer take
-      // precedence over stop-to-stop transfer priority (NeTEx station transfer priority).
+      // StopBoardAlightTransferCost is NOT added to the cost here. This is because a trip-to-trip
+      // constrained transfer take precedence over stop-to-stop transfer priority (NeTEx station
+      // transfer priority).
       return waitFactor * boardWaitTime;
     }
 
diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java
index d74e2fbcbdb..fe81d28c098 100644
--- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java
+++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java
@@ -13,6 +13,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
+import javax.annotation.Nullable;
 import org.opentripplanner.framework.application.OTPFeature;
 import org.opentripplanner.model.Timetable;
 import org.opentripplanner.routing.algorithm.raptoradapter.transit.Transfer;
@@ -103,7 +104,7 @@ private TransitLayer map(TransitTuningParameters tuningParameters) {
       transferCache,
       constrainedTransfers,
       transferIndexGenerator,
-      createStopTransferCosts(stopModel, tuningParameters)
+      createStopBoardAlightTransferCosts(stopModel, tuningParameters)
     );
   }
 
@@ -179,19 +180,23 @@ private HashMap> keyByRunningPeriodDates(
   }
 
   /**
-   * Create static board/alight cost for Raptor to include for each stop.
+   * Create static board/alight cost for Raptor to apply during transfer
    */
-  static int[] createStopTransferCosts(StopModel stops, TransitTuningParameters tuningParams) {
+  @Nullable
+  static int[] createStopBoardAlightTransferCosts(
+    StopModel stops,
+    TransitTuningParameters tuningParams
+  ) {
     if (!tuningParams.enableStopTransferPriority()) {
       return null;
     }
-    int[] stopTransferCosts = new int[stops.stopIndexSize()];
+    int[] stopBoardAlightTransferCosts = new int[stops.stopIndexSize()];
 
     for (int i = 0; i < stops.stopIndexSize(); ++i) {
       StopTransferPriority priority = stops.stopByIndex(i).getPriority();
-      int domainCost = tuningParams.stopTransferCost(priority);
-      stopTransferCosts[i] = RaptorCostConverter.toRaptorCost(domainCost);
+      int domainCost = tuningParams.stopBoardAlightDuringTransferCost(priority);
+      stopBoardAlightTransferCosts[i] = RaptorCostConverter.toRaptorCost(domainCost);
     }
-    return stopTransferCosts;
+    return stopBoardAlightTransferCosts;
   }
 }
diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java
index 4338593d59d..8c310206a01 100644
--- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java
+++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java
@@ -104,7 +104,7 @@ public RaptorRoutingRequestTransitData(
     this.generalizedCostCalculator =
       CostCalculatorFactory.createCostCalculator(
         mcCostParams,
-        transitLayer.getStopBoardAlightCosts()
+        transitLayer.getStopBoardAlightTransferCosts()
       );
 
     this.slackProvider =
diff --git a/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/api/TransferOptimizationParameters.java b/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/api/TransferOptimizationParameters.java
index 554de3ddc6d..0f51dd83165 100644
--- a/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/api/TransferOptimizationParameters.java
+++ b/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/api/TransferOptimizationParameters.java
@@ -35,9 +35,10 @@ public interface TransferOptimizationParameters {
   double minSafeWaitTimeFactor();
 
   /**
-   * Use this to add an extra board- and alight-cost for (none) prioritized stops. A {@code
-   * stopBoardAlightCosts} is added to the generalized-cost during routing. But, this cost cannot be
-   * too high, because that would add extra cost to the transfer, and favor other alternative paths.
+   * Use this to add an extra board- and alight-cost for (non) prioritized stops. A {@code
+   * stopBoardAlightTransferCosts} is added to the generalized-cost during routing. But, this cost
+   * cannot be too high, because that would add extra cost to the transfer, and favor other
+   * alternative paths.
    * But, when optimizing transfers, we do not have to take other paths into consideration and can
    * "boost" the stop-priority-cost to allow transfers to take place at a preferred stop.
    * 

diff --git a/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/configure/TransferOptimizationServiceConfigurator.java b/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/configure/TransferOptimizationServiceConfigurator.java index a733927f068..cb5f3d7fdab 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/configure/TransferOptimizationServiceConfigurator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/configure/TransferOptimizationServiceConfigurator.java @@ -1,6 +1,7 @@ package org.opentripplanner.routing.algorithm.transferoptimization.configure; import java.util.function.IntFunction; +import javax.annotation.Nullable; import org.opentripplanner.model.transfer.TransferService; import org.opentripplanner.raptor.api.model.RaptorTripSchedule; import org.opentripplanner.raptor.api.path.RaptorStopNameResolver; @@ -28,7 +29,10 @@ public class TransferOptimizationServiceConfigurator transitDataProvider; - private final int[] stopBoardAlightCosts; + + @Nullable + private final int[] stopBoardAlightTransferCosts; + private final TransferOptimizationParameters config; private final MultiCriteriaRequest multiCriteriaRequest; @@ -37,7 +41,7 @@ private TransferOptimizationServiceConfigurator( RaptorStopNameResolver stopNameResolver, TransferService transferService, RaptorTransitDataProvider transitDataProvider, - int[] stopBoardAlightCosts, + int[] stopBoardAlightTransferCosts, TransferOptimizationParameters config, MultiCriteriaRequest multiCriteriaRequest ) { @@ -45,7 +49,7 @@ private TransferOptimizationServiceConfigurator( this.stopNameResolver = stopNameResolver; this.transferService = transferService; this.transitDataProvider = transitDataProvider; - this.stopBoardAlightCosts = stopBoardAlightCosts; + this.stopBoardAlightTransferCosts = stopBoardAlightTransferCosts; this.config = config; this.multiCriteriaRequest = multiCriteriaRequest; } @@ -60,7 +64,7 @@ > OptimizeTransferService createOptimizeTransferService( RaptorStopNameResolver stopNameResolver, TransferService transferService, RaptorTransitDataProvider transitDataProvider, - int[] stopBoardAlightCosts, + @Nullable int[] stopBoardAlightTransferCosts, TransferOptimizationParameters config, MultiCriteriaRequest multiCriteriaRequest ) { @@ -69,7 +73,7 @@ > OptimizeTransferService createOptimizeTransferService( stopNameResolver, transferService, transitDataProvider, - stopBoardAlightCosts, + stopBoardAlightTransferCosts, config, multiCriteriaRequest ) @@ -113,7 +117,7 @@ private OptimizePathDomainService createOptimizePathService( costCalculator, transitDataProvider.slackProvider(), transferWaitTimeCostCalculator, - stopBoardAlightCosts, + stopBoardAlightTransferCosts, config.extraStopBoardAlightCostsFactor(), createFilter(), stopNameResolver diff --git a/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/model/OptimizedPathTail.java b/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/model/OptimizedPathTail.java index fa74648e8eb..8d18c461101 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/model/OptimizedPathTail.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/model/OptimizedPathTail.java @@ -33,6 +33,7 @@ public class OptimizedPathTail @Nullable private final TransferWaitTimeCostCalculator waitTimeCostCalculator; + @Nullable private final StopPriorityCostCalculator stopPriorityCostCalculator; private int transferPriorityCost = TransferConstraint.ZERO_COST; @@ -44,15 +45,18 @@ public OptimizedPathTail( RaptorCostCalculator costCalculator, int iterationDepartureTime, TransferWaitTimeCostCalculator waitTimeCostCalculator, - int[] stopBoardAlightCosts, + @Nullable int[] stopBoardAlightTransferCosts, double extraStopBoardAlightCostsFactor, RaptorStopNameResolver stopNameResolver ) { super(slackProvider, iterationDepartureTime, costCalculator, stopNameResolver, null); this.waitTimeCostCalculator = waitTimeCostCalculator; this.stopPriorityCostCalculator = - (stopBoardAlightCosts != null && extraStopBoardAlightCostsFactor > 0.01) - ? new StopPriorityCostCalculator(extraStopBoardAlightCostsFactor, stopBoardAlightCosts) + (stopBoardAlightTransferCosts != null && extraStopBoardAlightCostsFactor > 0.01) + ? new StopPriorityCostCalculator( + extraStopBoardAlightCostsFactor, + stopBoardAlightTransferCosts + ) : null; } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/model/StopPriorityCostCalculator.java b/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/model/StopPriorityCostCalculator.java index 090dd1ebd5a..48a4b733b58 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/model/StopPriorityCostCalculator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/model/StopPriorityCostCalculator.java @@ -1,22 +1,29 @@ package org.opentripplanner.routing.algorithm.transferoptimization.model; import org.opentripplanner.framework.lang.IntUtils; +import org.opentripplanner.routing.algorithm.raptoradapter.transit.TransitLayer; /** - * This calculator is used to give the stop-priority-cost a boost, by multiplying it with a {@code - * factor}. + * This class calculates an extra stop priority cost by using the stop-board-alight-transfer-cost + * and boosting it by multiplying it with a {@code factor}. */ public class StopPriorityCostCalculator { - private final int[] stopTransferCost; - private final double extraStopTransferCostFactor; + /** + * @see TransitLayer#getStopBoardAlightTransferCosts() + */ + private final int[] stopBoardAlightTransferCosts; + private final double extraStopBoardAlightCostFactor; - StopPriorityCostCalculator(double extraStopTransferCostFactor, int[] stopTransferCost) { - this.stopTransferCost = stopTransferCost; - this.extraStopTransferCostFactor = extraStopTransferCostFactor; + StopPriorityCostCalculator( + double extraStopBoardAlightCostFactor, + int[] stopBoardAlightTransferCosts + ) { + this.stopBoardAlightTransferCosts = stopBoardAlightTransferCosts; + this.extraStopBoardAlightCostFactor = extraStopBoardAlightCostFactor; } int extraStopPriorityCost(int stop) { - return IntUtils.round(stopTransferCost[stop] * extraStopTransferCostFactor); + return IntUtils.round(stopBoardAlightTransferCosts[stop] * extraStopBoardAlightCostFactor); } } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/services/OptimizePathDomainService.java b/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/services/OptimizePathDomainService.java index bfe5c3de841..ca8f953ced5 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/services/OptimizePathDomainService.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/services/OptimizePathDomainService.java @@ -8,7 +8,6 @@ import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nullable; -import org.opentripplanner.raptor.api.model.RaptorConstants; import org.opentripplanner.raptor.api.model.RaptorTripSchedule; import org.opentripplanner.raptor.api.path.RaptorPath; import org.opentripplanner.raptor.api.path.RaptorStopNameResolver; @@ -16,6 +15,7 @@ import org.opentripplanner.raptor.api.path.TransitPathLeg; import org.opentripplanner.raptor.spi.RaptorCostCalculator; import org.opentripplanner.raptor.spi.RaptorSlackProvider; +import org.opentripplanner.routing.algorithm.raptoradapter.transit.TransitLayer; import org.opentripplanner.routing.algorithm.transferoptimization.api.OptimizedPath; import org.opentripplanner.routing.algorithm.transferoptimization.model.OptimizedPathTail; import org.opentripplanner.routing.algorithm.transferoptimization.model.PathTailFilter; @@ -78,8 +78,11 @@ public class OptimizePathDomainService { @Nullable private final TransferWaitTimeCostCalculator waitTimeCostCalculator; + /** + * @see TransitLayer#getStopBoardAlightTransferCosts() + */ @Nullable - private final int[] stopBoardAlightCosts; + private final int[] stopBoardAlightTransferCosts; private final double extraStopBoardAlightCostsFactor; @@ -88,7 +91,7 @@ public OptimizePathDomainService( RaptorCostCalculator costCalculator, RaptorSlackProvider slackProvider, @Nullable TransferWaitTimeCostCalculator waitTimeCostCalculator, - int[] stopBoardAlightCosts, + @Nullable int[] stopBoardAlightTransferCosts, double extraStopBoardAlightCostsFactor, PathTailFilter filter, RaptorStopNameResolver stopNameTranslator @@ -97,7 +100,7 @@ public OptimizePathDomainService( this.costCalculator = costCalculator; this.slackProvider = slackProvider; this.waitTimeCostCalculator = waitTimeCostCalculator; - this.stopBoardAlightCosts = stopBoardAlightCosts; + this.stopBoardAlightTransferCosts = stopBoardAlightTransferCosts; this.extraStopBoardAlightCostsFactor = extraStopBoardAlightCostsFactor; this.filter = filter; this.stopNameTranslator = stopNameTranslator; @@ -139,7 +142,7 @@ private Set> findBestTransferOption( costCalculator, iterationDepartureTime, waitTimeCostCalculator, - stopBoardAlightCosts, + stopBoardAlightTransferCosts, extraStopBoardAlightCostsFactor, stopNameTranslator ) diff --git a/src/main/java/org/opentripplanner/standalone/config/buildconfig/GtfsConfig.java b/src/main/java/org/opentripplanner/standalone/config/buildconfig/GtfsConfig.java index a0a947e429a..e4a400dffe3 100644 --- a/src/main/java/org/opentripplanner/standalone/config/buildconfig/GtfsConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/buildconfig/GtfsConfig.java @@ -74,7 +74,7 @@ private static GtfsFeedParametersBuilder mapGenericParameters( .description( """ This parameter sets the generic level of preference. What is the actual cost can be changed - with the `stopTransferCost` parameter in the router configuration. + with the `stopBoardAlightDuringTransferCost` parameter in the router configuration. """ ) .docDefaultValue(docDefaults.stationTransferPreference()) diff --git a/src/main/java/org/opentripplanner/standalone/config/routerconfig/TransitRoutingConfig.java b/src/main/java/org/opentripplanner/standalone/config/routerconfig/TransitRoutingConfig.java index d4c99004a59..34465cff27a 100644 --- a/src/main/java/org/opentripplanner/standalone/config/routerconfig/TransitRoutingConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/routerconfig/TransitRoutingConfig.java @@ -31,7 +31,7 @@ public final class TransitRoutingConfig implements RaptorTuningParameters, Trans private final List transferCacheRequests; private final List pagingSearchWindowAdjustments; - private final Map stopTransferCost; + private final Map stopBoardAlightDuringTransferCost; private final Duration maxSearchWindow; private final DynamicSearchWindowCoefficients dynamicSearchWindowCoefficients; @@ -114,18 +114,24 @@ public TransitRoutingConfig( ) .asInt(dft.searchThreadPoolSize()); // Dynamic Search Window - this.stopTransferCost = + this.stopBoardAlightDuringTransferCost = c - .of("stopTransferCost") + .of("stopBoardAlightDuringTransferCost") .since(V2_0) - .summary("Use this to set a stop transfer cost for the given transfer priority") + .summary( + "Costs for boarding and alighting during transfers at stops with a given transfer priority." + ) .description( """ -The cost is applied to boarding and alighting at all stops. All stops have a transfer cost priority -set, the default is `allowed`. The `stopTransferCost` parameter is optional, but if listed all -values must be set. +This cost is applied **both to boarding and alighting** at stops during transfers. All stops have a +transfer cost priority set, the default is `allowed`. The `stopBoardAlightDuringTransferCost` +parameter is optional, but if listed all values must be set. + +When a transfer occurs at the same stop, the cost will be applied twice since the cost is both for +boarding and alighting, -If not set the `stopTransferCost` is ignored. This is only available for NeTEx imported Stops. +If not set the `stopBoardAlightDuringTransferCost` is ignored. This is only available for NeTEx +imported Stops. The cost is a scalar, but is equivalent to the felt cost of riding a transit trip for 1 second. @@ -137,7 +143,7 @@ public TransitRoutingConfig( | `preferred` | The best place to do transfers. Should be set to `0`(zero). | int | Use values in a range from `0` to `100 000`. **All key/value pairs are required if the -`stopTransferCost` is listed.** +`stopBoardAlightDuringTransferCost` is listed.** """ ) .asEnumMapAllKeysRequired(StopTransferPriority.class, Integer.class); @@ -250,12 +256,12 @@ public DynamicSearchWindowCoefficients dynamicSearchWindowCoefficients() { @Override public boolean enableStopTransferPriority() { - return stopTransferCost != null; + return stopBoardAlightDuringTransferCost != null; } @Override - public Integer stopTransferCost(StopTransferPriority key) { - return stopTransferCost.get(key); + public Integer stopBoardAlightDuringTransferCost(StopTransferPriority key) { + return stopBoardAlightDuringTransferCost.get(key); } @Override diff --git a/src/main/java/org/opentripplanner/standalone/config/routerequest/TransferConfig.java b/src/main/java/org/opentripplanner/standalone/config/routerequest/TransferConfig.java index 91665d56d38..365c4e89145 100644 --- a/src/main/java/org/opentripplanner/standalone/config/routerequest/TransferConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/routerequest/TransferConfig.java @@ -144,7 +144,7 @@ private static TransferOptimizationPreferences mapTransferOptimization(NodeAdapt .summary("Add an extra board- and alight-cost for prioritized stops.") .description( """ - A stopBoardAlightCosts is added to the generalized-cost during routing. But this cost + A stopBoardAlightTransferCosts is added to the generalized-cost during routing. But this cost cannot be too high, because that would add extra cost to the transfer, and favor other alternative paths. But, when optimizing transfers, we do not have to take other paths into consideration and can *boost* the stop-priority-cost to allow transfers to diff --git a/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java b/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java index 037c345bc23..97ad09e3bfd 100644 --- a/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java +++ b/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java @@ -66,7 +66,7 @@ public class TestTransitData private final List constrainedTransfers = new ArrayList<>(); private final GeneralizedCostParametersBuilder costParamsBuilder = GeneralizedCostParameters.of(); - private final int[] stopBoardAlightCosts = new int[NUM_STOPS]; + private final int[] stopBoardAlightTransferCosts = new int[NUM_STOPS]; private RaptorSlackProvider slackProvider = SLACK_PROVIDER; @@ -106,7 +106,7 @@ public int numberOfStops() { public RaptorCostCalculator multiCriteriaCostCalculator() { return CostCalculatorFactory.createCostCalculator( costParamsBuilder.build(), - stopBoardAlightCost() + stopBoardAlightTransferCosts() ); } @@ -302,8 +302,8 @@ public TestTransitData withConstrainedTransfer( return this; } - public TestTransitData withStopBoardAlightCost(int stop, int boardAlightCost) { - stopBoardAlightCosts[stop] = boardAlightCost; + public TestTransitData withStopBoardAlightTransferCost(int stop, int boardAlightTransferCost) { + stopBoardAlightTransferCosts[stop] = boardAlightTransferCost; return this; } @@ -357,9 +357,9 @@ public List getPatterns() { /* private methods */ - private int[] stopBoardAlightCost() { + private int[] stopBoardAlightTransferCosts() { // Not implemented, no test for this yet. - return stopBoardAlightCosts; + return stopBoardAlightTransferCosts; } private void expandNumOfStops(int stopIndex) { diff --git a/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopTransferCostTest.java b/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopBoardAlightTransferCostTest.java similarity index 86% rename from src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopTransferCostTest.java rename to src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopBoardAlightTransferCostTest.java index 21c1dd2a755..df38aabc483 100644 --- a/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopTransferCostTest.java +++ b/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopBoardAlightTransferCostTest.java @@ -22,10 +22,10 @@ /** * FEATURE UNDER TEST *

- * This verifies that the stopTransferCost is not applied for egress legs. If this is not correctly - * handled by the heuristics optimization, the cheapest journey could be discarded. + * This verifies that the stopBoardAlightTransferCost is not applied for egress legs. If this is not + * correctly handled by the heuristics optimization, the cheapest journey could be discarded. */ -public class B05_EgressStopTransferCostTest implements RaptorTestConstants { +public class B05_EgressStopBoardAlightTransferCostTest implements RaptorTestConstants { private final TestTransitData data = new TestTransitData(); private final RaptorRequestBuilder requestBuilder = new RaptorRequestBuilder<>(); @@ -40,7 +40,7 @@ void setup() { .withRoute(route("R2", STOP_C, STOP_D).withTimetable(schedule("0:18, 0:20"))); data.mcCostParamsBuilder().transferCost(0).boardCost(0); - data.withStopBoardAlightCost(STOP_D, 60000); + data.withStopBoardAlightTransferCost(STOP_D, 60000); requestBuilder .searchParams() @@ -61,7 +61,7 @@ static List testCases() { .add( multiCriteria(), // We should get both the fastest and the c1-cheapest results - // The stopTransferCost should not be applied to the egress leg from STOP_D + // The stopBoardAlightTransferCost should not be applied to the egress leg from STOP_D "B ~ BUS R1 0:10 0:14 ~ C ~ Walk 5m [0:10 0:19 9m Tₓ0 C₁840]", "B ~ BUS R1 0:10 0:14 ~ C ~ BUS R2 0:18 0:20 ~ D ~ Walk 20s [0:10 0:20:20 10m20s Tₓ1 C₁640]" ) diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapperTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapperTest.java index bebc11fd2c2..9296ee2730c 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapperTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapperTest.java @@ -42,8 +42,8 @@ class TransitLayerMapperTest { private final List STOPS = Arrays.asList(STOP_0, STOP_1, STOP_2, STOP_3, STOP_4); @Test - public void createStopTransferCosts() { - int[] result = TransitLayerMapper.createStopTransferCosts( + public void createStopBoardAlightTransferCosts() { + int[] result = TransitLayerMapper.createStopBoardAlightTransferCosts( new StopModelMock(STOPS), TransitTuningParameters.FOR_TEST ); diff --git a/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/OptimizedPathTailTest.java b/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/OptimizedPathTailTest.java index f9bdf0df273..8c82f68a8bc 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/OptimizedPathTailTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/transferoptimization/model/OptimizedPathTailTest.java @@ -53,14 +53,14 @@ class OptimizedPathTailTest implements RaptorTestConstants { * Extra cost = (30.0 + 2 * 10.0) * 2.0 = $100.00 *

*/ - private final int[] stopBoardAlightCost = new int[] { 0, 0, 3000, 0, 1000, 0 }; + private final int[] stopBoardAlightTransferCosts = new int[] { 0, 0, 3000, 0, 1000, 0 }; private final OptimizedPathTail subject = new OptimizedPathTail<>( SLACK_PROVIDER, BasicPathTestCase.C1_CALCULATOR, 0, waitTimeCalc, - stopBoardAlightCost, + stopBoardAlightTransferCosts, 2.0, this::stopIndexToName ); diff --git a/src/test/resources/standalone/config/router-config.json b/src/test/resources/standalone/config/router-config.json index f5eaa1f942b..1fb20306049 100644 --- a/src/test/resources/standalone/config/router-config.json +++ b/src/test/resources/standalone/config/router-config.json @@ -171,7 +171,7 @@ "minWindow": "1h", "maxWindow": "5h" }, - "stopTransferCost": { + "stopBoardAlightDuringTransferCost": { "DISCOURAGED": 1500, "ALLOWED": 75, "RECOMMENDED": 30, diff --git a/test/performance/norway/speed-test-config.json b/test/performance/norway/speed-test-config.json index e4ca99d7fe5..e8e33576769 100644 --- a/test/performance/norway/speed-test-config.json +++ b/test/performance/norway/speed-test-config.json @@ -31,7 +31,7 @@ // stepMinutes: 10 // stepMinutes: 10 }, - "stopTransferCost": { + "stopBoardAlightDuringTransferCost": { "DISCOURAGED": 86400, "ALLOWED": 3000, "RECOMMENDED": 300, diff --git a/test/performance/skanetrafiken/speed-test-config.json b/test/performance/skanetrafiken/speed-test-config.json index 66184ab124f..69a12755336 100644 --- a/test/performance/skanetrafiken/speed-test-config.json +++ b/test/performance/skanetrafiken/speed-test-config.json @@ -7,7 +7,7 @@ "dynamicSearchWindow": { "maxWindow": "24h" }, - "stopTransferCost": { + "stopBoardAlightDuringTransferCost": { "DISCOURAGED": 3000, "ALLOWED": 150, "RECOMMENDED": 60, diff --git a/test/performance/switzerland/speed-test-config.json b/test/performance/switzerland/speed-test-config.json index 19ac7a1358a..7c2e692d2cb 100644 --- a/test/performance/switzerland/speed-test-config.json +++ b/test/performance/switzerland/speed-test-config.json @@ -31,7 +31,7 @@ // stepMinutes: 10 // stepMinutes: 10 }, - "stopTransferCost": { + "stopBoardAlightDuringTransferCost": { "DISCOURAGED": 86400, "ALLOWED": 3000, "RECOMMENDED": 300, From 0fd7eca0e2943d6285e095fe2c2b3e93cc985237 Mon Sep 17 00:00:00 2001 From: Yannik Tausch Date: Thu, 2 May 2024 14:59:49 +0200 Subject: [PATCH 48/85] point to additional i in GraphiQL URL --- docs/apis/GTFS-GraphQL-API.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/apis/GTFS-GraphQL-API.md b/docs/apis/GTFS-GraphQL-API.md index f2cc32e2888..cda512bc689 100644 --- a/docs/apis/GTFS-GraphQL-API.md +++ b/docs/apis/GTFS-GraphQL-API.md @@ -11,7 +11,7 @@ makes it easy to use this API in a Java application. ## URLs - GraphQL endpoint: [`http://localhost:8080/otp/gtfs/v1`](http://localhost:8080/otp/gtfs/v1) - HTML schema documentation: [https://docs.opentripplanner.org/api/dev-2.x/graphql-gtfs/](https://docs.opentripplanner.org/api/dev-2.x/graphql-gtfs/) - - Built-in visual GraphQL client: [http://localhost:8080/graphiql](http://localhost:8080/graphiql) + - Built-in visual GraphQL client: [http://localhost:8080/graphiql](http://localhost:8080/graphiql) (note the additional `i`!) ## Built-in API client @@ -43,4 +43,4 @@ If you want to disable it, do it in `otp-config.json`: "GtfsGraphQlApi": false } } -``` \ No newline at end of file +``` From 8ad34a5c68e99f5dbcc73e9a9100177f49e9ace1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 5 May 2024 04:16:00 +0000 Subject: [PATCH 49/85] Update jackson.version to v2.17.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ce89fc467e6..fd55f783970 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ 31.0 2.51.1 - 2.17.0 + 2.17.1 3.1.6 5.10.2 1.12.3 From 2dd2825a421f8ca5bc8f93d36a12f3f46e5c3cfa Mon Sep 17 00:00:00 2001 From: Johan Torin Date: Fri, 3 May 2024 17:20:26 +0200 Subject: [PATCH 50/85] Add test-case for explicit null modes parameter. --- .../mapping/TripRequestMapperTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java b/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java index 22524a0297b..654c52ce912 100644 --- a/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java @@ -16,9 +16,11 @@ import java.time.Duration; import java.time.LocalDate; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.function.Function; import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; @@ -365,6 +367,21 @@ public void testEmptyModes() { assertEquals(StreetMode.WALK, req.journey().transfer().mode()); } + @Test + public void testNullModes() { + HashMap modes = new HashMap<>(); + modes.put("accessMode", null); + modes.put("egressMode", null); + modes.put("directMode", null); + Map arguments = Map.of("modes", modes); + var req = TripRequestMapper.createRequest(executionContext(arguments)); + + assertEquals(StreetMode.NOT_SET, req.journey().access().mode()); + assertEquals(StreetMode.NOT_SET, req.journey().egress().mode()); + assertEquals(StreetMode.NOT_SET, req.journey().direct().mode()); + assertEquals(StreetMode.WALK, req.journey().transfer().mode()); + } + @Test public void testExplicitModes() { Map arguments = Map.of( From 4013d70e0baca11a42e053ffaa76daa5ce188e62 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 6 May 2024 14:26:37 +0200 Subject: [PATCH 51/85] Remove unneeded operation --- .../module/osm/naming/SidewalkNamer.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index 990d1d73442..a8dd169dfe5 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -23,7 +23,6 @@ import org.locationtech.jts.geom.MultiLineString; import org.locationtech.jts.geom.Point; import org.locationtech.jts.operation.buffer.BufferParameters; -import org.locationtech.jts.operation.distance.DistanceOp; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.geometry.HashGridSpatialIndex; import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; @@ -146,8 +145,6 @@ private void assignNameToSidewalk(EdgeOnLevel sidewalkOnLevel, AtomicInteger nam var candidates = streetEdges.query(buffer.getEnvelopeInternal()); groupEdgesByName(candidates) - // remove edges that are far away - .filter(g -> g.nearestDistanceTo(sidewalk.getGeometry()) < MAX_DISTANCE_TO_SIDEWALK) // make sure we only compare sidewalks and streets that are on the same level .filter(g -> g.levels.equals(sidewalkOnLevel.levels)) .map(g -> computePercentInsideBuffer(g, buffer, sidewalkLength)) @@ -240,19 +237,6 @@ private double length(Geometry intersection) { }; } - /** - * Get the closest distance in meters between any of the edges in the group and the given geometry. - */ - double nearestDistanceTo(Geometry g) { - return edges - .stream() - .mapToDouble(e -> { - var points = DistanceOp.nearestPoints(e.getGeometry(), g); - return SphericalDistanceLibrary.fastDistance(points[0], points[1]); - }) - .min() - .orElse(Double.MAX_VALUE); - } } private record EdgeOnLevel(StreetEdge edge, Set levels) {} From e28a69c75fd5d4681f2f8a6213caba640671327c Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 6 May 2024 14:34:47 +0200 Subject: [PATCH 52/85] Improve performance of Portland custom namer --- .../module/osm/naming/PortlandCustomNamer.java | 11 ++++------- .../module/osm/naming/SidewalkNamer.java | 1 - .../openstreetmap/model/OSMWithTags.java | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/PortlandCustomNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/PortlandCustomNamer.java index fb0d3f583e0..b178adaab6b 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/PortlandCustomNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/PortlandCustomNamer.java @@ -71,24 +71,21 @@ public I18NString name(OSMWithTags way) { @Override public void recordEdges(OSMWithTags way, StreetEdgePair edgePair) { + final boolean isHighwayLink = way.isHighwayLink(); + final boolean isLowerLink = way.isLowerLink(); edgePair .asIterable() .forEach(edge -> { if (!edge.hasBogusName()) { return; // this edge already has a real name so there is nothing to do } - String highway = way.getTag("highway"); - if ("motorway_link".equals(highway) || "trunk_link".equals(highway)) { + if (isHighwayLink) { if (edge.isBack()) { nameByDestination.add(edge); } else { nameByOrigin.add(edge); } - } else if ( - "secondary_link".equals(highway) || - "primary_link".equals(highway) || - "tertiary_link".equals(highway) - ) { + } else if (isLowerLink) { if (edge.isBack()) { nameByOrigin.add(edge); } else { diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index a8dd169dfe5..19b750106fa 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -236,7 +236,6 @@ private double length(Geometry intersection) { ); }; } - } private record EdgeOnLevel(StreetEdge edge, Set levels) {} diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java index 636584eb770..d0f0dea764e 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java @@ -574,6 +574,20 @@ public boolean isLink() { return highway != null && highway.endsWith(("_link")); } + public boolean isHighwayLink() { + String highway = getTag("highway"); + return "motorway_link".equals(highway) || "trunk_link".equals(highway); + } + + public boolean isLowerLink() { + String highway = getTag("highway"); + return ( + "secondary_link".equals(highway) || + "primary_link".equals(highway) || + "tertiary_link".equals(highway) + ); + } + public boolean isElevator() { return isTag("highway", "elevator"); } From f995865fbe33c64691c5ea91f3f546057facc60b Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 6 May 2024 14:40:36 +0200 Subject: [PATCH 53/85] Improve computation of center point --- .../module/osm/naming/SidewalkNamer.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index 19b750106fa..5245a9e9887 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -96,7 +96,7 @@ public void postprocess() { unnamedSidewalks.size() ); - this.preciseBuffer = new PreciseBuffer(computeCentroid(), BUFFER_METERS); + this.preciseBuffer = new PreciseBuffer(computeEnvelopeCenter(), BUFFER_METERS); final AtomicInteger namesApplied = new AtomicInteger(0); unnamedSidewalks @@ -126,11 +126,12 @@ public void postprocess() { /** * Compute the centroid of all sidewalk edges. */ - private Coordinate computeCentroid() { + private Coordinate computeEnvelopeCenter() { var envelope = new Envelope(); - unnamedSidewalks.forEach(e -> - envelope.expandToInclude(e.edge.getGeometry().getEnvelopeInternal()) - ); + unnamedSidewalks.forEach(e -> { + envelope.expandToInclude(e.edge.getFromVertex().getCoordinate()); + envelope.expandToInclude(e.edge.getToVertex().getCoordinate()); + }); return envelope.centre(); } From e9a0722d9b6c4a6e28a48ca85a799fb4ece45e62 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 6 May 2024 14:51:01 +0200 Subject: [PATCH 54/85] Improve if/else logic --- .../graph_builder/module/osm/naming/SidewalkNamer.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index 5245a9e9887..c1080a66772 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -57,7 +57,6 @@ public class SidewalkNamer implements EdgeNamer { private static final Logger LOG = LoggerFactory.getLogger(SidewalkNamer.class); - private static final int MAX_DISTANCE_TO_SIDEWALK = 50; private static final double MIN_PERCENT_IN_BUFFER = .85; private static final int BUFFER_METERS = 25; @@ -72,14 +71,16 @@ public I18NString name(OSMWithTags way) { @Override public void recordEdges(OSMWithTags way, StreetEdgePair pair) { + // this way is a sidewalk and hasn't been named yet (and is not explicitly unnamed) if (way.isSidewalk() && way.needsFallbackName() && !way.isExplicitlyUnnamed()) { pair .asIterable() .forEach(edge -> unnamedSidewalks.add(new EdgeOnLevel(edge, way.getLevels()))); } - if (way.isNamed() && !way.isLink()) { + // the way is _not_ a sidewalk and does have a name + else if (way.isNamed() && !way.isLink()) { // we generate two edges for each osm way: one there and one back. since we don't do any routing - // in this class we don't need the two directions and keep only one of them. + // in this class we don't need the two directions and index only one of them. var edge = pair.pickAny(); streetEdges.insert( edge.getGeometry().getEnvelopeInternal(), From 8a88c9932f5fdfcd7eb643730e8f18a38dd6f03f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 6 May 2024 14:55:40 +0200 Subject: [PATCH 55/85] Update comment --- .../graph_builder/module/osm/naming/SidewalkNamer.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index c1080a66772..6016e8e1d44 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -79,8 +79,9 @@ public void recordEdges(OSMWithTags way, StreetEdgePair pair) { } // the way is _not_ a sidewalk and does have a name else if (way.isNamed() && !way.isLink()) { - // we generate two edges for each osm way: one there and one back. since we don't do any routing - // in this class we don't need the two directions and index only one of them. + // We generate two edges for each osm way: one there and one back. This spatial index only + // needs to contain one item for each road segment with a unique geometry and name, so we + // add only one of the two edges. var edge = pair.pickAny(); streetEdges.insert( edge.getGeometry().getEnvelopeInternal(), From 8823782eda247a587dd03169e2af18bbd992de00 Mon Sep 17 00:00:00 2001 From: Andrew Byrd Date: Mon, 6 May 2024 21:43:36 +0800 Subject: [PATCH 56/85] copy mailing list information from docs to readme --- README.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/README.md b/README.md index 5e016c22bb2..5e93f3c98b5 100644 --- a/README.md +++ b/README.md @@ -60,16 +60,7 @@ the world. ## Getting in touch -The fastest way to get help is to use our [Gitter chat room](https://gitter.im/opentripplanner/OpenTripPlanner) -where most of the core developers are. You can also send questions and comments to the -[mailing list](http://groups.google.com/group/opentripplanner-users). - -Changes and extensions to OTP are debated in issues on [GitHub](https://github.com/opentripplanner/OpenTripPlanner/issues) -and in the [Gitter chat room](https://gitter.im/opentripplanner/OpenTripPlanner). More general -questions and announcements of interest to non-developer OTP users should be directed to -the [opentripplanner-users](https://groups.google.com/forum/#!forum/opentripplanner-users) list. -Other details of [project governance](http://docs.opentripplanner.org/en/dev-2.x/Governance/) can be -found in the main documentation. +The fastest way to get help is to use our [Gitter chat room](https://gitter.im/opentripplanner/OpenTripPlanner) where most of the core developers are. Bug reports may be filed via the Github [issue tracker](https://github.com/openplans/OpenTripPlanner/issues). The OpenTripPlanner [mailing list](http://groups.google.com/group/opentripplanner-users) is used almost exclusively for project announcements. The mailing list and issue tracker are not intended for support questions or discussions. Please use the chat for this purpose. Other details of [project governance](http://docs.opentripplanner.org/en/dev-2.x/Governance/) can be found in the main documentation. ## OTP Ecosystem From c5ac1698f80acf240682b64b5953e14c7908e81b Mon Sep 17 00:00:00 2001 From: Johan Torin Date: Mon, 6 May 2024 15:46:25 +0200 Subject: [PATCH 57/85] Refactor to set values directly in the mapper and not rely on later logic. * Set NOT_SET early where applicable. * Rework how transferMode is applied but take care to not overwrite the defaults even if it's the same value. --- .../mapping/RequestModesMapper.java | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java b/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java index 469d6868658..133da8e3b76 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java @@ -1,12 +1,16 @@ package org.opentripplanner.apis.transmodel.mapping; import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Predicate; import org.opentripplanner.routing.api.request.RequestModes; import org.opentripplanner.routing.api.request.RequestModesBuilder; import org.opentripplanner.routing.api.request.StreetMode; class RequestModesMapper { + private static final Predicate IS_BIKE = m -> m == StreetMode.BIKE; private static final String accessModeKey = "accessMode"; private static final String egressModeKey = "egressMode"; private static final String directModeKey = "directMode"; @@ -19,17 +23,22 @@ class RequestModesMapper { static RequestModes mapRequestModes(Map modesInput) { RequestModesBuilder mBuilder = RequestModes.of(); - if (modesInput.containsKey(accessModeKey)) { - StreetMode accessMode = (StreetMode) modesInput.get(accessModeKey); - mBuilder.withAccessMode(accessMode); - mBuilder.withTransferMode(accessMode == StreetMode.BIKE ? StreetMode.BIKE : StreetMode.WALK); - } else { - mBuilder.withAccessMode(StreetMode.NOT_SET); - } - // Non-existing values and null are later translated into NOT_SET. - mBuilder.withEgressMode((StreetMode) modesInput.get(egressModeKey)); - mBuilder.withDirectMode((StreetMode) modesInput.get(directModeKey)); + final StreetMode accessMode = (StreetMode) modesInput.get(accessModeKey); + ensureValueAndSet(accessMode, mBuilder::withAccessMode); + ensureValueAndSet((StreetMode) modesInput.get(egressModeKey), mBuilder::withEgressMode); + ensureValueAndSet((StreetMode) modesInput.get(directModeKey), mBuilder::withDirectMode); + Optional.ofNullable(accessMode).filter(IS_BIKE).ifPresent(mBuilder::withTransferMode); return mBuilder.build(); } + + /** + * Use the provided consumer to apply the StreetMode if it's non-null, otherwise apply NOT_SET. + * + * @param streetMode + * @param consumer + */ + private static void ensureValueAndSet(StreetMode streetMode, Consumer consumer) { + Optional.ofNullable(streetMode).or(() -> Optional.of(StreetMode.NOT_SET)).ifPresent(consumer); + } } From b1d36f902aaacd1ca4fbe46120155e119ba793e1 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 6 May 2024 17:22:46 +0200 Subject: [PATCH 58/85] Update accessibility score docs [ci skip] --- docs/sandbox/IBIAccessibilityScore.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/sandbox/IBIAccessibilityScore.md b/docs/sandbox/IBIAccessibilityScore.md index e3c6ef16bbd..5b944eaf73b 100644 --- a/docs/sandbox/IBIAccessibilityScore.md +++ b/docs/sandbox/IBIAccessibilityScore.md @@ -1,14 +1,9 @@ -# IBI Group Accessibility Score - OTP Sandbox Extension +# IBI Group Accessibility Score ## Contact Info - IBI Group ([transitrealtime@ibigroup.com](mailto:transitrealtime@ibigroup.com)) -## Changelog - -- Create initial - implementation [#4221](https://github.com/opentripplanner/OpenTripPlanner/pull/4221) - ## Documentation This extension computes a numeric accessibility score between 0 and 1 and adds it to the itinerary @@ -32,4 +27,8 @@ To enable the feature add the following to `router-config.json`: } ``` -The score is only computed when you search for wheelchair-accessible routes. \ No newline at end of file +The score is only computed when you search for wheelchair-accessible routes. + +## Changelog + +- Create initial implementation [#4221](https://github.com/opentripplanner/OpenTripPlanner/pull/4221) From 90148e089134c2289f1d6310bb50cf1645f0d000 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 6 May 2024 17:41:19 +0200 Subject: [PATCH 59/85] Update docs about experimental fields --- doc-templates/Flex.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc-templates/Flex.md b/doc-templates/Flex.md index 50512f66133..0e79ecdbffc 100644 --- a/doc-templates/Flex.md +++ b/doc-templates/Flex.md @@ -13,6 +13,17 @@ To enable this turn on `FlexRouting` as a feature in `otp-config.json`. The GTFS feeds must conform to the final, approved version of the draft which has been merged into the [mainline specification](https://gtfs.org/schedule/reference/) in March 2024. +### Experimental features + +This sandbox feature also has experimental support for the following fields: + +- `safe_duration_factor` +- `safe_duration_offset` + +These features are currently [undergoing specification](https://github.com/MobilityData/gtfs-flex/pull/79) +and their definition might change. OTP's implementation will be also be changed so be careful +when relying on this feature. + ## Configuration This feature allows a limited number of config options. To change the configuration, add the From a4dc25b7d635ec872e4843d23d7ab1533f662249 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 6 May 2024 17:46:16 +0200 Subject: [PATCH 60/85] Revert renaming --- docs/sandbox/Flex.md | 11 +++++++++++ .../ext/flex/trip/ScheduledDeviatedTrip.java | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/sandbox/Flex.md b/docs/sandbox/Flex.md index 277e4e617f2..7b08fd7d05f 100644 --- a/docs/sandbox/Flex.md +++ b/docs/sandbox/Flex.md @@ -13,6 +13,17 @@ To enable this turn on `FlexRouting` as a feature in `otp-config.json`. The GTFS feeds must conform to the final, approved version of the draft which has been merged into the [mainline specification](https://gtfs.org/schedule/reference/) in March 2024. +### Experimental features + +This sandbox feature also has experimental support for the following fields: + +- `safe_duration_factor` +- `safe_duration_offset` + +These features are currently [undergoing specification](https://github.com/MobilityData/gtfs-flex/pull/79) +and their definition might change. OTP's implementation will be also be changed so be careful +when relying on this feature. + ## Configuration This feature allows a limited number of config options. To change the configuration, add the diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java index 0026d98c964..e16e1e5e1f7 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java @@ -176,7 +176,7 @@ public int earliestDepartureTime(int stopIndex) { @Override public int latestArrivalTime( int arrivalTime, - int ignored, + int fromStopIndex, int toStopIndex, int flexTripDurationSeconds ) { From 7b07cd56482574a42b504b5693de877505ece940 Mon Sep 17 00:00:00 2001 From: Johan Torin Date: Tue, 7 May 2024 09:04:08 +0200 Subject: [PATCH 61/85] Update src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java Replace Optional-chain with a less complicated ternary condition. Co-authored-by: Henrik Abrahamsson <127481124+habrahamsson-skanetrafiken@users.noreply.github.com> --- .../apis/transmodel/mapping/RequestModesMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java b/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java index 133da8e3b76..974b8dd10c3 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java @@ -39,6 +39,6 @@ static RequestModes mapRequestModes(Map modesInput) { * @param consumer */ private static void ensureValueAndSet(StreetMode streetMode, Consumer consumer) { - Optional.ofNullable(streetMode).or(() -> Optional.of(StreetMode.NOT_SET)).ifPresent(consumer); + consumer.accept(streetMode == null ? StreetMode.NOT_SET : streetMode); } } From 1b2c59998228aa458e16b6b000554ee812014313 Mon Sep 17 00:00:00 2001 From: Henrik Abrahamsson Date: Tue, 7 May 2024 10:15:20 +0200 Subject: [PATCH 62/85] Regenerate documentation --- docs/RouterConfiguration.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/RouterConfiguration.md b/docs/RouterConfiguration.md index e948a23dd54..59fa53fc7c9 100644 --- a/docs/RouterConfiguration.md +++ b/docs/RouterConfiguration.md @@ -366,13 +366,15 @@ for more info." Costs for boarding and alighting during transfers at stops with a given transfer priority. -This cost is applied **both to boarding and alighting** at stops during transfers. All stops have a transfer cost priority -set, the default is `allowed`. The `stopBoardAlightDuringTransferCost` parameter is optional, but if listed all -values must be set. +This cost is applied **both to boarding and alighting** at stops during transfers. All stops have a +transfer cost priority set, the default is `allowed`. The `stopBoardAlightDuringTransferCost` +parameter is optional, but if listed all values must be set. -When a transfer occurs at the same stop, the cost will be applied twice since the cost is both for boarding and alighting, +When a transfer occurs at the same stop, the cost will be applied twice since the cost is both for +boarding and alighting, -If not set the `stopBoardAlightDuringTransferCost` is ignored. This is only available for NeTEx imported Stops. +If not set the `stopBoardAlightDuringTransferCost` is ignored. This is only available for NeTEx +imported Stops. The cost is a scalar, but is equivalent to the felt cost of riding a transit trip for 1 second. From 6100a6db094fa9627107a0a3ebdf0b63127763e8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 7 May 2024 11:20:00 +0000 Subject: [PATCH 63/85] chore(deps): update debug ui dependencies (non-major) --- client-next/package-lock.json | 110 ++++++++++++++++++---------------- client-next/package.json | 8 +-- 2 files changed, 63 insertions(+), 55 deletions(-) diff --git a/client-next/package-lock.json b/client-next/package-lock.json index abe2c5d3ed6..83f19bf3c48 100644 --- a/client-next/package-lock.json +++ b/client-next/package-lock.json @@ -12,7 +12,7 @@ "bootstrap": "5.3.3", "graphql": "16.8.1", "graphql-request": "6.1.0", - "maplibre-gl": "4.1.3", + "maplibre-gl": "4.2.0", "react": "18.3.1", "react-bootstrap": "2.10.2", "react-dom": "18.3.1", @@ -23,13 +23,13 @@ "@graphql-codegen/client-preset": "4.2.5", "@graphql-codegen/introspection": "4.0.3", "@parcel/watcher": "2.4.1", - "@testing-library/react": "15.0.6", + "@testing-library/react": "15.0.7", "@types/react": "18.3.1", "@types/react-dom": "18.3.0", "@typescript-eslint/eslint-plugin": "7.8.0", "@typescript-eslint/parser": "7.8.0", "@vitejs/plugin-react": "4.2.1", - "@vitest/coverage-v8": "1.5.3", + "@vitest/coverage-v8": "1.6.0", "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.29.1", @@ -41,7 +41,7 @@ "prettier": "3.2.5", "typescript": "5.4.5", "vite": "5.2.11", - "vitest": "1.5.3" + "vitest": "1.6.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -2737,16 +2737,18 @@ } }, "node_modules/@maplibre/maplibre-gl-style-spec": { - "version": "20.1.1", - "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.1.1.tgz", - "integrity": "sha512-z85ARNPCBI2Cs5cPOS3DSbraTN+ue8zrcYVoSWBuNrD/mA+2SKAJ+hIzI22uN7gac6jBMnCdpPKRxS/V0KSZVQ==", + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.2.0.tgz", + "integrity": "sha512-BTw6/3ysowky22QMtNDjElp+YLwwvBDh3xxnq1izDFjTtUERm5nYSihlNZ6QaxXb+6lX2T2t0hBEjheAI+kBEQ==", "dependencies": { "@mapbox/jsonlint-lines-primitives": "~2.0.2", "@mapbox/unitbezier": "^0.0.1", "json-stringify-pretty-compact": "^4.0.0", "minimist": "^1.2.8", + "quickselect": "^2.0.0", "rw": "^1.3.3", - "sort-object": "^3.0.3" + "sort-object": "^3.0.3", + "tinyqueue": "^2.0.3" }, "bin": { "gl-style-format": "dist/gl-style-format.mjs", @@ -3372,9 +3374,9 @@ } }, "node_modules/@testing-library/react": { - "version": "15.0.6", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-15.0.6.tgz", - "integrity": "sha512-UlbazRtEpQClFOiYp+1BapMT+xyqWMnE+hh9tn5DQ6gmlE7AIZWcGpzZukmDZuFk3By01oiqOf8lRedLS4k6xQ==", + "version": "15.0.7", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-15.0.7.tgz", + "integrity": "sha512-cg0RvEdD1TIhhkm1IeYMQxrzy0MtUNfa3minv4MjbgcYzJAZ7yD0i0lwoPOTPr+INtiXFezt2o8xMSnyHhEn2Q==", "dev": true, "dependencies": { "@babel/runtime": "^7.12.5", @@ -3485,6 +3487,11 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/junit-report-builder": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/junit-report-builder/-/junit-report-builder-3.0.2.tgz", + "integrity": "sha512-R5M+SYhMbwBeQcNXYWNCZkl09vkVfAtcPIaCGdzIkkbeaTrVbGQ7HVgi4s+EmM/M1K4ZuWQH0jGcvMvNePfxYA==" + }, "node_modules/@types/mapbox__point-geometry": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz", @@ -3896,9 +3903,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.5.3.tgz", - "integrity": "sha512-DPyGSu/fPHOJuPxzFSQoT4N/Fu/2aJfZRtEpEp8GI7NHsXBGE94CQ+pbEGBUMFjatsHPDJw/+TAF9r4ens2CNw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.6.0.tgz", + "integrity": "sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.1", @@ -3919,17 +3926,17 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.5.3" + "vitest": "1.6.0" } }, "node_modules/@vitest/expect": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.3.tgz", - "integrity": "sha512-y+waPz31pOFr3rD7vWTbwiLe5+MgsMm40jTZbQE8p8/qXyBX3CQsIXRx9XK12IbY7q/t5a5aM/ckt33b4PxK2g==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", + "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", "dev": true, "dependencies": { - "@vitest/spy": "1.5.3", - "@vitest/utils": "1.5.3", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", "chai": "^4.3.10" }, "funding": { @@ -3937,12 +3944,12 @@ } }, "node_modules/@vitest/runner": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.3.tgz", - "integrity": "sha512-7PlfuReN8692IKQIdCxwir1AOaP5THfNkp0Uc4BKr2na+9lALNit7ub9l3/R7MP8aV61+mHKRGiqEKRIwu6iiQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", + "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", "dev": true, "dependencies": { - "@vitest/utils": "1.5.3", + "@vitest/utils": "1.6.0", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -3978,9 +3985,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.3.tgz", - "integrity": "sha512-K3mvIsjyKYBhNIDujMD2gfQEzddLe51nNOAf45yKRt/QFJcUIeTQd2trRvv6M6oCBHNVnZwFWbQ4yj96ibiDsA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", + "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", "dev": true, "dependencies": { "magic-string": "^0.30.5", @@ -4024,9 +4031,9 @@ "dev": true }, "node_modules/@vitest/spy": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.3.tgz", - "integrity": "sha512-Llj7Jgs6lbnL55WoshJUUacdJfjU2honvGcAJBxhra5TPEzTJH8ZuhI3p/JwqqfnTr4PmP7nDmOXP53MS7GJlg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", + "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", "dev": true, "dependencies": { "tinyspy": "^2.2.0" @@ -4036,9 +4043,9 @@ } }, "node_modules/@vitest/utils": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.3.tgz", - "integrity": "sha512-rE9DTN1BRhzkzqNQO+kw8ZgfeEBCLXiHJwetk668shmNBpSagQxneT5eSqEBLP+cqSiAeecvQmbpFfdMyLcIQA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", + "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", @@ -8339,9 +8346,9 @@ } }, "node_modules/maplibre-gl": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.1.3.tgz", - "integrity": "sha512-nMy5h0kzq9Z66C6AIb3p2BvLIVHz75dGGQow22x+h9/VOihr0IPQI26ylAi6lHqvEy2VqjiRmKAMlFwt0xFKfQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.2.0.tgz", + "integrity": "sha512-x5GgYyKKn5UDvbUZFK7ng3Pq829/uYWDSVN/itZoP2slWSzKbjIXKi/Qhz5FnYiMXwpRgM08UIcVjtn1PLK9Tg==", "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", @@ -8350,9 +8357,10 @@ "@mapbox/unitbezier": "^0.0.1", "@mapbox/vector-tile": "^1.3.1", "@mapbox/whoots-js": "^3.1.0", - "@maplibre/maplibre-gl-style-spec": "^20.1.1", + "@maplibre/maplibre-gl-style-spec": "^20.2.0", "@types/geojson": "^7946.0.14", "@types/geojson-vt": "3.2.5", + "@types/junit-report-builder": "^3.0.2", "@types/mapbox__point-geometry": "^0.1.4", "@types/mapbox__vector-tile": "^1.3.4", "@types/pbf": "^3.0.5", @@ -10903,9 +10911,9 @@ } }, "node_modules/vite-node": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.3.tgz", - "integrity": "sha512-axFo00qiCpU/JLd8N1gu9iEYL3xTbMbMrbe5nDp9GL0nb6gurIdZLkkFogZXWnE8Oyy5kfSLwNVIcVsnhE7lgQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", + "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -10925,16 +10933,16 @@ } }, "node_modules/vitest": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.3.tgz", - "integrity": "sha512-2oM7nLXylw3mQlW6GXnRriw+7YvZFk/YNV8AxIC3Z3MfFbuziLGWP9GPxxu/7nRlXhqyxBikpamr+lEEj1sUEw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", + "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", "dev": true, "dependencies": { - "@vitest/expect": "1.5.3", - "@vitest/runner": "1.5.3", - "@vitest/snapshot": "1.5.3", - "@vitest/spy": "1.5.3", - "@vitest/utils": "1.5.3", + "@vitest/expect": "1.6.0", + "@vitest/runner": "1.6.0", + "@vitest/snapshot": "1.6.0", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -10948,7 +10956,7 @@ "tinybench": "^2.5.1", "tinypool": "^0.8.3", "vite": "^5.0.0", - "vite-node": "1.5.3", + "vite-node": "1.6.0", "why-is-node-running": "^2.2.2" }, "bin": { @@ -10963,8 +10971,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.5.3", - "@vitest/ui": "1.5.3", + "@vitest/browser": "1.6.0", + "@vitest/ui": "1.6.0", "happy-dom": "*", "jsdom": "*" }, diff --git a/client-next/package.json b/client-next/package.json index 1677c6a3b1f..5ff988bdc87 100644 --- a/client-next/package.json +++ b/client-next/package.json @@ -21,7 +21,7 @@ "bootstrap": "5.3.3", "graphql": "16.8.1", "graphql-request": "6.1.0", - "maplibre-gl": "4.1.3", + "maplibre-gl": "4.2.0", "react": "18.3.1", "react-bootstrap": "2.10.2", "react-dom": "18.3.1", @@ -32,13 +32,13 @@ "@graphql-codegen/client-preset": "4.2.5", "@graphql-codegen/introspection": "4.0.3", "@parcel/watcher": "2.4.1", - "@testing-library/react": "15.0.6", + "@testing-library/react": "15.0.7", "@types/react": "18.3.1", "@types/react-dom": "18.3.0", "@typescript-eslint/eslint-plugin": "7.8.0", "@typescript-eslint/parser": "7.8.0", "@vitejs/plugin-react": "4.2.1", - "@vitest/coverage-v8": "1.5.3", + "@vitest/coverage-v8": "1.6.0", "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.29.1", @@ -50,6 +50,6 @@ "prettier": "3.2.5", "typescript": "5.4.5", "vite": "5.2.11", - "vitest": "1.5.3" + "vitest": "1.6.0" } } From 203fcddeb7068e5dd8145c975c03f310ee54fe65 Mon Sep 17 00:00:00 2001 From: Henrik Abrahamsson <127481124+habrahamsson-skanetrafiken@users.noreply.github.com> Date: Tue, 7 May 2024 15:51:16 +0200 Subject: [PATCH 64/85] Apply review suggestion Co-authored-by: Thomas Gran --- .../routing/algorithm/raptoradapter/transit/TransitLayer.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayer.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayer.java index 0afcd97450a..2dde108b97e 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayer.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayer.java @@ -48,9 +48,6 @@ public class TransitLayer { private final TransferIndexGenerator transferIndexGenerator; - /** - * @see #getStopBoardAlightTransferCosts() - */ @Nullable private final int[] stopBoardAlightTransferCosts; From 49436441cf399d12ce5bd051a874bcdee7ab9e30 Mon Sep 17 00:00:00 2001 From: OTP Bot Date: Wed, 8 May 2024 07:11:16 +0000 Subject: [PATCH 65/85] Upgrade debug client to version 2024/05/2024-05-08T07:10 --- src/client/debug-client-preview/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/debug-client-preview/index.html b/src/client/debug-client-preview/index.html index 8968324b94d..974356161cb 100644 --- a/src/client/debug-client-preview/index.html +++ b/src/client/debug-client-preview/index.html @@ -5,8 +5,8 @@ OTP Debug Client - - + +
From 2b2d36505ab952ffb6bcb2ec7c7ac84c4f609361 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 8 May 2024 11:00:25 +0200 Subject: [PATCH 66/85] Clarify hasName/hasNoName/isExplicitlyUnnamed --- .../graph_builder/module/osm/OsmModule.java | 2 +- .../module/osm/WalkableAreaBuilder.java | 4 +-- .../module/osm/naming/SidewalkNamer.java | 2 +- .../openstreetmap/model/OSMWithTags.java | 33 ++++++++++++------- .../openstreetmap/model/OSMWithTagsTest.java | 4 +-- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 79bcacda660..3a745dddf52 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -542,7 +542,7 @@ private StreetEdge getEdgeForStreet( .withSlopeOverride(way.getOsmProvider().getWayPropertySet().getSlopeOverride(way)) .withStairs(way.isSteps()) .withWheelchairAccessible(way.isWheelchairAccessible()) - .withBogusName(way.needsFallbackName()); + .withBogusName(way.hasNoName()); StreetEdge street = seb.buildAndConnect(); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java index 6244a50321f..89b71403232 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/WalkableAreaBuilder.java @@ -532,7 +532,7 @@ private Set createSegments( .withBack(false) .withArea(edgeList) .withCarSpeed(carSpeed) - .withBogusName(areaEntity.needsFallbackName()) + .withBogusName(areaEntity.hasNoName()) .withWheelchairAccessible(areaEntity.isWheelchairAccessible()) .withLink(areaEntity.isLink()); @@ -555,7 +555,7 @@ private Set createSegments( .withBack(true) .withArea(edgeList) .withCarSpeed(carSpeed) - .withBogusName(areaEntity.needsFallbackName()) + .withBogusName(areaEntity.hasNoName()) .withWheelchairAccessible(areaEntity.isWheelchairAccessible()) .withLink(areaEntity.isLink()); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index 6016e8e1d44..196ebe73946 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -72,7 +72,7 @@ public I18NString name(OSMWithTags way) { @Override public void recordEdges(OSMWithTags way, StreetEdgePair pair) { // this way is a sidewalk and hasn't been named yet (and is not explicitly unnamed) - if (way.isSidewalk() && way.needsFallbackName() && !way.isExplicitlyUnnamed()) { + if (way.isSidewalk() && way.hasNoName() && !way.isExplicitlyUnnamed()) { pair .asIterable() .forEach(edge -> unnamedSidewalks.add(new EdgeOnLevel(edge, way.getLevels()))); diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java index d0f0dea764e..b4c9c4b01b5 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java @@ -168,13 +168,6 @@ public boolean isSidewalk() { return isTag("footway", "sidewalk") && isTag("highway", "footway"); } - /** - * Whether this entity explicity doesn't have a name. - */ - public boolean isExplicitlyUnnamed() { - return isTagTrue("noname"); - } - protected boolean doesTagAllowAccess(String tag) { if (tags == null) { return false; @@ -602,17 +595,33 @@ public boolean isWheelchairAccessible() { } /** - * Does this entity has a name of its own or if it needs to have a fallback one assigned? + * Does this entity have tags that allow extracting a name? */ - public boolean needsFallbackName() { + public boolean isNamed() { + return hasTag("name") || hasTag("ref"); + } + + /** + * Is this entity unnamed? + *

+ * Perhaps this entity has a name that isn't in the source data, but it's also possible that + * it's explicitly tagged as not having one. + * + * @see OSMWithTags#isExplicitlyUnnamed() + */ + public boolean hasNoName() { return !isNamed(); } /** - * Does this entity have tags that allow extracting a name? + * Whether this entity explicitly doesn't have a name. This is different to no name being + * set on the entity in OSM. + * + * @see OSMWithTags#isNamed() + * @see https://wiki.openstreetmap.org/wiki/Tag:noname%3Dyes */ - public boolean isNamed() { - return hasTag("name") || hasTag("ref"); + public boolean isExplicitlyUnnamed() { + return isTagTrue("noname"); } /** diff --git a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java index 3c060150714..3b50d0bff0d 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java @@ -266,9 +266,9 @@ void testGenerateI18NForPattern() { @Test void fallbackName() { var nameless = WayTestData.cycleway(); - assertTrue(nameless.needsFallbackName()); + assertTrue(nameless.hasNoName()); var namedTunnel = WayTestData.carTunnel(); - assertFalse(namedTunnel.needsFallbackName()); + assertFalse(namedTunnel.hasNoName()); } } From ea679755a77beb05536b5c163f174141f62fbd72 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 8 May 2024 13:02:43 +0200 Subject: [PATCH 67/85] Remove extra collection conversion --- src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index 76a13255b67..5347e7504c5 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -33,8 +33,7 @@ public class FlexTripsMapper { ProgressTracker progress = ProgressTracker.track("Create flex trips", 500, tripSize); for (Trip trip : stopTimesByTrip.keys()) { - /* Fetch the stop times for this trip. Copy the list since it's immutable. */ - List stopTimes = new ArrayList<>(stopTimesByTrip.get(trip)); + var stopTimes = stopTimesByTrip.get(trip); if (UnscheduledTrip.isUnscheduledTrip(stopTimes)) { var modifier = builder.getFlexDurationFactors().getOrDefault(trip, TimePenalty.ZERO); result.add( From 9128aa6b873407284c4f844028d60d9631e300da Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 8 May 2024 11:07:17 +0000 Subject: [PATCH 68/85] fix(deps): update dependency org.onebusaway:onebusaway-gtfs to v1.4.17 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ce89fc467e6..2c537332022 100644 --- a/pom.xml +++ b/pom.xml @@ -817,7 +817,7 @@ org.onebusaway onebusaway-gtfs - 1.4.15 + 1.4.17 org.slf4j From 695161a86c82dbbeed34140417d071df55823191 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 8 May 2024 13:25:53 +0200 Subject: [PATCH 69/85] Rename modifiers/factors to time penalty --- ...rTest.java => TimePenaltyCalculatorTest.java} | 6 +++--- .../trip/UnscheduledDrivingDurationTest.java | 6 +++--- .../ext/flex/FlexTripsMapper.java | 4 ++-- ...alculator.java => TimePenaltyCalculator.java} | 4 ++-- .../ext/flex/trip/UnscheduledTrip.java | 16 ++++++++++------ .../ext/flex/trip/UnscheduledTripBuilder.java | 10 +++++----- .../mapping/GTFSToOtpTransitServiceMapper.java | 2 +- .../model/impl/OtpTransitServiceBuilder.java | 2 +- .../api/request/framework/TimePenalty.java | 8 ++++++++ .../api/request/framework/TimePenaltyTest.java | 7 +++++++ 10 files changed, 42 insertions(+), 23 deletions(-) rename src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/{DurationModifierCalculatorTest.java => TimePenaltyCalculatorTest.java} (88%) rename src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/{DurationModifierCalculator.java => TimePenaltyCalculator.java} (83%) diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/TimePenaltyCalculatorTest.java similarity index 88% rename from src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java rename to src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/TimePenaltyCalculatorTest.java index a97460dda75..e504f8ac762 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculatorTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/flexpathcalculator/TimePenaltyCalculatorTest.java @@ -9,7 +9,7 @@ import org.opentripplanner.routing.api.request.framework.TimePenalty; import org.opentripplanner.street.model._data.StreetModelForTest; -class DurationModifierCalculatorTest { +class TimePenaltyCalculatorTest { private static final int THIRTY_MINS_IN_SECONDS = (int) Duration.ofMinutes(30).toSeconds(); @@ -19,7 +19,7 @@ void calculate() { new FlexPath(10_000, THIRTY_MINS_IN_SECONDS, () -> LineStrings.SIMPLE); var mod = TimePenalty.of(Duration.ofMinutes(10), 1.5f); - var calc = new DurationModifierCalculator(delegate, mod); + var calc = new TimePenaltyCalculator(delegate, mod); var path = calc.calculateFlexPath(StreetModelForTest.V1, StreetModelForTest.V2, 0, 5); assertEquals(3300, path.durationSeconds); } @@ -28,7 +28,7 @@ void calculate() { void nullValue() { FlexPathCalculator delegate = (fromv, tov, fromStopIndex, toStopIndex) -> null; var mod = TimePenalty.of(Duration.ofMinutes(10), 1.5f); - var calc = new DurationModifierCalculator(delegate, mod); + var calc = new TimePenaltyCalculator(delegate, mod); var path = calc.calculateFlexPath(StreetModelForTest.V1, StreetModelForTest.V2, 0, 5); assertNull(path); } diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java index bd06a51960b..a4b245b7de8 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledDrivingDurationTest.java @@ -22,7 +22,7 @@ class UnscheduledDrivingDurationTest { private static final StopTime STOP_TIME = FlexStopTimesForTest.area("10:00", "18:00"); @Test - void noModifier() { + void noPenalty() { var trip = UnscheduledTrip.of(id("1")).withStopTimes(List.of(STOP_TIME)).build(); var calculator = trip.flexPathCalculator(STATIC_CALCULATOR); @@ -31,11 +31,11 @@ void noModifier() { } @Test - void withModifier() { + void withPenalty() { var trip = UnscheduledTrip .of(id("1")) .withStopTimes(List.of(STOP_TIME)) - .withDurationModifier(TimePenalty.of(Duration.ofMinutes(2), 1.5f)) + .withTimePenalty(TimePenalty.of(Duration.ofMinutes(2), 1.5f)) .build(); var calculator = trip.flexPathCalculator(STATIC_CALCULATOR); diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java index 5347e7504c5..c4167f2f9e1 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexTripsMapper.java @@ -35,13 +35,13 @@ public class FlexTripsMapper { for (Trip trip : stopTimesByTrip.keys()) { var stopTimes = stopTimesByTrip.get(trip); if (UnscheduledTrip.isUnscheduledTrip(stopTimes)) { - var modifier = builder.getFlexDurationFactors().getOrDefault(trip, TimePenalty.ZERO); + var timePenalty = builder.getFlexTimePenalty().getOrDefault(trip, TimePenalty.NONE); result.add( UnscheduledTrip .of(trip.getId()) .withTrip(trip) .withStopTimes(stopTimes) - .withDurationModifier(modifier) + .withTimePenalty(timePenalty) .build() ); } else if (ScheduledDeviatedTrip.isScheduledFlexTrip(stopTimes)) { diff --git a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/TimePenaltyCalculator.java similarity index 83% rename from src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java rename to src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/TimePenaltyCalculator.java index 0f521b55b1f..63b661f0f9a 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/DurationModifierCalculator.java +++ b/src/ext/java/org/opentripplanner/ext/flex/flexpathcalculator/TimePenaltyCalculator.java @@ -8,12 +8,12 @@ * A calculator to delegates the main computation to another instance and applies a duration * modifier afterward. */ -public class DurationModifierCalculator implements FlexPathCalculator { +public class TimePenaltyCalculator implements FlexPathCalculator { private final FlexPathCalculator delegate; private final TimePenalty factors; - public DurationModifierCalculator(FlexPathCalculator delegate, TimePenalty penalty) { + public TimePenaltyCalculator(FlexPathCalculator delegate, TimePenalty penalty) { this.delegate = delegate; this.factors = penalty; } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index 7fa31c78114..402d39e2aa7 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -15,11 +15,13 @@ import java.util.stream.Stream; import javax.annotation.Nonnull; import org.opentripplanner.ext.flex.FlexServiceDate; -import org.opentripplanner.ext.flex.flexpathcalculator.DurationModifierCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; +import org.opentripplanner.ext.flex.flexpathcalculator.TimePenaltyCalculator; import org.opentripplanner.ext.flex.template.FlexAccessTemplate; import org.opentripplanner.ext.flex.template.FlexEgressTemplate; +import org.opentripplanner.framework.lang.DoubleUtils; import org.opentripplanner.framework.lang.IntRange; +import org.opentripplanner.framework.time.DurationUtils; import org.opentripplanner.model.BookingInfo; import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.StopTime; @@ -53,7 +55,7 @@ public class UnscheduledTrip extends FlexTrip getFlexAccessTemplates( } /** - * Get the correct {@link FlexPathCalculator} depending on the {@code durationModifier}. + * Get the correct {@link FlexPathCalculator} depending on the {@code timePenalty}. * If the modifier doesn't actually modify, we return the regular calculator. */ protected FlexPathCalculator flexPathCalculator(FlexPathCalculator calculator) { - if (!durationModifier.isZero()) { - return new DurationModifierCalculator(calculator, durationModifier); + if (timePenalty.modifies()) { + return new TimePenaltyCalculator(calculator, timePenalty); } else { return calculator; } diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java index cfffebe4fa7..1f8585f5ad0 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTripBuilder.java @@ -9,7 +9,7 @@ public class UnscheduledTripBuilder extends FlexTripBuilder { private List stopTimes; - private TimePenalty durationModifier = TimePenalty.ZERO; + private TimePenalty timePenalty = TimePenalty.NONE; UnscheduledTripBuilder(FeedScopedId id) { super(id); @@ -31,13 +31,13 @@ public List stopTimes() { return stopTimes; } - public UnscheduledTripBuilder withDurationModifier(TimePenalty factors) { - this.durationModifier = factors; + public UnscheduledTripBuilder withTimePenalty(TimePenalty factors) { + this.timePenalty = factors; return this; } - public TimePenalty durationModifier() { - return durationModifier; + public TimePenalty timePenalty() { + return timePenalty; } @Override diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java index 24db367f829..ce65d6b0820 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java @@ -171,7 +171,7 @@ public void mapStopTripAndRouteDataIntoBuilder() { builder.getPathways().addAll(pathwayMapper.map(data.getAllPathways())); builder.getStopTimesSortedByTrip().addAll(stopTimeMapper.map(data.getAllStopTimes())); - builder.getFlexDurationFactors().putAll(tripMapper.flexSafeDurationModifiers()); + builder.getFlexTimePenalty().putAll(tripMapper.flexSafeDurationModifiers()); builder.getTripsById().addAll(tripMapper.map(data.getAllTrips())); fareRulesBuilder.fareAttributes().addAll(fareAttributeMapper.map(data.getAllFareAttributes())); diff --git a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java index 756a1ccf2a8..544ca29599d 100644 --- a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java +++ b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java @@ -213,7 +213,7 @@ public TripStopTimes getStopTimesSortedByTrip() { return stopTimesByTrip; } - public Map getFlexDurationFactors() { + public Map getFlexTimePenalty() { return flexDurationFactors; } diff --git a/src/main/java/org/opentripplanner/routing/api/request/framework/TimePenalty.java b/src/main/java/org/opentripplanner/routing/api/request/framework/TimePenalty.java index aaa81db5a09..0c6fdd96436 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/framework/TimePenalty.java +++ b/src/main/java/org/opentripplanner/routing/api/request/framework/TimePenalty.java @@ -7,6 +7,7 @@ public final class TimePenalty extends AbstractLinearFunction { public static final TimePenalty ZERO = new TimePenalty(Duration.ZERO, 0.0); + public static final TimePenalty NONE = new TimePenalty(Duration.ZERO, 1.0); private TimePenalty(Duration constant, double coefficient) { super(DurationUtils.requireNonNegative(constant), coefficient); @@ -31,6 +32,13 @@ protected boolean isZero(Duration value) { return value.isZero(); } + /** + * Does this penalty actually modify a duration or would it be returned unchanged? + */ + public boolean modifies() { + return !constant().isZero() && coefficient() != 1.0; + } + @Override protected Duration constantAsDuration() { return constant(); diff --git a/src/test/java/org/opentripplanner/routing/api/request/framework/TimePenaltyTest.java b/src/test/java/org/opentripplanner/routing/api/request/framework/TimePenaltyTest.java index f5bffe1f415..087ffa1d637 100644 --- a/src/test/java/org/opentripplanner/routing/api/request/framework/TimePenaltyTest.java +++ b/src/test/java/org/opentripplanner/routing/api/request/framework/TimePenaltyTest.java @@ -64,4 +64,11 @@ void calculate() { var subject = TimePenalty.of(D2m, 0.5); assertEquals(120 + 150, subject.calculate(Duration.ofMinutes(5)).toSeconds()); } + + @Test + void modifies() { + var subject = TimePenalty.of(D2m, 0.5); + assertTrue(subject.modifies()); + assertFalse(TimePenalty.ZERO.modifies()); + } } From 13c47176aa97f4e3e184e60c017d7d724509e424 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Thu, 9 May 2024 23:05:04 +0200 Subject: [PATCH 70/85] Fix spelling [ci skip] --- docs/Version-Comparison.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Version-Comparison.md b/docs/Version-Comparison.md index 48257bdbd54..bc4a7caf5d8 100644 --- a/docs/Version-Comparison.md +++ b/docs/Version-Comparison.md @@ -92,7 +92,7 @@ applications. See the [Analysis](Analysis.md) page for more details. ### Routers API and Hot Reloading -Via it's Routers API, OTP1 allows loading data and serving APIs for multiple separate geographic +Via its Routers API, OTP1 allows loading data and serving APIs for multiple separate geographic areas. This is functionally equivalent to running more than one OTP server with separate data sets. This system also allows reloading transportation network data when it changes, or even pushing new data over a network connection. From fdf2bf297f85d5f7d804d00f1d372a16e4abced7 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Fri, 10 May 2024 10:03:30 +0000 Subject: [PATCH 71/85] Add changelog entry for #5821 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index cafc677ccc6..c4b90705ae7 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -16,6 +16,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Fix trip duplication in Graph Builder DSJ mapping [#5794](https://github.com/opentripplanner/OpenTripPlanner/pull/5794) - Optionally abort startup when unknown configuration parameters were detected [#5676](https://github.com/opentripplanner/OpenTripPlanner/pull/5676) - Fix bug in heuristics cost calculation for egress legs [#5783](https://github.com/opentripplanner/OpenTripPlanner/pull/5783) +- Fix handling of implicit access and egress mode parameters. [#5821](https://github.com/opentripplanner/OpenTripPlanner/pull/5821) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.5.0 (2024-03-13) From a9b98b9349ddcffb8e53941dc60f9708b5e66866 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 13 May 2024 14:17:24 +0200 Subject: [PATCH 72/85] Mark banned stops as deprected --- .../resources/org/opentripplanner/apis/gtfs/schema.graphqls | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 56f3528d6e3..0f863393d87 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -1082,7 +1082,7 @@ input InputBanned { banned for boarding and disembarking vehicles — it is possible to get an itinerary where a vehicle stops at one of these stops """ - stops: String + stops: String @deprecated(reason: "Not implemented in OTP2.") """ A comma-separated list of banned stop ids. Only itineraries where these stops @@ -1090,7 +1090,7 @@ input InputBanned { these stops, that route will not be used in the itinerary, even if the stop is not used for boarding or disembarking the vehicle. """ - stopsHard: String + stopsHard: String @deprecated(reason: "Not implemented in OTP2.") } input InputCoordinates { From dbd8f2f1053c89ca183b5fa37e12612882394a70 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Mon, 13 May 2024 17:45:56 +0200 Subject: [PATCH 73/85] Remove separate section about OSM naming --- doc-templates/BuildConfiguration.md | 16 ---------------- docs/BuildConfiguration.md | 16 ---------------- 2 files changed, 32 deletions(-) diff --git a/doc-templates/BuildConfiguration.md b/doc-templates/BuildConfiguration.md index 756593a57a3..43a29e1fb79 100644 --- a/doc-templates/BuildConfiguration.md +++ b/doc-templates/BuildConfiguration.md @@ -140,22 +140,6 @@ The mechanism is that for any two identical tags, OTP will use the first one. } ``` -### Custom naming - -You can define a custom naming scheme for elements drawn from OSM by defining an `osmNaming` field -in `build-config.json`, such as: - -```JSON -// build-config.json -{ - "osmNaming": "portland" -} -``` - -There is currently only one custom naming module called `portland` (which has no parameters). - - - ## Elevation data OpenTripPlanner can "drape" the OSM street network over a digital elevation model (DEM). This allows diff --git a/docs/BuildConfiguration.md b/docs/BuildConfiguration.md index fe3a2109ba4..d2e7833c251 100644 --- a/docs/BuildConfiguration.md +++ b/docs/BuildConfiguration.md @@ -238,22 +238,6 @@ The mechanism is that for any two identical tags, OTP will use the first one. } ``` -### Custom naming - -You can define a custom naming scheme for elements drawn from OSM by defining an `osmNaming` field -in `build-config.json`, such as: - -```JSON -// build-config.json -{ - "osmNaming": "portland" -} -``` - -There is currently only one custom naming module called `portland` (which has no parameters). - - - ## Elevation data OpenTripPlanner can "drape" the OSM street network over a digital elevation model (DEM). This allows From 07aab67bb6c45445bb5f71875b0ed55dd7dbd3b9 Mon Sep 17 00:00:00 2001 From: Henrik Abrahamsson Date: Tue, 14 May 2024 10:44:18 +0200 Subject: [PATCH 74/85] Don't expose mutable tripOnServiceDates in TransitModel --- .../org/opentripplanner/netex/NetexModule.java | 2 +- .../transit/service/TransitModel.java | 5 +++-- .../ScheduledTransitLegReferenceTest.java | 18 ++++++++---------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/opentripplanner/netex/NetexModule.java b/src/main/java/org/opentripplanner/netex/NetexModule.java index 08904c947e4..b9a05d25b10 100644 --- a/src/main/java/org/opentripplanner/netex/NetexModule.java +++ b/src/main/java/org/opentripplanner/netex/NetexModule.java @@ -69,7 +69,7 @@ public void buildGraph() { ); transitBuilder.limitServiceDays(transitPeriodLimit); for (var tripOnServiceDate : transitBuilder.getTripOnServiceDates().values()) { - transitModel.getTripOnServiceDates().put(tripOnServiceDate.getId(), tripOnServiceDate); + transitModel.addTripOnServiceDate(tripOnServiceDate.getId(), tripOnServiceDate); } calendarServiceData.add(transitBuilder.buildCalendarServiceData()); diff --git a/src/main/java/org/opentripplanner/transit/service/TransitModel.java b/src/main/java/org/opentripplanner/transit/service/TransitModel.java index 2f0162aa8b1..46e22a937be 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitModel.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitModel.java @@ -371,8 +371,9 @@ public TripPattern getTripPatternForId(FeedScopedId id) { return tripPatternForId.get(id); } - public Map getTripOnServiceDates() { - return tripOnServiceDates; + public void addTripOnServiceDate(FeedScopedId id, TripOnServiceDate tripOnServiceDate) { + invalidateIndex(); + tripOnServiceDates.put(id, tripOnServiceDate); } /** diff --git a/src/test/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReferenceTest.java b/src/test/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReferenceTest.java index a3fab1838cb..480639e5bda 100644 --- a/src/test/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReferenceTest.java +++ b/src/test/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReferenceTest.java @@ -87,16 +87,14 @@ static void buildTransitService() { calendarServiceData.putServiceDatesForServiceId(tripPattern.getId(), List.of(SERVICE_DATE)); transitModel.updateCalendarServiceData(true, calendarServiceData, DataImportIssueStore.NOOP); - transitModel - .getTripOnServiceDates() - .put( - TRIP_ON_SERVICE_DATE_ID, - TripOnServiceDate - .of(TRIP_ON_SERVICE_DATE_ID) - .withTrip(trip) - .withServiceDate(SERVICE_DATE) - .build() - ); + transitModel.addTripOnServiceDate( + TRIP_ON_SERVICE_DATE_ID, + TripOnServiceDate + .of(TRIP_ON_SERVICE_DATE_ID) + .withTrip(trip) + .withServiceDate(SERVICE_DATE) + .build() + ); transitModel.index(); From 60c4ee925f51689bacd596f517ea709984b36615 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 14 May 2024 11:52:48 +0200 Subject: [PATCH 75/85] Incorporate review feedback --- .../graph_builder/module/osm/OsmModule.java | 4 +--- .../module/osm/naming/PortlandCustomNamer.java | 18 ++++++++++++++++-- .../module/osm/naming/SidewalkNamer.java | 8 ++++---- .../openstreetmap/model/OSMWithTags.java | 14 -------------- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 3a745dddf52..10c215ee448 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -544,9 +544,7 @@ private StreetEdge getEdgeForStreet( .withWheelchairAccessible(way.isWheelchairAccessible()) .withBogusName(way.hasNoName()); - StreetEdge street = seb.buildAndConnect(); - - return street; + return seb.buildAndConnect(); } private float getMaxCarSpeed() { diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/PortlandCustomNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/PortlandCustomNamer.java index b178adaab6b..c0ca595fbd7 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/PortlandCustomNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/PortlandCustomNamer.java @@ -71,8 +71,8 @@ public I18NString name(OSMWithTags way) { @Override public void recordEdges(OSMWithTags way, StreetEdgePair edgePair) { - final boolean isHighwayLink = way.isHighwayLink(); - final boolean isLowerLink = way.isLowerLink(); + final boolean isHighwayLink = isHighwayLink(way); + final boolean isLowerLink = isLowerLink(way); edgePair .asIterable() .forEach(edge -> { @@ -185,4 +185,18 @@ private static String nameAccordingToOrigin(StreetEdge e, int maxDepth) { } return null; } + + private static boolean isHighwayLink(OSMWithTags way) { + String highway = way.getTag("highway"); + return "motorway_link".equals(highway) || "trunk_link".equals(highway); + } + + private static boolean isLowerLink(OSMWithTags way) { + String highway = way.getTag("highway"); + return ( + "secondary_link".equals(highway) || + "primary_link".equals(highway) || + "tertiary_link".equals(highway) + ); + } } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java index 196ebe73946..eafccc0da1b 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/naming/SidewalkNamer.java @@ -71,13 +71,13 @@ public I18NString name(OSMWithTags way) { @Override public void recordEdges(OSMWithTags way, StreetEdgePair pair) { - // this way is a sidewalk and hasn't been named yet (and is not explicitly unnamed) + // This way is a sidewalk and hasn't been named yet (and is not explicitly unnamed) if (way.isSidewalk() && way.hasNoName() && !way.isExplicitlyUnnamed()) { pair .asIterable() .forEach(edge -> unnamedSidewalks.add(new EdgeOnLevel(edge, way.getLevels()))); } - // the way is _not_ a sidewalk and does have a name + // The way is _not_ a sidewalk and does have a name else if (way.isNamed() && !way.isLink()) { // We generate two edges for each osm way: one there and one back. This spatial index only // needs to contain one item for each road segment with a unique geometry and name, so we @@ -120,7 +120,7 @@ public void postprocess() { LOG.info(progress.completeMessage()); - // set the indices to null so they can be garbage-collected + // Set the indices to null so they can be garbage-collected streetEdges = null; unnamedSidewalks = null; } @@ -148,7 +148,7 @@ private void assignNameToSidewalk(EdgeOnLevel sidewalkOnLevel, AtomicInteger nam var candidates = streetEdges.query(buffer.getEnvelopeInternal()); groupEdgesByName(candidates) - // make sure we only compare sidewalks and streets that are on the same level + // Make sure we only compare sidewalks and streets that are on the same level .filter(g -> g.levels.equals(sidewalkOnLevel.levels)) .map(g -> computePercentInsideBuffer(g, buffer, sidewalkLength)) // Remove those groups where less than a certain percentage is inside the buffer around diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java index b4c9c4b01b5..b53739bf6a1 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java @@ -567,20 +567,6 @@ public boolean isLink() { return highway != null && highway.endsWith(("_link")); } - public boolean isHighwayLink() { - String highway = getTag("highway"); - return "motorway_link".equals(highway) || "trunk_link".equals(highway); - } - - public boolean isLowerLink() { - String highway = getTag("highway"); - return ( - "secondary_link".equals(highway) || - "primary_link".equals(highway) || - "tertiary_link".equals(highway) - ); - } - public boolean isElevator() { return isTag("highway", "elevator"); } From fed0a5fc3534d009834559bef279425643f7f7fc Mon Sep 17 00:00:00 2001 From: Andrew Byrd Date: Tue, 14 May 2024 19:49:49 +0800 Subject: [PATCH 76/85] add prettier:write to docs, rephrase slightly (#5831) [ci skip] --- docs/Codestyle.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/Codestyle.md b/docs/Codestyle.md index d127ff21e5c..d468937adfa 100644 --- a/docs/Codestyle.md +++ b/docs/Codestyle.md @@ -14,14 +14,21 @@ it takes a bit of time, but reformat the entire codebase. Only code you have cha formatted, since the existing code is already formatted. The second way is to set up prettier and run it manually or hick it into your IDE, so it runs every time a file is changed. -### How to run Prittier with Maven +### How to run Prettier with Maven -Format all code is done in the validate phase (run before test, package, install) +Prettier will automatically format all code in the Maven "validate" phase, which runs before the test, package, and install phases. So formatting will happen for example when you run: ``` % mvn test ``` +You can manually run _only_ the formatting process with: + +``` +% mvn prettier:write + +``` + To skip the prettier formating use profile `prettierSkip`: ``` @@ -31,7 +38,7 @@ To skip the prettier formating use profile `prettierSkip`: The check for formatting errors use profile `prettierCheck`: ``` -% mvn test -P prettierSkip +% mvn test -P prettierCheck ``` The check is run by the CI server and will fail the build if the code is incorrectly formatted. From a4a5d01ae103cb0d76b70c41cc6b6fe6ec966cee Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Tue, 14 May 2024 11:50:03 +0000 Subject: [PATCH 77/85] Add changelog entry for #5831 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index c4b90705ae7..6943759a1ae 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -17,6 +17,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Optionally abort startup when unknown configuration parameters were detected [#5676](https://github.com/opentripplanner/OpenTripPlanner/pull/5676) - Fix bug in heuristics cost calculation for egress legs [#5783](https://github.com/opentripplanner/OpenTripPlanner/pull/5783) - Fix handling of implicit access and egress mode parameters. [#5821](https://github.com/opentripplanner/OpenTripPlanner/pull/5821) +- Add prettier:write to docs and rephrase slightly [ci skip] [#5831](https://github.com/opentripplanner/OpenTripPlanner/pull/5831) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.5.0 (2024-03-13) From 6329621caf63afd11b53405ddc303413c6ead5e4 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Tue, 14 May 2024 13:57:48 +0000 Subject: [PATCH 78/85] Add changelog entry for #5822 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index 6943759a1ae..872ee7a96bb 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -18,6 +18,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Fix bug in heuristics cost calculation for egress legs [#5783](https://github.com/opentripplanner/OpenTripPlanner/pull/5783) - Fix handling of implicit access and egress mode parameters. [#5821](https://github.com/opentripplanner/OpenTripPlanner/pull/5821) - Add prettier:write to docs and rephrase slightly [ci skip] [#5831](https://github.com/opentripplanner/OpenTripPlanner/pull/5831) +- Make naming of stopTransferCosts/stopBoardAlightCosts consistent [#5822](https://github.com/opentripplanner/OpenTripPlanner/pull/5822) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.5.0 (2024-03-13) From aff398abbbc126e0c7b96059c3e4fe7fddd690ec Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Tue, 14 May 2024 22:29:44 +0200 Subject: [PATCH 79/85] Update link in magidoc documentation --- magidoc.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/magidoc.mjs b/magidoc.mjs index a57b17a4308..4fea5e4e127 100644 --- a/magidoc.mjs +++ b/magidoc.mjs @@ -29,7 +29,7 @@ at http://localhost:8080/graphiql This API is activated by default. To learn how to deactivate it, read the -[documentation](https://docs.opentripplanner.org/en/dev-2.x/apis/GTFS-GraphQ-API/). +[documentation](https://docs.opentripplanner.org/en/dev-2.x/apis/GTFS-GraphQL-API/). `, }], appTitle: 'OTP GTFS GraphQL API', From 835b4091af527ee8e0f5d8a718258a2eebe48c50 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 15 May 2024 01:19:19 +0000 Subject: [PATCH 80/85] chore(deps): update dependency org.mockito:mockito-core to v5.12.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 79dd2c45d50..60570418622 100644 --- a/pom.xml +++ b/pom.xml @@ -699,7 +699,7 @@ org.mockito mockito-core - 5.11.0 + 5.12.0 test From 62822005e2ed22733dcdcba199a8cb3695fa1fc0 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 15 May 2024 11:45:25 +0200 Subject: [PATCH 81/85] Remove non-standard trip fare ID --- .../ext/restapi/mapping/TripMapper.java | 1 - .../org/opentripplanner/gtfs/mapping/TripMapper.java | 1 - .../opentripplanner/transit/model/timetable/Trip.java | 11 +---------- .../transit/model/timetable/TripBuilder.java | 11 ----------- .../opentripplanner/gtfs/mapping/TripMapperTest.java | 4 ---- .../transit/model/timetable/TripTest.java | 4 ---- 6 files changed, 1 insertion(+), 31 deletions(-) diff --git a/src/ext/java/org/opentripplanner/ext/restapi/mapping/TripMapper.java b/src/ext/java/org/opentripplanner/ext/restapi/mapping/TripMapper.java index f4894a4a7a3..1f7abd3897a 100644 --- a/src/ext/java/org/opentripplanner/ext/restapi/mapping/TripMapper.java +++ b/src/ext/java/org/opentripplanner/ext/restapi/mapping/TripMapper.java @@ -31,7 +31,6 @@ public static ApiTrip mapToApi(Trip obj) { api.shapeId = FeedScopedIdMapper.mapToApi(obj.getShapeId()); api.wheelchairAccessible = WheelchairAccessibilityMapper.mapToApi(obj.getWheelchairBoarding()); api.bikesAllowed = BikeAccessMapper.mapToApi(obj.getBikesAllowed()); - api.fareId = obj.getGtfsFareId(); return api; } diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java index a80ae035ed1..d7a4dfd403e 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java @@ -60,7 +60,6 @@ private Trip doMap(org.onebusaway.gtfs.model.Trip rhs) { lhs.withShapeId(AgencyAndIdMapper.mapAgencyAndId(rhs.getShapeId())); lhs.withWheelchairBoarding(WheelchairAccessibilityMapper.map(rhs.getWheelchairAccessible())); lhs.withBikesAllowed(BikeAccessMapper.mapForTrip(rhs)); - lhs.withGtfsFareId(rhs.getFareId()); return lhs.build(); } diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/Trip.java b/src/main/java/org/opentripplanner/transit/model/timetable/Trip.java index 9e6795e5140..5a1e9150e78 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/Trip.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/Trip.java @@ -36,7 +36,6 @@ public final class Trip extends AbstractTransitEntity impleme private final Accessibility wheelchairBoarding; private final String gtfsBlockId; - private final String gtfsFareId; private final String netexInternalPlanningCode; private final TripAlteration netexAlteration; @@ -65,7 +64,6 @@ public final class Trip extends AbstractTransitEntity impleme this.headsign = builder.getHeadsign(); this.shapeId = builder.getShapeId(); this.gtfsBlockId = builder.getGtfsBlockId(); - this.gtfsFareId = builder.getGtfsFareId(); this.netexInternalPlanningCode = builder.getNetexInternalPlanningCode(); } @@ -149,12 +147,6 @@ public String getGtfsBlockId() { return gtfsBlockId; } - /** Custom extension for KCM to specify a fare per-trip */ - @Nullable - public String getGtfsFareId() { - return gtfsFareId; - } - /** * Internal code (non-public identifier) for the journey (e.g. train- or trip number from the * planners' tool). This is kept to ensure compatibility with legacy planning systems. In NeTEx @@ -209,8 +201,7 @@ public boolean sameAs(@Nonnull Trip other) { Objects.equals(this.direction, other.direction) && Objects.equals(this.bikesAllowed, other.bikesAllowed) && Objects.equals(this.wheelchairBoarding, other.wheelchairBoarding) && - Objects.equals(this.netexAlteration, other.netexAlteration) && - Objects.equals(this.gtfsFareId, other.gtfsFareId) + Objects.equals(this.netexAlteration, other.netexAlteration) ); } diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/TripBuilder.java b/src/main/java/org/opentripplanner/transit/model/timetable/TripBuilder.java index fd51b9035c7..063dfe10da2 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/TripBuilder.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/TripBuilder.java @@ -23,7 +23,6 @@ public class TripBuilder extends AbstractEntityBuilder { private BikeAccess bikesAllowed; private Accessibility wheelchairBoarding; private String gtfsBlockId; - private String gtfsFareId; private String netexInternalPlanningCode; private TripAlteration netexAlteration; @@ -47,7 +46,6 @@ public class TripBuilder extends AbstractEntityBuilder { this.bikesAllowed = original.getBikesAllowed(); this.wheelchairBoarding = original.getWheelchairBoarding(); this.netexInternalPlanningCode = original.getNetexInternalPlanningCode(); - this.gtfsFareId = original.getGtfsFareId(); } public Operator getOperator() { @@ -176,15 +174,6 @@ public TripBuilder withNetexAlteration(TripAlteration netexAlteration) { return this; } - public String getGtfsFareId() { - return gtfsFareId; - } - - public TripBuilder withGtfsFareId(String gtfsFareId) { - this.gtfsFareId = gtfsFareId; - return this; - } - @Override protected Trip buildFromValues() { return new Trip(this); diff --git a/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java b/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java index 4f0c70f22d2..12c513b28d9 100644 --- a/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java +++ b/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java @@ -23,7 +23,6 @@ public class TripMapperTest { private static final int BIKES_ALLOWED = 1; private static final String BLOCK_ID = "Block Id"; private static final int DIRECTION_ID = 1; - private static final String FARE_ID = "Fare Id"; private static final String TRIP_HEADSIGN = "Trip Headsign"; private static final String TRIP_SHORT_NAME = "Trip Short Name"; @@ -46,7 +45,6 @@ public class TripMapperTest { TRIP.setBikesAllowed(BIKES_ALLOWED); TRIP.setBlockId(BLOCK_ID); TRIP.setDirectionId(Integer.toString(DIRECTION_ID)); - TRIP.setFareId(FARE_ID); TRIP.setRoute(data.route); TRIP.setServiceId(AGENCY_AND_ID); TRIP.setShapeId(AGENCY_AND_ID); @@ -69,7 +67,6 @@ public void testMap() throws Exception { assertEquals("A:1", result.getId().toString()); assertEquals(BLOCK_ID, result.getGtfsBlockId()); assertEquals(Direction.INBOUND, result.getDirection()); - assertEquals(FARE_ID, result.getGtfsFareId()); assertNotNull(result.getRoute()); assertEquals("A:1", result.getServiceId().toString()); assertEquals("A:1", result.getShapeId().toString()); @@ -91,7 +88,6 @@ public void testMapWithNulls() throws Exception { assertNotNull(result.getRoute()); assertNull(result.getGtfsBlockId()); - assertNull(result.getGtfsFareId()); assertNull(result.getServiceId()); assertNull(result.getShapeId()); assertNull(result.getHeadsign()); diff --git a/src/test/java/org/opentripplanner/transit/model/timetable/TripTest.java b/src/test/java/org/opentripplanner/transit/model/timetable/TripTest.java index 098c1d0994a..3a3f35643fa 100644 --- a/src/test/java/org/opentripplanner/transit/model/timetable/TripTest.java +++ b/src/test/java/org/opentripplanner/transit/model/timetable/TripTest.java @@ -26,7 +26,6 @@ class TripTest { private static final BikeAccess BIKE_ACCESS = BikeAccess.ALLOWED; private static final TransitMode TRANSIT_MODE = TransitMode.BUS; private static final String BLOCK_ID = "blockId"; - private static final String FARE_ID = "fareId"; private static final TripAlteration TRIP_ALTERATION = TripAlteration.CANCELLATION; private static final String NETEX_SUBMODE_NAME = "submode"; private static final SubMode NETEX_SUBMODE = SubMode.of(NETEX_SUBMODE_NAME); @@ -46,7 +45,6 @@ class TripTest { .withBikesAllowed(BIKE_ACCESS) .withMode(TRANSIT_MODE) .withGtfsBlockId(BLOCK_ID) - .withGtfsFareId(FARE_ID) .withNetexAlteration(TRIP_ALTERATION) .withNetexSubmode(NETEX_SUBMODE_NAME) .withNetexInternalPlanningCode(NETEX_INTERNAL_PLANNING_CODE) @@ -88,7 +86,6 @@ void copy() { assertEquals(BIKE_ACCESS, copy.getBikesAllowed()); assertEquals(TRANSIT_MODE, copy.getMode()); assertEquals(BLOCK_ID, copy.getGtfsBlockId()); - assertEquals(FARE_ID, copy.getGtfsFareId()); assertEquals(TRIP_ALTERATION, copy.getNetexAlteration()); assertEquals(NETEX_SUBMODE, copy.getNetexSubMode()); assertEquals(NETEX_INTERNAL_PLANNING_CODE, copy.getNetexInternalPlanningCode()); @@ -115,7 +112,6 @@ void sameAs() { assertFalse(subject.sameAs(subject.copy().withBikesAllowed(BikeAccess.NOT_ALLOWED).build())); assertFalse(subject.sameAs(subject.copy().withMode(TransitMode.RAIL).build())); assertFalse(subject.sameAs(subject.copy().withGtfsBlockId("X").build())); - assertFalse(subject.sameAs(subject.copy().withGtfsFareId("X").build())); assertFalse( subject.sameAs(subject.copy().withNetexAlteration(TripAlteration.REPLACED).build()) ); From 6197cada88f6fa3e39780a60e6e558d1dbedd02f Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 15 May 2024 15:20:33 +0200 Subject: [PATCH 82/85] Fix grammar [ci skip] --- doc-templates/sandbox/siri/SiriAzureUpdater.md | 5 +++-- docs/sandbox/siri/SiriAzureUpdater.md | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/doc-templates/sandbox/siri/SiriAzureUpdater.md b/doc-templates/sandbox/siri/SiriAzureUpdater.md index eb092ddd08d..85e22e30bda 100644 --- a/doc-templates/sandbox/siri/SiriAzureUpdater.md +++ b/doc-templates/sandbox/siri/SiriAzureUpdater.md @@ -1,7 +1,8 @@ # Siri Azure Updater -It is sandbox extension developed by Skånetrafiken that allows OTP to fetch Siri ET & SX messages through *Azure Service Bus*. -IT also OTP to download historical data from en HTTP endpoint on startup. +This is a sandbox extension developed by Skånetrafiken that allows OTP to fetch Siri ET & SX messages +through *Azure Service Bus*. +It also enables OTP to download historical data from en HTTP endpoint on startup. ## Contact Info diff --git a/docs/sandbox/siri/SiriAzureUpdater.md b/docs/sandbox/siri/SiriAzureUpdater.md index c6ddf9f3ebe..7b29e802f21 100644 --- a/docs/sandbox/siri/SiriAzureUpdater.md +++ b/docs/sandbox/siri/SiriAzureUpdater.md @@ -1,7 +1,8 @@ # Siri Azure Updater -It is sandbox extension developed by Skånetrafiken that allows OTP to fetch Siri ET & SX messages through *Azure Service Bus*. -IT also OTP to download historical data from en HTTP endpoint on startup. +This is a sandbox extension developed by Skånetrafiken that allows OTP to fetch Siri ET & SX messages +through *Azure Service Bus*. +It also enables OTP to download historical data from en HTTP endpoint on startup. ## Contact Info From e0c163c1c937b08e163daeb324ce8bda010b6328 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Fri, 17 May 2024 07:50:04 +0000 Subject: [PATCH 83/85] Add changelog entry for #5774 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index 872ee7a96bb..0b7c70dac2b 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -19,6 +19,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Fix handling of implicit access and egress mode parameters. [#5821](https://github.com/opentripplanner/OpenTripPlanner/pull/5821) - Add prettier:write to docs and rephrase slightly [ci skip] [#5831](https://github.com/opentripplanner/OpenTripPlanner/pull/5831) - Make naming of stopTransferCosts/stopBoardAlightCosts consistent [#5822](https://github.com/opentripplanner/OpenTripPlanner/pull/5822) +- Namer for applying street names to nearby sidewalks [#5774](https://github.com/opentripplanner/OpenTripPlanner/pull/5774) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.5.0 (2024-03-13) From a3d0659688b8ac71aae638ed4bbec630b4846ee8 Mon Sep 17 00:00:00 2001 From: OTP Changelog Bot Date: Fri, 17 May 2024 10:47:55 +0000 Subject: [PATCH 84/85] Add changelog entry for #5796 [ci skip] --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Changelog.md b/docs/Changelog.md index 0b7c70dac2b..55b76823b79 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -20,6 +20,7 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Add prettier:write to docs and rephrase slightly [ci skip] [#5831](https://github.com/opentripplanner/OpenTripPlanner/pull/5831) - Make naming of stopTransferCosts/stopBoardAlightCosts consistent [#5822](https://github.com/opentripplanner/OpenTripPlanner/pull/5822) - Namer for applying street names to nearby sidewalks [#5774](https://github.com/opentripplanner/OpenTripPlanner/pull/5774) +- Implement GTFS Flex safe duration spec draft [#5796](https://github.com/opentripplanner/OpenTripPlanner/pull/5796) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.5.0 (2024-03-13) From ef02c9084c2ce85e8c32b3c15140837242b7e741 Mon Sep 17 00:00:00 2001 From: OTP Serialization Version Bot Date: Fri, 17 May 2024 10:48:20 +0000 Subject: [PATCH 85/85] Bump serialization version id for #5796 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 60570418622..cb8f45f5a16 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ - 149 + 150 31.0 2.51.1