diff --git a/application/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java b/application/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java
index 0b071b64728..744a8209702 100644
--- a/application/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java
+++ b/application/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java
@@ -17,7 +17,9 @@
import org.opentripplanner.graph_builder.issue.api.DataImportIssueSummary;
import org.opentripplanner.graph_builder.model.GraphBuilderModule;
import org.opentripplanner.graph_builder.module.configure.DaggerGraphBuilderFactory;
+import org.opentripplanner.graph_builder.module.configure.GraphBuilderFactory;
import org.opentripplanner.routing.graph.Graph;
+import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository;
import org.opentripplanner.service.vehicleparking.VehicleParkingRepository;
import org.opentripplanner.service.worldenvelope.WorldEnvelopeRepository;
import org.opentripplanner.standalone.config.BuildConfig;
@@ -62,6 +64,7 @@ public static GraphBuilder create(
BuildConfig config,
GraphBuilderDataSources dataSources,
Graph graph,
+ OsmInfoGraphBuildRepository osmInfoGraphBuildRepository,
TimetableRepository timetableRepository,
WorldEnvelopeRepository worldEnvelopeRepository,
VehicleParkingRepository vehicleParkingService,
@@ -78,10 +81,11 @@ public static GraphBuilder create(
timetableRepository.initTimeZone(config.transitModelTimeZone);
- var builder = DaggerGraphBuilderFactory
- .builder()
+ GraphBuilderFactory.Builder builder = DaggerGraphBuilderFactory.builder();
+ builder
.config(config)
.graph(graph)
+ .osmInfoGraphBuildRepository(osmInfoGraphBuildRepository)
.timetableRepository(timetableRepository)
.worldEnvelopeRepository(worldEnvelopeRepository)
.vehicleParkingRepository(vehicleParkingService)
diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java b/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java
index c4acabefd6c..25e33b9c057 100644
--- a/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java
+++ b/application/src/main/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModule.java
@@ -1,18 +1,27 @@
package org.opentripplanner.graph_builder.module;
import jakarta.inject.Inject;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Set;
import java.util.stream.Collectors;
+import javax.annotation.Nullable;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Envelope;
+import org.locationtech.jts.geom.Point;
import org.opentripplanner.framework.geometry.GeometryUtils;
import org.opentripplanner.framework.geometry.SphericalDistanceLibrary;
+import org.opentripplanner.framework.i18n.I18NString;
import org.opentripplanner.framework.i18n.LocalizedString;
import org.opentripplanner.graph_builder.model.GraphBuilderModule;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.graph.index.StreetIndex;
import org.opentripplanner.routing.linking.LinkingDirection;
import org.opentripplanner.routing.linking.VertexLinker;
+import org.opentripplanner.service.osminfo.OsmInfoGraphBuildService;
+import org.opentripplanner.service.osminfo.model.Platform;
import org.opentripplanner.street.model.StreetTraversalPermission;
import org.opentripplanner.street.model.edge.AreaEdge;
import org.opentripplanner.street.model.edge.BoardingLocationToStopLink;
@@ -24,9 +33,12 @@
import org.opentripplanner.street.model.vertex.OsmBoardingLocationVertex;
import org.opentripplanner.street.model.vertex.StreetVertex;
import org.opentripplanner.street.model.vertex.TransitStopVertex;
+import org.opentripplanner.street.model.vertex.Vertex;
import org.opentripplanner.street.model.vertex.VertexFactory;
import org.opentripplanner.street.search.TraverseMode;
import org.opentripplanner.street.search.TraverseModeSet;
+import org.opentripplanner.transit.model.site.RegularStop;
+import org.opentripplanner.transit.model.site.StationElement;
import org.opentripplanner.transit.service.TimetableRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -55,14 +67,20 @@ public class OsmBoardingLocationsModule implements GraphBuilderModule {
private final Graph graph;
+ private final OsmInfoGraphBuildService osmInfoGraphBuildService;
private final TimetableRepository timetableRepository;
private final VertexFactory vertexFactory;
private VertexLinker linker;
@Inject
- public OsmBoardingLocationsModule(Graph graph, TimetableRepository timetableRepository) {
+ public OsmBoardingLocationsModule(
+ Graph graph,
+ OsmInfoGraphBuildService osmInfoGraphBuildService,
+ TimetableRepository timetableRepository
+ ) {
this.graph = graph;
+ this.osmInfoGraphBuildService = osmInfoGraphBuildService;
this.timetableRepository = timetableRepository;
this.vertexFactory = new VertexFactory(graph);
}
@@ -99,54 +117,32 @@ public void buildGraph() {
}
private boolean connectVertexToStop(TransitStopVertex ts, StreetIndex index) {
- var stopCode = ts.getStop().getCode();
- var stopId = ts.getStop().getId().getId();
+ if (connectVertexToNode(ts, index)) return true;
+
+ if (connectVertexToWay(ts, index)) return true;
+
+ return connectVertexToArea(ts, index);
+ }
+
+ private Envelope getEnvelope(TransitStopVertex ts) {
Envelope envelope = new Envelope(ts.getCoordinate());
double xscale = Math.cos(ts.getCoordinate().y * Math.PI / 180);
envelope.expandBy(searchRadiusDegrees / xscale, searchRadiusDegrees);
+ return envelope;
+ }
- // if the boarding location is an OSM node it's generated in the OSM processing step but we need
- // link it here
- var nearbyBoardingLocations = index
- .getVerticesForEnvelope(envelope)
- .stream()
- .filter(OsmBoardingLocationVertex.class::isInstance)
- .map(OsmBoardingLocationVertex.class::cast)
- .collect(Collectors.toSet());
-
- for (var boardingLocation : nearbyBoardingLocations) {
- if (
- (stopCode != null && boardingLocation.references.contains(stopCode)) ||
- boardingLocation.references.contains(stopId)
- ) {
- if (!boardingLocation.isConnectedToStreetNetwork()) {
- linker.linkVertexPermanently(
- boardingLocation,
- new TraverseModeSet(TraverseMode.WALK),
- LinkingDirection.BOTH_WAYS,
- (osmBoardingLocationVertex, splitVertex) -> {
- if (osmBoardingLocationVertex == splitVertex) {
- return List.of();
- }
- // the OSM boarding location vertex is not connected to the street network, so we
- // need to link it first
- return List.of(
- linkBoardingLocationToStreetNetwork(boardingLocation, splitVertex),
- linkBoardingLocationToStreetNetwork(splitVertex, boardingLocation)
- );
- }
- );
- }
- linkBoardingLocationToStop(ts, stopCode, boardingLocation);
- return true;
- }
- }
-
- // if the boarding location is an OSM way (an area) then we are generating the vertex here and
- // use the AreaEdgeList to link it to the correct vertices of the platform edge
- var nearbyEdgeLists = index
- .getEdgesForEnvelope(envelope)
+ /**
+ * Connect a transit stop vertex into a boarding location area in the index.
+ *
+ * A centroid vertex is generated in the area and connected to the vertices on the platform edge.
+ *
+ * @return if the vertex has been connected
+ */
+ private boolean connectVertexToArea(TransitStopVertex ts, StreetIndex index) {
+ RegularStop stop = ts.getStop();
+ var nearbyAreaEdgeList = index
+ .getEdgesForEnvelope(getEnvelope(ts))
.stream()
.filter(AreaEdge.class::isInstance)
.map(AreaEdge.class::cast)
@@ -155,33 +151,141 @@ private boolean connectVertexToStop(TransitStopVertex ts, StreetIndex index) {
// Iterate over all nearby areas representing transit stops in OSM, linking to them if they have a stop code or id
// in their ref= tag that matches the GTFS stop code of this StopVertex.
- for (var edgeList : nearbyEdgeLists) {
- if (
- (stopCode != null && edgeList.references.contains(stopCode)) ||
- edgeList.references.contains(stopId)
- ) {
+ for (var edgeList : nearbyAreaEdgeList) {
+ if (matchesReference(stop, edgeList.references)) {
var name = edgeList
.getAreas()
.stream()
.findFirst()
.map(NamedArea::getName)
.orElse(LOCALIZED_PLATFORM_NAME);
- var label = "platform-centroid/%s".formatted(ts.getStop().getId().toString());
- var centroid = edgeList.getGeometry().getCentroid();
- var boardingLocation = vertexFactory.osmBoardingLocation(
- new Coordinate(centroid.getX(), centroid.getY()),
- label,
+ var boardingLocation = makeBoardingLocation(
+ stop,
+ edgeList.getGeometry().getCentroid(),
edgeList.references,
name
);
linker.addPermanentAreaVertex(boardingLocation, edgeList);
- linkBoardingLocationToStop(ts, stopCode, boardingLocation);
+ linkBoardingLocationToStop(ts, stop.getCode(), boardingLocation);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Connect a transit stop vertex to a boarding location way in the index.
+ *
+ * The vertex is connected to the center of the way if one is found, splitting it if needed.
+ *
+ * @return if the vertex has been connected
+ */
+ private boolean connectVertexToWay(TransitStopVertex ts, StreetIndex index) {
+ var stop = ts.getStop();
+ var nearbyEdges = new HashMap>();
+
+ for (var edge : index.getEdgesForEnvelope(getEnvelope(ts))) {
+ osmInfoGraphBuildService
+ .findPlatform(edge)
+ .ifPresent(platform -> {
+ if (matchesReference(stop, platform.references())) {
+ if (!nearbyEdges.containsKey(platform)) {
+ var list = new ArrayList();
+ list.add(edge);
+ nearbyEdges.put(platform, list);
+ } else {
+ nearbyEdges.get(platform).add(edge);
+ }
+ }
+ });
+ }
+
+ for (var platformEdgeList : nearbyEdges.entrySet()) {
+ Platform platform = platformEdgeList.getKey();
+ var name = platform.name();
+ var boardingLocation = makeBoardingLocation(
+ stop,
+ platform.geometry().getCentroid(),
+ platform.references(),
+ name
+ );
+ for (var vertex : linker.linkToSpecificStreetEdgesPermanently(
+ boardingLocation,
+ new TraverseModeSet(TraverseMode.WALK),
+ LinkingDirection.BOTH_WAYS,
+ platformEdgeList.getValue().stream().map(StreetEdge.class::cast).collect(Collectors.toSet())
+ )) {
+ linkBoardingLocationToStop(ts, stop.getCode(), vertex);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Connect a transit stop vertex to a boarding location node.
+ *
+ * The node is generated in the OSM processing step but we need to link it here.
+ *
+ * @return If the vertex has been connected.
+ */
+ private boolean connectVertexToNode(TransitStopVertex ts, StreetIndex index) {
+ var nearbyBoardingLocations = index
+ .getVerticesForEnvelope(getEnvelope(ts))
+ .stream()
+ .filter(OsmBoardingLocationVertex.class::isInstance)
+ .map(OsmBoardingLocationVertex.class::cast)
+ .collect(Collectors.toSet());
+
+ for (var boardingLocation : nearbyBoardingLocations) {
+ if (matchesReference(ts.getStop(), boardingLocation.references)) {
+ if (!boardingLocation.isConnectedToStreetNetwork()) {
+ linker.linkVertexPermanently(
+ boardingLocation,
+ new TraverseModeSet(TraverseMode.WALK),
+ LinkingDirection.BOTH_WAYS,
+ (osmBoardingLocationVertex, splitVertex) ->
+ getConnectingEdges(boardingLocation, osmBoardingLocationVertex, splitVertex)
+ );
+ }
+ linkBoardingLocationToStop(ts, ts.getStop().getCode(), boardingLocation);
return true;
}
}
return false;
}
+ private OsmBoardingLocationVertex makeBoardingLocation(
+ RegularStop stop,
+ Point centroid,
+ Set refs,
+ I18NString name
+ ) {
+ var label = "platform-centroid/%s".formatted(stop.getId().toString());
+ return vertexFactory.osmBoardingLocation(
+ new Coordinate(centroid.getX(), centroid.getY()),
+ label,
+ refs,
+ name
+ );
+ }
+
+ private List getConnectingEdges(
+ OsmBoardingLocationVertex boardingLocation,
+ Vertex osmBoardingLocationVertex,
+ StreetVertex splitVertex
+ ) {
+ if (osmBoardingLocationVertex == splitVertex) {
+ return List.of();
+ }
+ // the OSM boarding location vertex is not connected to the street network, so we
+ // need to link it first
+ return List.of(
+ linkBoardingLocationToStreetNetwork(boardingLocation, splitVertex),
+ linkBoardingLocationToStreetNetwork(splitVertex, boardingLocation)
+ );
+ }
+
private StreetEdge linkBoardingLocationToStreetNetwork(StreetVertex from, StreetVertex to) {
var line = GeometryUtils.makeLineString(List.of(from.getCoordinate(), to.getCoordinate()));
return new StreetEdgeBuilder<>()
@@ -197,8 +301,8 @@ private StreetEdge linkBoardingLocationToStreetNetwork(StreetVertex from, Street
private void linkBoardingLocationToStop(
TransitStopVertex ts,
- String stopCode,
- OsmBoardingLocationVertex boardingLocation
+ @Nullable String stopCode,
+ StreetVertex boardingLocation
) {
BoardingLocationToStopLink.createBoardingLocationToStopLink(ts, boardingLocation);
BoardingLocationToStopLink.createBoardingLocationToStopLink(boardingLocation, ts);
@@ -210,4 +314,11 @@ private void linkBoardingLocationToStop(
boardingLocation.getCoordinate()
);
}
+
+ private boolean matchesReference(StationElement, ?> stop, Collection references) {
+ var stopCode = stop.getCode();
+ var stopId = stop.getId().getId();
+
+ return (stopCode != null && references.contains(stopCode)) || references.contains(stopId);
+ }
}
diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderFactory.java b/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderFactory.java
index d4d00fdc2a0..4155ecf9614 100644
--- a/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderFactory.java
+++ b/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderFactory.java
@@ -30,6 +30,8 @@
import org.opentripplanner.gtfs.graphbuilder.GtfsModule;
import org.opentripplanner.netex.NetexModule;
import org.opentripplanner.routing.graph.Graph;
+import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository;
+import org.opentripplanner.service.osminfo.configure.OsmInfoGraphBuildServiceModule;
import org.opentripplanner.service.vehicleparking.VehicleParkingRepository;
import org.opentripplanner.service.worldenvelope.WorldEnvelopeRepository;
import org.opentripplanner.standalone.config.BuildConfig;
@@ -37,7 +39,7 @@
import org.opentripplanner.transit.service.TimetableRepository;
@Singleton
-@Component(modules = { GraphBuilderModules.class })
+@Component(modules = { GraphBuilderModules.class, OsmInfoGraphBuildServiceModule.class })
public interface GraphBuilderFactory {
//DataImportIssueStore issueStore();
GraphBuilder graphBuilder();
@@ -80,6 +82,9 @@ interface Builder {
@BindsInstance
Builder timetableRepository(TimetableRepository timetableRepository);
+ @BindsInstance
+ Builder osmInfoGraphBuildRepository(OsmInfoGraphBuildRepository osmInfoGraphBuildRepository);
+
@BindsInstance
Builder worldEnvelopeRepository(WorldEnvelopeRepository worldEnvelopeRepository);
diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java b/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java
index 5371142d612..46c74b52a2d 100644
--- a/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java
+++ b/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java
@@ -43,13 +43,14 @@
import org.opentripplanner.osm.OsmProvider;
import org.opentripplanner.routing.api.request.preference.WalkPreferences;
import org.opentripplanner.routing.graph.Graph;
+import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository;
import org.opentripplanner.service.vehicleparking.VehicleParkingRepository;
import org.opentripplanner.standalone.config.BuildConfig;
import org.opentripplanner.street.model.StreetLimitationParameters;
import org.opentripplanner.transit.service.TimetableRepository;
/**
- * Configure all modules which is not simple enough to be injected.
+ * Configure all modules that are not simple enough to be injected.
*/
@Module
public class GraphBuilderModules {
@@ -60,7 +61,8 @@ static OsmModule provideOsmModule(
GraphBuilderDataSources dataSources,
BuildConfig config,
Graph graph,
- VehicleParkingRepository parkingService,
+ OsmInfoGraphBuildRepository osmInfoGraphBuildRepository,
+ VehicleParkingRepository vehicleParkingRepository,
DataImportIssueStore issueStore,
StreetLimitationParameters streetLimitationParameters
) {
@@ -78,7 +80,7 @@ static OsmModule provideOsmModule(
}
return OsmModule
- .of(providers, graph, parkingService)
+ .of(providers, graph, osmInfoGraphBuildRepository, vehicleParkingRepository)
.withEdgeNamer(config.edgeNamer)
.withAreaVisibility(config.areaVisibility)
.withPlatformEntriesLinking(config.platformEntriesLinking)
diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java
index db495905041..195d36b9ed1 100644
--- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java
+++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java
@@ -8,6 +8,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.LineString;
@@ -25,6 +26,8 @@
import org.opentripplanner.osm.wayproperty.WayProperties;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.util.ElevationUtils;
+import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository;
+import org.opentripplanner.service.osminfo.model.Platform;
import org.opentripplanner.service.vehicleparking.VehicleParkingRepository;
import org.opentripplanner.service.vehicleparking.model.VehicleParking;
import org.opentripplanner.street.model.StreetLimitationParameters;
@@ -52,7 +55,9 @@ public class OsmModule implements GraphBuilderModule {
*/
private final List providers;
private final Graph graph;
+ private final OsmInfoGraphBuildRepository osmInfoGraphBuildRepository;
private final VehicleParkingRepository parkingRepository;
+
private final DataImportIssueStore issueStore;
private final OsmProcessingParameters params;
private final SafetyValueNormalizer normalizer;
@@ -63,36 +68,45 @@ public class OsmModule implements GraphBuilderModule {
OsmModule(
Collection providers,
Graph graph,
- VehicleParkingRepository parkingService,
+ OsmInfoGraphBuildRepository osmInfoGraphBuildRepository,
+ VehicleParkingRepository parkingRepository,
DataImportIssueStore issueStore,
StreetLimitationParameters streetLimitationParameters,
OsmProcessingParameters params
) {
this.providers = List.copyOf(providers);
this.graph = graph;
+ this.osmInfoGraphBuildRepository = osmInfoGraphBuildRepository;
+ this.parkingRepository = parkingRepository;
this.issueStore = issueStore;
this.params = params;
this.osmdb = new OsmDatabase(issueStore);
this.vertexGenerator = new VertexGenerator(osmdb, graph, params.boardingAreaRefTags());
this.normalizer = new SafetyValueNormalizer(graph, issueStore);
this.streetLimitationParameters = Objects.requireNonNull(streetLimitationParameters);
- this.parkingRepository = parkingService;
}
public static OsmModuleBuilder of(
Collection providers,
Graph graph,
- VehicleParkingRepository service
+ OsmInfoGraphBuildRepository osmInfoGraphBuildRepository,
+ VehicleParkingRepository vehicleParkingRepository
) {
- return new OsmModuleBuilder(providers, graph, service);
+ return new OsmModuleBuilder(
+ providers,
+ graph,
+ osmInfoGraphBuildRepository,
+ vehicleParkingRepository
+ );
}
public static OsmModuleBuilder of(
OsmProvider provider,
Graph graph,
- VehicleParkingRepository service
+ OsmInfoGraphBuildRepository osmInfoGraphBuildRepository,
+ VehicleParkingRepository vehicleParkingRepository
) {
- return of(List.of(provider), graph, service);
+ return of(List.of(provider), graph, osmInfoGraphBuildRepository, vehicleParkingRepository);
}
@Override
@@ -319,6 +333,8 @@ private void buildBasicGraph() {
// where the current edge should start
OsmNode osmStartNode = null;
+ var platform = getPlatform(way);
+
for (int i = 0; i < nodes.size() - 1; i++) {
OsmNode segmentStartOsmNode = osmdb.getNode(nodes.get(i));
@@ -341,7 +357,7 @@ private void buildBasicGraph() {
* We split segments at intersections, self-intersections, nodes with ele tags, and transit stops;
* the only processing we do on other nodes is to accumulate their geometry
*/
- if (segmentCoordinates.size() == 0) {
+ if (segmentCoordinates.isEmpty()) {
segmentCoordinates.add(osmStartNode.getCoordinate());
}
@@ -408,6 +424,11 @@ private void buildBasicGraph() {
StreetEdge backStreet = streets.back();
normalizer.applyWayProperties(street, backStreet, wayData, way);
+ platform.ifPresent(plat -> {
+ osmInfoGraphBuildRepository.addPlatform(street, plat);
+ osmInfoGraphBuildRepository.addPlatform(backStreet, plat);
+ });
+
applyEdgesToTurnRestrictions(way, startNode, endNode, street, backStreet);
startNode = endNode;
osmStartNode = osmdb.getNode(startNode);
@@ -422,6 +443,33 @@ private void buildBasicGraph() {
LOG.info(progress.completeMessage());
}
+ private Optional getPlatform(OsmWay way) {
+ if (way.isBoardingLocation()) {
+ var nodeRefs = way.getNodeRefs();
+ var size = nodeRefs.size();
+ var nodes = new Coordinate[size];
+ for (int i = 0; i < size; i++) {
+ nodes[i] = osmdb.getNode(nodeRefs.get(i)).getCoordinate();
+ }
+
+ var geometryFactory = GeometryUtils.getGeometryFactory();
+
+ var geometry = geometryFactory.createLineString(nodes);
+
+ var references = way.getMultiTagValues(params.boardingAreaRefTags());
+
+ return Optional.of(
+ new Platform(
+ params.edgeNamer().getNameForWay(way, "platform " + way.getId()),
+ geometry,
+ references
+ )
+ );
+ } else {
+ return Optional.empty();
+ }
+ }
+
private void validateBarriers() {
List vertices = graph.getVerticesOfType(BarrierVertex.class);
vertices.forEach(bv -> bv.makeBarrierAtEndReachable());
diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModuleBuilder.java b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModuleBuilder.java
index 2f7f4c506c9..f7038a40c74 100644
--- a/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModuleBuilder.java
+++ b/application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModuleBuilder.java
@@ -8,6 +8,7 @@
import org.opentripplanner.graph_builder.services.osm.EdgeNamer;
import org.opentripplanner.osm.OsmProvider;
import org.opentripplanner.routing.graph.Graph;
+import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository;
import org.opentripplanner.service.vehicleparking.VehicleParkingRepository;
import org.opentripplanner.street.model.StreetLimitationParameters;
@@ -19,6 +20,7 @@ public class OsmModuleBuilder {
private final Collection providers;
private final Graph graph;
private final VehicleParkingRepository parkingRepository;
+ private final OsmInfoGraphBuildRepository osmInfoGraphBuildRepository;
private Set boardingAreaRefTags = Set.of();
private DataImportIssueStore issueStore = DataImportIssueStore.NOOP;
private EdgeNamer edgeNamer = new DefaultNamer();
@@ -32,10 +34,12 @@ public class OsmModuleBuilder {
OsmModuleBuilder(
Collection providers,
Graph graph,
+ OsmInfoGraphBuildRepository osmInfoGraphBuildRepository,
VehicleParkingRepository parkingRepository
) {
this.providers = providers;
this.graph = graph;
+ this.osmInfoGraphBuildRepository = osmInfoGraphBuildRepository;
this.parkingRepository = parkingRepository;
}
@@ -88,6 +92,7 @@ public OsmModule build() {
return new OsmModule(
providers,
graph,
+ osmInfoGraphBuildRepository,
parkingRepository,
issueStore,
streetLimitationParameters,
diff --git a/application/src/main/java/org/opentripplanner/routing/graph/SerializedGraphObject.java b/application/src/main/java/org/opentripplanner/routing/graph/SerializedGraphObject.java
index a152b96682d..8565952e557 100644
--- a/application/src/main/java/org/opentripplanner/routing/graph/SerializedGraphObject.java
+++ b/application/src/main/java/org/opentripplanner/routing/graph/SerializedGraphObject.java
@@ -25,6 +25,7 @@
import org.opentripplanner.model.projectinfo.GraphFileHeader;
import org.opentripplanner.model.projectinfo.OtpProjectInfo;
import org.opentripplanner.routing.graph.kryosupport.KryoBuilder;
+import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository;
import org.opentripplanner.service.vehicleparking.VehicleParkingRepository;
import org.opentripplanner.service.worldenvelope.WorldEnvelopeRepository;
import org.opentripplanner.standalone.config.BuildConfig;
@@ -56,6 +57,10 @@ public class SerializedGraphObject implements Serializable {
private static final Logger LOG = LoggerFactory.getLogger(SerializedGraphObject.class);
public final Graph graph;
+
+ @Nullable
+ public final OsmInfoGraphBuildRepository osmInfoGraphBuildRepository;
+
public final TimetableRepository timetableRepository;
public final WorldEnvelopeRepository worldEnvelopeRepository;
private final Collection edges;
@@ -84,6 +89,7 @@ public class SerializedGraphObject implements Serializable {
public SerializedGraphObject(
Graph graph,
+ @Nullable OsmInfoGraphBuildRepository osmInfoGraphBuildRepository,
TimetableRepository timetableRepository,
WorldEnvelopeRepository worldEnvelopeRepository,
VehicleParkingRepository parkingRepository,
@@ -96,6 +102,7 @@ public SerializedGraphObject(
) {
this.graph = graph;
this.edges = graph.getEdges();
+ this.osmInfoGraphBuildRepository = osmInfoGraphBuildRepository;
this.timetableRepository = timetableRepository;
this.worldEnvelopeRepository = worldEnvelopeRepository;
this.parkingRepository = parkingRepository;
diff --git a/application/src/main/java/org/opentripplanner/routing/linking/VertexLinker.java b/application/src/main/java/org/opentripplanner/routing/linking/VertexLinker.java
index 48f5ff997c8..2f09d618ffd 100644
--- a/application/src/main/java/org/opentripplanner/routing/linking/VertexLinker.java
+++ b/application/src/main/java/org/opentripplanner/routing/linking/VertexLinker.java
@@ -7,6 +7,7 @@
import java.util.Set;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
+import javax.annotation.Nullable;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
@@ -226,20 +227,43 @@ private DisposableEdgeCollection link(
return tempEdges;
}
+ /**
+ * Link a boarding location vertex to specific street edges.
+ *
+ * This is used if a platform is mapped as a linear way, where the given edges form the platform.
+ */
+ public Set linkToSpecificStreetEdgesPermanently(
+ Vertex vertex,
+ TraverseModeSet traverseModes,
+ LinkingDirection direction,
+ Set edges
+ ) {
+ var xscale = getXscale(vertex);
+ return linkToCandidateEdges(
+ vertex,
+ traverseModes,
+ direction,
+ Scope.PERMANENT,
+ null,
+ edges.stream().map(e -> new DistanceTo<>(e, distance(vertex, e, xscale))).toList(),
+ xscale
+ );
+ }
+
private Set linkToStreetEdges(
Vertex vertex,
TraverseModeSet traverseModes,
LinkingDirection direction,
Scope scope,
int radiusMeters,
- DisposableEdgeCollection tempEdges
+ @Nullable DisposableEdgeCollection tempEdges
) {
final double radiusDeg = SphericalDistanceLibrary.metersToDegrees(radiusMeters);
Envelope env = new Envelope(vertex.getCoordinate());
// Perform a simple local equirectangular projection, so distances are expressed in degrees latitude.
- final double xscale = Math.cos(vertex.getLat() * Math.PI / 180);
+ final double xscale = getXscale(vertex);
// Expand more in the longitude direction than the latitude direction to account for converging meridians.
env.expandBy(radiusDeg / xscale, radiusDeg);
@@ -257,6 +281,30 @@ private Set linkToStreetEdges(
.filter(ead -> ead.distanceDegreesLat < radiusDeg)
.toList();
+ return linkToCandidateEdges(
+ vertex,
+ traverseModes,
+ direction,
+ scope,
+ tempEdges,
+ candidateEdges,
+ xscale
+ );
+ }
+
+ private static double getXscale(Vertex vertex) {
+ return Math.cos(vertex.getLat() * Math.PI / 180);
+ }
+
+ private Set linkToCandidateEdges(
+ Vertex vertex,
+ TraverseModeSet traverseModes,
+ LinkingDirection direction,
+ Scope scope,
+ @Nullable DisposableEdgeCollection tempEdges,
+ List> candidateEdges,
+ double xscale
+ ) {
if (candidateEdges.isEmpty()) {
return Set.of();
}
@@ -269,7 +317,7 @@ private Set linkToStreetEdges(
return closestEdges
.stream()
.map(ce -> link(vertex, ce.item, xscale, scope, direction, tempEdges, linkedAreas))
- .filter(v -> v != null)
+ .filter(Objects::nonNull)
.collect(Collectors.toSet());
}
diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildRepository.java b/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildRepository.java
new file mode 100644
index 00000000000..ac8f7276072
--- /dev/null
+++ b/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildRepository.java
@@ -0,0 +1,23 @@
+package org.opentripplanner.service.osminfo;
+
+import java.io.Serializable;
+import java.util.Optional;
+import org.opentripplanner.service.osminfo.model.Platform;
+import org.opentripplanner.street.model.edge.Edge;
+
+/**
+ * Store OSM data used during graph build, but discard it after it is complete.
+ *
+ * This is a repository to support the {@link OsmInfoGraphBuildService}.
+ */
+public interface OsmInfoGraphBuildRepository extends Serializable {
+ /**
+ * Associate the edge with a platform
+ */
+ void addPlatform(Edge edge, Platform platform);
+
+ /**
+ * Find the platform the edge belongs to
+ */
+ Optional findPlatform(Edge edge);
+}
diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildService.java b/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildService.java
new file mode 100644
index 00000000000..6a50c3c92be
--- /dev/null
+++ b/application/src/main/java/org/opentripplanner/service/osminfo/OsmInfoGraphBuildService.java
@@ -0,0 +1,25 @@
+package org.opentripplanner.service.osminfo;
+
+import java.util.Optional;
+import org.opentripplanner.service.osminfo.model.Platform;
+import org.opentripplanner.street.model.edge.Edge;
+
+/**
+ * The responsibility of this service is to provide information from Open Street Map, which
+ * is NOT in the OTP street graph. The graph build happens in phases, and some data is read in
+ * from the OSM files, but needed later on. For example, we might need info from OSM to link street
+ * edges/vertexes with transit stops/platforms. We do not want to put data in the OTP street graph
+ * unless it is relevant for routing. So, for information that is read by the OsmGraphBuilder, but
+ * needed later on, we have this service.
+ *
+ * THIS SERVICE IS ONLY AVAILABLE DURING GRAPH BUILD, NOT DURING ROUTING. *
+ */
+public interface OsmInfoGraphBuildService {
+ /**
+ * Find the platform the given edge is part of.
+ *
+ * TODO: This service currently only stores linear platforms, but area platforms and
+ * node platforms should be supported as well.
+ */
+ Optional findPlatform(Edge edge);
+}
diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/configure/OsmInfoGraphBuildRepositoryModule.java b/application/src/main/java/org/opentripplanner/service/osminfo/configure/OsmInfoGraphBuildRepositoryModule.java
new file mode 100644
index 00000000000..d8b9db5608e
--- /dev/null
+++ b/application/src/main/java/org/opentripplanner/service/osminfo/configure/OsmInfoGraphBuildRepositoryModule.java
@@ -0,0 +1,12 @@
+package org.opentripplanner.service.osminfo.configure;
+
+import dagger.Binds;
+import dagger.Module;
+import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository;
+import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository;
+
+@Module
+public interface OsmInfoGraphBuildRepositoryModule {
+ @Binds
+ OsmInfoGraphBuildRepository bind(DefaultOsmInfoGraphBuildRepository repository);
+}
diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/configure/OsmInfoGraphBuildServiceModule.java b/application/src/main/java/org/opentripplanner/service/osminfo/configure/OsmInfoGraphBuildServiceModule.java
new file mode 100644
index 00000000000..c6ac5c31ec9
--- /dev/null
+++ b/application/src/main/java/org/opentripplanner/service/osminfo/configure/OsmInfoGraphBuildServiceModule.java
@@ -0,0 +1,12 @@
+package org.opentripplanner.service.osminfo.configure;
+
+import dagger.Binds;
+import dagger.Module;
+import org.opentripplanner.service.osminfo.OsmInfoGraphBuildService;
+import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildService;
+
+@Module
+public interface OsmInfoGraphBuildServiceModule {
+ @Binds
+ OsmInfoGraphBuildService bind(DefaultOsmInfoGraphBuildService service);
+}
diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildRepository.java b/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildRepository.java
new file mode 100644
index 00000000000..6505fdd67a4
--- /dev/null
+++ b/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildRepository.java
@@ -0,0 +1,37 @@
+package org.opentripplanner.service.osminfo.internal;
+
+import jakarta.inject.Inject;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository;
+import org.opentripplanner.service.osminfo.model.Platform;
+import org.opentripplanner.street.model.edge.Edge;
+
+public class DefaultOsmInfoGraphBuildRepository
+ implements OsmInfoGraphBuildRepository, Serializable {
+
+ private final Map platforms = new HashMap<>();
+
+ @Inject
+ public DefaultOsmInfoGraphBuildRepository() {}
+
+ @Override
+ public void addPlatform(Edge edge, Platform platform) {
+ Objects.requireNonNull(edge);
+ Objects.requireNonNull(platform);
+ this.platforms.put(edge, platform);
+ }
+
+ @Override
+ public Optional findPlatform(Edge edge) {
+ return Optional.ofNullable(platforms.get(edge));
+ }
+
+ @Override
+ public String toString() {
+ return "DefaultOsmInfoGraphBuildRepository{platforms size = " + platforms.size() + "}";
+ }
+}
diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildService.java b/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildService.java
new file mode 100644
index 00000000000..42eb5bf364f
--- /dev/null
+++ b/application/src/main/java/org/opentripplanner/service/osminfo/internal/DefaultOsmInfoGraphBuildService.java
@@ -0,0 +1,28 @@
+package org.opentripplanner.service.osminfo.internal;
+
+import jakarta.inject.Inject;
+import java.util.Optional;
+import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository;
+import org.opentripplanner.service.osminfo.OsmInfoGraphBuildService;
+import org.opentripplanner.service.osminfo.model.Platform;
+import org.opentripplanner.street.model.edge.Edge;
+
+public class DefaultOsmInfoGraphBuildService implements OsmInfoGraphBuildService {
+
+ private final OsmInfoGraphBuildRepository repository;
+
+ @Inject
+ public DefaultOsmInfoGraphBuildService(OsmInfoGraphBuildRepository repository) {
+ this.repository = repository;
+ }
+
+ @Override
+ public Optional findPlatform(Edge edge) {
+ return repository.findPlatform(edge);
+ }
+
+ @Override
+ public String toString() {
+ return "DefaultOsmInfoGraphBuildService{ repository=" + repository + '}';
+ }
+}
diff --git a/application/src/main/java/org/opentripplanner/service/osminfo/model/Platform.java b/application/src/main/java/org/opentripplanner/service/osminfo/model/Platform.java
new file mode 100644
index 00000000000..91d78385a34
--- /dev/null
+++ b/application/src/main/java/org/opentripplanner/service/osminfo/model/Platform.java
@@ -0,0 +1,7 @@
+package org.opentripplanner.service.osminfo.model;
+
+import java.util.Set;
+import org.locationtech.jts.geom.LineString;
+import org.opentripplanner.framework.i18n.I18NString;
+
+public record Platform(I18NString name, LineString geometry, Set references) {}
diff --git a/application/src/main/java/org/opentripplanner/standalone/OTPMain.java b/application/src/main/java/org/opentripplanner/standalone/OTPMain.java
index ade5067a981..25eea6df473 100644
--- a/application/src/main/java/org/opentripplanner/standalone/OTPMain.java
+++ b/application/src/main/java/org/opentripplanner/standalone/OTPMain.java
@@ -150,6 +150,7 @@ private static void startOTPServer(CommandLineParameters cli) {
// with using the embedded router config.
new SerializedGraphObject(
app.graph(),
+ app.osmInfoGraphBuildRepository(),
app.timetableRepository(),
app.worldEnvelopeRepository(),
app.vehicleParkingRepository(),
diff --git a/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java b/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java
index b4edbb36299..ebb83a045d0 100644
--- a/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java
+++ b/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplication.java
@@ -18,6 +18,7 @@
import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.TransitLayerMapper;
import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.TransitLayerUpdater;
import org.opentripplanner.routing.graph.Graph;
+import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository;
import org.opentripplanner.service.realtimevehicles.RealtimeVehicleRepository;
import org.opentripplanner.service.vehicleparking.VehicleParkingRepository;
import org.opentripplanner.service.vehicleparking.VehicleParkingService;
@@ -64,6 +65,11 @@ public class ConstructApplication {
private final CommandLineParameters cli;
private final GraphBuilderDataSources graphBuilderDataSources;
+ /**
+ * The OSM Info is injected into the graph-builder, but not the web-server; Hence not part of
+ * the application context.
+ */
+ private final OsmInfoGraphBuildRepository osmInfoGraphBuildRepository;
private final ConstructApplicationFactory factory;
/**
@@ -72,6 +78,7 @@ public class ConstructApplication {
ConstructApplication(
CommandLineParameters cli,
Graph graph,
+ OsmInfoGraphBuildRepository osmInfoGraphBuildRepository,
TimetableRepository timetableRepository,
WorldEnvelopeRepository worldEnvelopeRepository,
ConfigModel config,
@@ -84,6 +91,7 @@ public class ConstructApplication {
) {
this.cli = cli;
this.graphBuilderDataSources = graphBuilderDataSources;
+ this.osmInfoGraphBuildRepository = osmInfoGraphBuildRepository;
// We create the optional GraphVisualizer here, because it would be significant more complex to
// use Dagger DI to do it - passing in a parameter to enable it or not.
@@ -130,7 +138,8 @@ public GraphBuilder createGraphBuilder() {
buildConfig(),
graphBuilderDataSources,
graph(),
- timetableRepository(),
+ osmInfoGraphBuildRepository,
+ factory.timetableRepository(),
factory.worldEnvelopeRepository(),
factory.vehicleParkingRepository(),
factory.emissionsDataModel(),
@@ -261,6 +270,10 @@ public DataImportIssueSummary dataImportIssueSummary() {
return factory.dataImportIssueSummary();
}
+ public OsmInfoGraphBuildRepository osmInfoGraphBuildRepository() {
+ return osmInfoGraphBuildRepository;
+ }
+
public StopConsolidationRepository stopConsolidationRepository() {
return factory.stopConsolidationRepository();
}
diff --git a/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationFactory.java b/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationFactory.java
index d6310c0c616..3d479f0fa63 100644
--- a/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationFactory.java
+++ b/application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationFactory.java
@@ -51,21 +51,21 @@
@Component(
modules = {
ConfigModule.class,
- TransitModule.class,
- WorldEnvelopeServiceModule.class,
+ ConstructApplicationModule.class,
+ EmissionsServiceModule.class,
+ GeocoderModule.class,
+ InteractiveLauncherModule.class,
RealtimeVehicleServiceModule.class,
RealtimeVehicleRepositoryModule.class,
- VehicleRentalServiceModule.class,
- VehicleRentalRepositoryModule.class,
- VehicleParkingServiceModule.class,
- ConstructApplicationModule.class,
RideHailingServicesModule.class,
- EmissionsServiceModule.class,
+ TransitModule.class,
+ VehicleParkingServiceModule.class,
+ VehicleRentalRepositoryModule.class,
+ VehicleRentalServiceModule.class,
SorlandsbanenNorwayModule.class,
StopConsolidationServiceModule.class,
- InteractiveLauncherModule.class,
StreetLimitationParametersServiceModule.class,
- GeocoderModule.class,
+ WorldEnvelopeServiceModule.class,
}
)
public interface ConstructApplicationFactory {
diff --git a/application/src/main/java/org/opentripplanner/standalone/configure/LoadApplication.java b/application/src/main/java/org/opentripplanner/standalone/configure/LoadApplication.java
index 021af778345..ad1a7293855 100644
--- a/application/src/main/java/org/opentripplanner/standalone/configure/LoadApplication.java
+++ b/application/src/main/java/org/opentripplanner/standalone/configure/LoadApplication.java
@@ -8,6 +8,7 @@
import org.opentripplanner.graph_builder.issue.api.DataImportIssueSummary;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.graph.SerializedGraphObject;
+import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository;
import org.opentripplanner.service.vehicleparking.VehicleParkingRepository;
import org.opentripplanner.service.worldenvelope.WorldEnvelopeRepository;
import org.opentripplanner.standalone.config.CommandLineParameters;
@@ -20,7 +21,7 @@
* This is used to load the graph, and finally this class can create the
* {@link ConstructApplication} for the next phase.
*
- * By splitting the these two responsibilities into two separate phases we are sure all
+ * By splitting these two responsibilities into two separate phases we are sure all
* components (graph and transit model) created in the load phase will be available for
* creating the application using Dagger dependency injection.
*/
@@ -55,6 +56,7 @@ public DataSource getInputGraphDataStore() {
public ConstructApplication appConstruction(SerializedGraphObject obj) {
return createAppConstruction(
obj.graph,
+ obj.osmInfoGraphBuildRepository,
obj.timetableRepository,
obj.worldEnvelopeRepository,
obj.parkingRepository,
@@ -69,6 +71,7 @@ public ConstructApplication appConstruction(SerializedGraphObject obj) {
public ConstructApplication appConstruction() {
return createAppConstruction(
factory.emptyGraph(),
+ factory.emptyOsmInfoGraphBuildRepository(),
factory.emptyTimetableRepository(),
factory.emptyWorldEnvelopeRepository(),
factory.emptyVehicleParkingRepository(),
@@ -92,6 +95,7 @@ public ConfigModel config() {
private ConstructApplication createAppConstruction(
Graph graph,
+ OsmInfoGraphBuildRepository osmInfoGraphBuildRepository,
TimetableRepository timetableRepository,
WorldEnvelopeRepository worldEnvelopeRepository,
VehicleParkingRepository parkingRepository,
@@ -103,6 +107,7 @@ private ConstructApplication createAppConstruction(
return new ConstructApplication(
cli,
graph,
+ osmInfoGraphBuildRepository,
timetableRepository,
worldEnvelopeRepository,
config(),
diff --git a/application/src/main/java/org/opentripplanner/standalone/configure/LoadApplicationFactory.java b/application/src/main/java/org/opentripplanner/standalone/configure/LoadApplicationFactory.java
index b054fac3ca5..9fdbf59bfda 100644
--- a/application/src/main/java/org/opentripplanner/standalone/configure/LoadApplicationFactory.java
+++ b/application/src/main/java/org/opentripplanner/standalone/configure/LoadApplicationFactory.java
@@ -11,6 +11,8 @@
import org.opentripplanner.ext.stopconsolidation.configure.StopConsolidationRepositoryModule;
import org.opentripplanner.graph_builder.GraphBuilderDataSources;
import org.opentripplanner.routing.graph.Graph;
+import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository;
+import org.opentripplanner.service.osminfo.configure.OsmInfoGraphBuildRepositoryModule;
import org.opentripplanner.service.vehicleparking.VehicleParkingRepository;
import org.opentripplanner.service.vehicleparking.configure.VehicleParkingRepositoryModule;
import org.opentripplanner.service.worldenvelope.WorldEnvelopeRepository;
@@ -30,6 +32,7 @@
LoadConfigModule.class,
DataStoreModule.class,
GsDataSourceModule.class,
+ OsmInfoGraphBuildRepositoryModule.class,
WorldEnvelopeRepositoryModule.class,
StopConsolidationRepositoryModule.class,
VehicleParkingRepositoryModule.class,
@@ -43,6 +46,9 @@ public interface LoadApplicationFactory {
@Singleton
Graph emptyGraph();
+ @Singleton
+ OsmInfoGraphBuildRepository emptyOsmInfoGraphBuildRepository();
+
@Singleton
TimetableRepository emptyTimetableRepository();
diff --git a/application/src/main/java/org/opentripplanner/street/model/edge/BoardingLocationToStopLink.java b/application/src/main/java/org/opentripplanner/street/model/edge/BoardingLocationToStopLink.java
index 2b306b63cf3..235ec7c6be5 100644
--- a/application/src/main/java/org/opentripplanner/street/model/edge/BoardingLocationToStopLink.java
+++ b/application/src/main/java/org/opentripplanner/street/model/edge/BoardingLocationToStopLink.java
@@ -3,7 +3,7 @@
import java.util.List;
import org.locationtech.jts.geom.LineString;
import org.opentripplanner.framework.geometry.GeometryUtils;
-import org.opentripplanner.street.model.vertex.OsmBoardingLocationVertex;
+import org.opentripplanner.street.model.vertex.StreetVertex;
import org.opentripplanner.street.model.vertex.TransitStopVertex;
/**
@@ -12,16 +12,16 @@
*/
public class BoardingLocationToStopLink extends StreetTransitEntityLink {
- private BoardingLocationToStopLink(OsmBoardingLocationVertex fromv, TransitStopVertex tov) {
+ private BoardingLocationToStopLink(StreetVertex fromv, TransitStopVertex tov) {
super(fromv, tov, tov.getWheelchairAccessibility());
}
- private BoardingLocationToStopLink(TransitStopVertex fromv, OsmBoardingLocationVertex tov) {
+ private BoardingLocationToStopLink(TransitStopVertex fromv, StreetVertex tov) {
super(fromv, tov, fromv.getWheelchairAccessibility());
}
public static BoardingLocationToStopLink createBoardingLocationToStopLink(
- OsmBoardingLocationVertex fromv,
+ StreetVertex fromv,
TransitStopVertex tov
) {
return connectToGraph(new BoardingLocationToStopLink(fromv, tov));
@@ -29,7 +29,7 @@ public static BoardingLocationToStopLink createBoardingLocationToStopLink(
public static BoardingLocationToStopLink createBoardingLocationToStopLink(
TransitStopVertex fromv,
- OsmBoardingLocationVertex tov
+ StreetVertex tov
) {
return connectToGraph(new BoardingLocationToStopLink(fromv, tov));
}
diff --git a/application/src/test/java/org/opentripplanner/ConstantsForTests.java b/application/src/test/java/org/opentripplanner/ConstantsForTests.java
index e5ab48cee54..3f188ff2c89 100644
--- a/application/src/test/java/org/opentripplanner/ConstantsForTests.java
+++ b/application/src/test/java/org/opentripplanner/ConstantsForTests.java
@@ -34,6 +34,7 @@
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.linking.LinkingDirection;
import org.opentripplanner.routing.linking.VertexLinker;
+import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository;
import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository;
import org.opentripplanner.service.vehiclerental.model.RentalVehicleType;
import org.opentripplanner.service.vehiclerental.model.VehicleRentalStation;
@@ -137,9 +138,11 @@ public static TestOtpModel buildNewPortlandGraph(boolean withElevation) {
var timetableRepository = new TimetableRepository(new SiteRepository(), deduplicator);
// Add street data from OSM
{
- OsmProvider osmProvider = new OsmProvider(PORTLAND_CENTRAL_OSM, false);
- OsmModule osmModule = OsmModule
- .of(osmProvider, graph, new DefaultVehicleParkingRepository())
+ var osmProvider = new OsmProvider(PORTLAND_CENTRAL_OSM, false);
+ var osmInfoRepository = new DefaultOsmInfoGraphBuildRepository();
+ var vehicleParkingRepository = new DefaultVehicleParkingRepository();
+ var osmModule = OsmModule
+ .of(osmProvider, graph, osmInfoRepository, vehicleParkingRepository)
.withStaticParkAndRide(true)
.withStaticBikeParkAndRide(true)
.build();
@@ -195,9 +198,11 @@ public static TestOtpModel buildOsmGraph(File osmFile) {
var graph = new Graph(deduplicator);
var timetableRepository = new TimetableRepository(siteRepository, deduplicator);
// Add street data from OSM
- OsmProvider osmProvider = new OsmProvider(osmFile, true);
- OsmModule osmModule = OsmModule
- .of(osmProvider, graph, new DefaultVehicleParkingRepository())
+ var osmProvider = new OsmProvider(osmFile, true);
+ var osmInfoRepository = new DefaultOsmInfoGraphBuildRepository();
+ var vehicleParkingRepository = new DefaultVehicleParkingRepository();
+ var osmModule = OsmModule
+ .of(osmProvider, graph, osmInfoRepository, vehicleParkingRepository)
.build();
osmModule.buildGraph();
return new TestOtpModel(graph, timetableRepository);
@@ -245,8 +250,9 @@ public static TestOtpModel buildNewMinimalNetexGraph() {
var timetableRepository = new TimetableRepository(siteRepository, deduplicator);
// Add street data from OSM
{
- OsmProvider osmProvider = new OsmProvider(OSLO_EAST_OSM, false);
- OsmModule osmModule = OsmModule.of(osmProvider, graph, parkingService).build();
+ var osmProvider = new OsmProvider(OSLO_EAST_OSM, false);
+ var osmInfoRepository = new DefaultOsmInfoGraphBuildRepository();
+ var osmModule = OsmModule.of(osmProvider, graph, osmInfoRepository, parkingService).build();
osmModule.buildGraph();
}
// Add transit data from Netex
diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java
index c55e482e533..a4d5e86ced8 100644
--- a/application/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java
+++ b/application/src/test/java/org/opentripplanner/graph_builder/module/OsmBoardingLocationsModuleTest.java
@@ -2,18 +2,27 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.File;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
+import org.opentripplanner.framework.geometry.SphericalDistanceLibrary;
+import org.opentripplanner.framework.i18n.I18NString;
import org.opentripplanner.framework.i18n.NonLocalizedString;
import org.opentripplanner.graph_builder.module.osm.OsmModule;
import org.opentripplanner.osm.OsmProvider;
import org.opentripplanner.routing.graph.Graph;
+import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository;
+import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildService;
import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository;
import org.opentripplanner.street.model.edge.AreaEdge;
import org.opentripplanner.street.model.edge.BoardingLocationToStopLink;
@@ -32,25 +41,11 @@
import org.opentripplanner.transit.service.SiteRepository;
import org.opentripplanner.transit.service.TimetableRepository;
-/**
- * We test that the platform area at Herrenberg station (https://www.openstreetmap.org/way/27558650)
- * is correctly linked to the stop even though it is not the closest edge to the stop.
- */
class OsmBoardingLocationsModuleTest {
private final TimetableRepositoryForTest testModel = TimetableRepositoryForTest.of();
- File file = ResourceLoader
- .of(OsmBoardingLocationsModuleTest.class)
- .file("herrenberg-minimal.osm.pbf");
- RegularStop platform = testModel
- .stop("de:08115:4512:4:101")
- .withCoordinate(48.59328, 8.86128)
- .build();
- RegularStop busStop = testModel.stop("de:08115:4512:5:C", 48.59434, 8.86452).build();
- RegularStop floatingBusStop = testModel.stop("floating-bus-stop", 48.59417, 8.86464).build();
-
- static Stream testCases() {
+ static Stream herrenbergTestCases() {
return Stream.of(
Arguments.of(
false,
@@ -63,11 +58,25 @@ static Stream testCases() {
);
}
+ /**
+ * We test that the platform area at Herrenberg station (https://www.openstreetmap.org/way/27558650)
+ * is correctly linked to the stop even though it is not the closest edge to the stop.
+ */
@ParameterizedTest(
name = "add boarding locations and link them to platform edges when skipVisibility={0}"
)
- @MethodSource("testCases")
+ @MethodSource("herrenbergTestCases")
void addAndLinkBoardingLocations(boolean areaVisibility, Set linkedVertices) {
+ File file = ResourceLoader
+ .of(OsmBoardingLocationsModuleTest.class)
+ .file("herrenberg-minimal.osm.pbf");
+ RegularStop platform = testModel
+ .stop("de:08115:4512:4:101")
+ .withCoordinate(48.59328, 8.86128)
+ .build();
+ RegularStop busStop = testModel.stop("de:08115:4512:5:C", 48.59434, 8.86452).build();
+ RegularStop floatingBusStop = testModel.stop("floating-bus-stop", 48.59417, 8.86464).build();
+
var deduplicator = new Deduplicator();
var graph = new Graph(deduplicator);
var timetableRepository = new TimetableRepository(new SiteRepository(), deduplicator);
@@ -83,8 +92,10 @@ void addAndLinkBoardingLocations(boolean areaVisibility, Set linkedVerti
Set.of(floatingBusVertex.getStop().getId().getId()),
new NonLocalizedString("bus stop not connected to street network")
);
+ var osmInfoRepository = new DefaultOsmInfoGraphBuildRepository();
+ var vehicleParkingRepository = new DefaultVehicleParkingRepository();
var osmModule = OsmModule
- .of(provider, graph, new DefaultVehicleParkingRepository())
+ .of(provider, graph, osmInfoRepository, vehicleParkingRepository)
.withBoardingAreaRefTags(Set.of("ref", "ref:IFOPT"))
.withAreaVisibility(areaVisibility)
.build();
@@ -107,7 +118,8 @@ void addAndLinkBoardingLocations(boolean areaVisibility, Set linkedVerti
assertEquals(0, platformVertex.getIncoming().size());
assertEquals(0, platformVertex.getOutgoing().size());
- new OsmBoardingLocationsModule(graph, timetableRepository).buildGraph();
+ var osmService = new DefaultOsmInfoGraphBuildService(osmInfoRepository);
+ new OsmBoardingLocationsModule(graph, osmService, timetableRepository).buildGraph();
var boardingLocations = graph.getVerticesOfType(OsmBoardingLocationVertex.class);
assertEquals(5, boardingLocations.size()); // 3 nodes connected to the street network, plus one "floating" and one area centroid created by the module
@@ -141,13 +153,13 @@ void addAndLinkBoardingLocations(boolean areaVisibility, Set linkedVerti
assertEquals(1, platformCentroids.size());
- var platform = platformCentroids.get(0);
+ var platformCentroid = platformCentroids.get(0);
- assertConnections(platform, Set.of(BoardingLocationToStopLink.class, AreaEdge.class));
+ assertConnections(platformCentroid, Set.of(BoardingLocationToStopLink.class, AreaEdge.class));
assertEquals(
linkedVertices,
- platform
+ platformCentroid
.getOutgoingStreetEdges()
.stream()
.map(Edge::getToVertex)
@@ -157,7 +169,7 @@ void addAndLinkBoardingLocations(boolean areaVisibility, Set linkedVerti
assertEquals(
linkedVertices,
- platform
+ platformCentroid
.getIncomingStreetEdges()
.stream()
.map(Edge::getFromVertex)
@@ -177,6 +189,201 @@ void addAndLinkBoardingLocations(boolean areaVisibility, Set linkedVerti
.forEach(e -> assertEquals("Platform 101;102", e.getName().toString()));
}
+ /**
+ * We test that the underground platforms at Moorgate station (https://www.openstreetmap.org/way/1328222021)
+ * is correctly linked to the stop even though it is not the closest edge to the stop.
+ */
+ @Test
+ void testLinearPlatforms() {
+ var deduplicator = new Deduplicator();
+ var graph = new Graph(deduplicator);
+ var osmInfoRepository = new DefaultOsmInfoGraphBuildRepository();
+ var osmModule = OsmModule
+ .of(
+ new OsmProvider(
+ ResourceLoader.of(OsmBoardingLocationsModuleTest.class).file("moorgate.osm.pbf"),
+ false
+ ),
+ graph,
+ osmInfoRepository,
+ new DefaultVehicleParkingRepository()
+ )
+ .withBoardingAreaRefTags(Set.of("naptan:AtcoCode"))
+ .build();
+ osmModule.buildGraph();
+
+ var factory = new VertexFactory(graph);
+
+ class TestCase {
+
+ /**
+ * The linear platform to be tested
+ */
+ public final RegularStop platform;
+
+ /**
+ * The label of a vertex where the centroid should be connected to
+ */
+ public final VertexLabel beginLabel;
+
+ /**
+ * The label of the other vertex where the centroid should be connected to
+ */
+ public final VertexLabel endLabel;
+
+ private TransitStopVertex platformVertex = null;
+
+ public TestCase(RegularStop platform, VertexLabel beginLabel, VertexLabel endLabel) {
+ this.platform = platform;
+ this.beginLabel = beginLabel;
+ this.endLabel = endLabel;
+ }
+
+ /**
+ * Get a TransitStopVertex for the platform in the graph. It is made and added to the graph
+ * on the first call.
+ */
+ TransitStopVertex getPlatformVertex() {
+ if (platformVertex == null) {
+ platformVertex = factory.transitStop(TransitStopVertex.of().withStop(platform));
+ }
+ return platformVertex;
+ }
+ }
+
+ var testCases = List.of(
+ new TestCase(
+ testModel
+ .stop("9100MRGT9")
+ .withName(I18NString.of("Moorgate (Platform 9)"))
+ .withCoordinate(51.51922107872304, -0.08767468698832413)
+ .withPlatformCode("9")
+ .build(),
+ VertexLabel.osm(12288669589L),
+ VertexLabel.osm(12288675219L)
+ ),
+ new TestCase(
+ testModel
+ .stop("9400ZZLUMGT3")
+ .withName(I18NString.of("Moorgate (Platform 7)"))
+ .withCoordinate(51.51919235051611, -0.08769925990953176)
+ .withPlatformCode("7")
+ .build(),
+ VertexLabel.osm(12288669575L),
+ VertexLabel.osm(12288675230L)
+ )
+ );
+
+ for (var testCase : testCases) {
+ // test that the platforms are not connected
+ var platformVertex = testCase.getPlatformVertex();
+ assertEquals(0, platformVertex.getIncoming().size());
+ assertEquals(0, platformVertex.getOutgoing().size());
+
+ // test that the vertices to be connected by the centroid are currently connected
+ var fromVertex = Objects.requireNonNull(graph.getVertex(testCase.beginLabel));
+ var toVertex = Objects.requireNonNull(graph.getVertex(testCase.endLabel));
+ assertTrue(
+ getEdge(fromVertex, toVertex).isPresent(),
+ "malformed test: the vertices where the centroid is supposed to be located between aren't connected"
+ );
+ assertTrue(
+ getEdge(toVertex, fromVertex).isPresent(),
+ "malformed test: the vertices where the centroid is supposed to be located between aren't connected"
+ );
+ }
+
+ var timetableRepository = new TimetableRepository(new SiteRepository(), deduplicator);
+ new OsmBoardingLocationsModule(
+ graph,
+ new DefaultOsmInfoGraphBuildService(osmInfoRepository),
+ timetableRepository
+ )
+ .buildGraph();
+
+ var boardingLocations = graph.getVerticesOfType(OsmBoardingLocationVertex.class);
+
+ for (var testCase : testCases) {
+ var platformVertex = testCase.getPlatformVertex();
+ var fromVertex = Objects.requireNonNull(graph.getVertex(testCase.beginLabel));
+ var toVertex = Objects.requireNonNull(graph.getVertex(testCase.endLabel));
+
+ var centroid = boardingLocations
+ .stream()
+ .filter(b -> b.references.contains(testCase.platform.getId().getId()))
+ .findFirst()
+ .orElseThrow();
+
+ // TODO: we should ideally place the centroid vertex directly on the platform by splitting
+ // the platform edge, but it is too difficult to touch the splitter code to use a given
+ // centroid vertex instead of a generated split vertex, so what we actually do is to directly
+ // connect the platform vertex to the split vertex
+
+ // the actual centroid isn't used
+ assertEquals(0, centroid.getDegreeIn());
+ assertEquals(0, centroid.getDegreeOut());
+
+ for (var vertex : platformVertex.getIncoming()) {
+ assertSplitVertex(vertex.getFromVertex(), centroid, fromVertex, toVertex);
+ }
+
+ for (var vertex : platformVertex.getOutgoing()) {
+ assertSplitVertex(vertex.getToVertex(), centroid, fromVertex, toVertex);
+ }
+ }
+ }
+
+ /**
+ * Assert that a split vertex is near to the given centroid, and it is possible to travel between
+ * the original vertices through the split vertex in a straight line
+ */
+ private static void assertSplitVertex(
+ Vertex splitVertex,
+ OsmBoardingLocationVertex centroid,
+ Vertex begin,
+ Vertex end
+ ) {
+ var distance = SphericalDistanceLibrary.distance(
+ splitVertex.getCoordinate(),
+ centroid.getCoordinate()
+ );
+ // FIXME: I am not sure why the calculated centroid from the original OSM geometry is about 2 m
+ // from the platform
+ assertTrue(distance < 4, "The split vertex is more than 4 m apart from the centroid");
+ assertConnections(splitVertex, begin, end);
+
+ if (splitVertex != begin && splitVertex != end) {
+ var forwardEdges = getEdge(begin, splitVertex)
+ .flatMap(first -> getEdge(splitVertex, end).map(second -> List.of(first, second)));
+ var backwardEdges = getEdge(end, splitVertex)
+ .flatMap(first -> getEdge(splitVertex, begin).map(second -> List.of(first, second)));
+ for (var edgeList : List.of(forwardEdges, backwardEdges)) {
+ edgeList.ifPresent(edges ->
+ assertEquals(
+ edges.getFirst().getOutAngle(),
+ edges.getLast().getInAngle(),
+ "The split vertex is not on a straight line between the connected vertices"
+ )
+ );
+ }
+ }
+ }
+
+ /**
+ * Assert that there is a one-way path from the beginning through the given vertex to the end
+ * or vice versa.
+ */
+ private static void assertConnections(Vertex vertex, Vertex beginning, Vertex end) {
+ if (vertex == beginning || vertex == end) {
+ assertTrue(beginning.isConnected(end));
+ }
+
+ assertTrue(
+ (getEdge(beginning, vertex).isPresent() && getEdge(vertex, end).isPresent()) ||
+ (getEdge(end, vertex).isPresent() && getEdge(vertex, beginning).isPresent())
+ );
+ }
+
private void assertConnections(
OsmBoardingLocationVertex busBoardingLocation,
Set> expected
@@ -187,4 +394,12 @@ private void assertConnections(
assertEquals(expected, edges.stream().map(Edge::getClass).collect(Collectors.toSet()))
);
}
+
+ private static Optional getEdge(Vertex from, Vertex to) {
+ return from
+ .getOutgoingStreetEdges()
+ .stream()
+ .filter(edge -> edge.getToVertex() == to)
+ .findFirst();
+ }
}
diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/IslandPruningUtils.java b/application/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/IslandPruningUtils.java
index d71a60a972e..8e5a455095b 100644
--- a/application/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/IslandPruningUtils.java
+++ b/application/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/IslandPruningUtils.java
@@ -5,6 +5,7 @@
import org.opentripplanner.graph_builder.module.osm.OsmModule;
import org.opentripplanner.osm.OsmProvider;
import org.opentripplanner.routing.graph.Graph;
+import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository;
import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository;
import org.opentripplanner.transit.model.framework.Deduplicator;
import org.opentripplanner.transit.service.SiteRepository;
@@ -24,9 +25,11 @@ static Graph buildOsmGraph(
var graph = new Graph(deduplicator);
var timetableRepository = new TimetableRepository(new SiteRepository(), deduplicator);
// Add street data from OSM
- OsmProvider osmProvider = new OsmProvider(osmFile, true);
- OsmModule osmModule = OsmModule
- .of(osmProvider, graph, new DefaultVehicleParkingRepository())
+ var osmProvider = new OsmProvider(osmFile, true);
+ var osmInfoRepository = new DefaultOsmInfoGraphBuildRepository();
+ var vehicleParkingRepository = new DefaultVehicleParkingRepository();
+ var osmModule = OsmModule
+ .of(osmProvider, graph, osmInfoRepository, vehicleParkingRepository)
.withEdgeNamer(new TestNamer())
.build();
diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/linking/LinkingTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/linking/LinkingTest.java
index a6afa89707f..6c51235e703 100644
--- a/application/src/test/java/org/opentripplanner/graph_builder/module/linking/LinkingTest.java
+++ b/application/src/test/java/org/opentripplanner/graph_builder/module/linking/LinkingTest.java
@@ -21,6 +21,7 @@
import org.opentripplanner.graph_builder.module.osm.OsmModule;
import org.opentripplanner.osm.OsmProvider;
import org.opentripplanner.routing.graph.Graph;
+import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository;
import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository;
import org.opentripplanner.street.model.StreetTraversalPermission;
import org.opentripplanner.street.model._data.StreetModelForTest;
@@ -152,16 +153,20 @@ public void testStopsLinkedIdentically() {
public static TestOtpModel buildGraphNoTransit() {
var deduplicator = new Deduplicator();
var siteRepository = new SiteRepository();
- var gg = new Graph(deduplicator);
+ var graph = new Graph(deduplicator);
var timetableRepository = new TimetableRepository(siteRepository, deduplicator);
File file = ResourceLoader.of(LinkingTest.class).file("columbus.osm.pbf");
- OsmProvider provider = new OsmProvider(file, false);
+ var provider = new OsmProvider(file, false);
+ var osmInfoRepository = new DefaultOsmInfoGraphBuildRepository();
+ var vehicleParkingRepository = new DefaultVehicleParkingRepository();
- OsmModule osmModule = OsmModule.of(provider, gg, new DefaultVehicleParkingRepository()).build();
+ var osmModule = OsmModule
+ .of(provider, graph, osmInfoRepository, vehicleParkingRepository)
+ .build();
osmModule.buildGraph();
- return new TestOtpModel(gg, timetableRepository);
+ return new TestOtpModel(graph, timetableRepository);
}
private static List outgoingStls(final TransitStopVertex tsv) {
diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java
index 833b14ade9d..6de345ddd9c 100644
--- a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java
+++ b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java
@@ -32,6 +32,7 @@
import org.opentripplanner.routing.api.request.RouteRequest;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.impl.GraphPathFinder;
+import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository;
import org.opentripplanner.service.vehicleparking.VehicleParkingRepository;
import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository;
import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingService;
@@ -53,30 +54,35 @@ public class OsmModuleTest {
@Test
public void testGraphBuilder() {
var deduplicator = new Deduplicator();
- var gg = new Graph(deduplicator);
+ var graph = new Graph(deduplicator);
File file = RESOURCE_LOADER.file("map.osm.pbf");
OsmProvider provider = new OsmProvider(file, true);
OsmModule osmModule = OsmModule
- .of(provider, gg, new DefaultVehicleParkingRepository())
+ .of(
+ provider,
+ graph,
+ new DefaultOsmInfoGraphBuildRepository(),
+ new DefaultVehicleParkingRepository()
+ )
.withAreaVisibility(true)
.build();
osmModule.buildGraph();
// Kamiennogorska at south end of segment
- Vertex v1 = gg.getVertex(VertexLabel.osm(280592578));
+ Vertex v1 = graph.getVertex(VertexLabel.osm(280592578));
// Kamiennogorska at Mariana Smoluchowskiego
- Vertex v2 = gg.getVertex(VertexLabel.osm(288969929));
+ Vertex v2 = graph.getVertex(VertexLabel.osm(288969929));
// Mariana Smoluchowskiego, north end
- Vertex v3 = gg.getVertex(VertexLabel.osm(280107802));
+ Vertex v3 = graph.getVertex(VertexLabel.osm(280107802));
// Mariana Smoluchowskiego, south end (of segment connected to v2)
- Vertex v4 = gg.getVertex(VertexLabel.osm(288970952));
+ Vertex v4 = graph.getVertex(VertexLabel.osm(288970952));
assertNotNull(v1);
assertNotNull(v2);
@@ -117,9 +123,11 @@ public void testBuildGraphDetailed() {
var gg = new Graph(deduplicator);
File file = RESOURCE_LOADER.file("NYC_small.osm.pbf");
- OsmProvider provider = new OsmProvider(file, true);
- OsmModule osmModule = OsmModule
- .of(provider, gg, new DefaultVehicleParkingRepository())
+ var provider = new OsmProvider(file, true);
+ var osmInfoRepository = new DefaultOsmInfoGraphBuildRepository();
+ var vehicleParkingRepository = new DefaultVehicleParkingRepository();
+ var osmModule = OsmModule
+ .of(provider, gg, osmInfoRepository, vehicleParkingRepository)
.withAreaVisibility(true)
.build();
@@ -315,7 +323,14 @@ void testBarrierAtEnd() {
File file = RESOURCE_LOADER.file("accessno-at-end.pbf");
OsmProvider provider = new OsmProvider(file, false);
- OsmModule loader = OsmModule.of(provider, graph, new DefaultVehicleParkingRepository()).build();
+ OsmModule loader = OsmModule
+ .of(
+ provider,
+ graph,
+ new DefaultOsmInfoGraphBuildRepository(),
+ new DefaultVehicleParkingRepository()
+ )
+ .build();
loader.buildGraph();
Vertex start = graph.getVertex(VertexLabel.osm(1));
@@ -339,7 +354,7 @@ private BuildResult buildParkingLots() {
.map(f -> new OsmProvider(f, false))
.toList();
var module = OsmModule
- .of(providers, graph, service)
+ .of(providers, graph, new DefaultOsmInfoGraphBuildRepository(), service)
.withStaticParkAndRide(true)
.withStaticBikeParkAndRide(true)
.build();
@@ -363,10 +378,12 @@ private void testBuildingAreas(boolean skipVisibility) {
var graph = new Graph(deduplicator);
File file = RESOURCE_LOADER.file("usf_area.osm.pbf");
- OsmProvider provider = new OsmProvider(file, false);
+ var provider = new OsmProvider(file, false);
+ var osmInfoRepository = new DefaultOsmInfoGraphBuildRepository();
+ var vehicleParkingRepository = new DefaultVehicleParkingRepository();
- OsmModule loader = OsmModule
- .of(provider, graph, new DefaultVehicleParkingRepository())
+ var loader = OsmModule
+ .of(provider, graph, osmInfoRepository, vehicleParkingRepository)
.withAreaVisibility(!skipVisibility)
.build();
diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java
index f952bf90710..97ccbdd7719 100644
--- a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java
+++ b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/PlatformLinkerTest.java
@@ -6,6 +6,7 @@
import org.junit.jupiter.api.Test;
import org.opentripplanner.osm.OsmProvider;
import org.opentripplanner.routing.graph.Graph;
+import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository;
import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository;
import org.opentripplanner.street.model.edge.AreaEdge;
import org.opentripplanner.street.model.vertex.Vertex;
@@ -24,20 +25,25 @@ public void testLinkEntriesToPlatforms() {
var stairsEndpointLabel = VertexLabel.osm(1028861028);
var deduplicator = new Deduplicator();
- var gg = new Graph(deduplicator);
+ var graph = new Graph(deduplicator);
File file = ResourceLoader.of(this).file("skoyen.osm.pbf");
OsmProvider provider = new OsmProvider(file, false);
OsmModule osmModule = OsmModule
- .of(provider, gg, new DefaultVehicleParkingRepository())
+ .of(
+ provider,
+ graph,
+ new DefaultOsmInfoGraphBuildRepository(),
+ new DefaultVehicleParkingRepository()
+ )
.withPlatformEntriesLinking(true)
.build();
osmModule.buildGraph();
- Vertex stairsEndpoint = gg.getVertex(stairsEndpointLabel);
+ Vertex stairsEndpoint = graph.getVertex(stairsEndpointLabel);
// verify outgoing links
assertTrue(stairsEndpoint.getOutgoing().stream().anyMatch(AreaEdge.class::isInstance));
diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java
index ffc1f661dcc..1b8f7c9c58a 100644
--- a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java
+++ b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/TriangleInequalityTest.java
@@ -21,6 +21,7 @@
import org.opentripplanner.routing.api.request.request.filter.AllowAllTransitFilter;
import org.opentripplanner.routing.api.request.request.filter.TransitFilter;
import org.opentripplanner.routing.graph.Graph;
+import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository;
import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository;
import org.opentripplanner.street.model.edge.Edge;
import org.opentripplanner.street.model.vertex.Vertex;
@@ -52,7 +53,12 @@ public static void onlyOnce() {
File file = ResourceLoader.of(TriangleInequalityTest.class).file("NYC_small.osm.pbf");
OsmProvider provider = new OsmProvider(file, true);
OsmModule osmModule = OsmModule
- .of(provider, graph, new DefaultVehicleParkingRepository())
+ .of(
+ provider,
+ graph,
+ new DefaultOsmInfoGraphBuildRepository(),
+ new DefaultVehicleParkingRepository()
+ )
.withAreaVisibility(true)
.build();
osmModule.buildGraph();
diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java
index 103dafa61b9..22b486d7cd6 100644
--- a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java
+++ b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/UnconnectedAreasTest.java
@@ -13,6 +13,7 @@
import org.opentripplanner.graph_builder.module.TestStreetLinkerModule;
import org.opentripplanner.osm.OsmProvider;
import org.opentripplanner.routing.graph.Graph;
+import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository;
import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository;
import org.opentripplanner.street.model.edge.StreetVehicleParkingLink;
import org.opentripplanner.street.model.edge.VehicleParkingEdge;
@@ -163,7 +164,12 @@ private Graph buildOsmGraph(String osmFileName, DataImportIssueStore issueStore)
var timetableRepository = new TimetableRepository(siteRepository, deduplicator);
OsmProvider provider = new OsmProvider(RESOURCE_LOADER.file(osmFileName), false);
OsmModule loader = OsmModule
- .of(provider, graph, new DefaultVehicleParkingRepository())
+ .of(
+ provider,
+ graph,
+ new DefaultOsmInfoGraphBuildRepository(),
+ new DefaultVehicleParkingRepository()
+ )
.withIssueStore(issueStore)
.withAreaVisibility(true)
.withStaticParkAndRide(true)
diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java
index 138c3e67181..835c5a7fbcb 100644
--- a/application/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java
+++ b/application/src/test/java/org/opentripplanner/graph_builder/module/osm/UnroutableTest.java
@@ -10,6 +10,7 @@
import org.opentripplanner.routing.api.request.RouteRequest;
import org.opentripplanner.routing.api.request.StreetMode;
import org.opentripplanner.routing.graph.Graph;
+import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository;
import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository;
import org.opentripplanner.street.model.edge.Edge;
import org.opentripplanner.street.model.vertex.Vertex;
@@ -39,7 +40,12 @@ public void setUp() throws Exception {
var osmDataFile = ResourceLoader.of(UnroutableTest.class).file("bridge_construction.osm.pbf");
OsmProvider provider = new OsmProvider(osmDataFile, true);
OsmModule osmBuilder = OsmModule
- .of(provider, graph, new DefaultVehicleParkingRepository())
+ .of(
+ provider,
+ graph,
+ new DefaultOsmInfoGraphBuildRepository(),
+ new DefaultVehicleParkingRepository()
+ )
.withAreaVisibility(true)
.build();
osmBuilder.buildGraph();
diff --git a/application/src/test/java/org/opentripplanner/routing/graph/GraphSerializationTest.java b/application/src/test/java/org/opentripplanner/routing/graph/GraphSerializationTest.java
index 400a9eba2ba..9ccd6177cfd 100644
--- a/application/src/test/java/org/opentripplanner/routing/graph/GraphSerializationTest.java
+++ b/application/src/test/java/org/opentripplanner/routing/graph/GraphSerializationTest.java
@@ -23,6 +23,8 @@
import org.opentripplanner.ext.emissions.EmissionsDataModel;
import org.opentripplanner.framework.geometry.HashGridSpatialIndex;
import org.opentripplanner.graph_builder.issue.api.DataImportIssueSummary;
+import org.opentripplanner.service.osminfo.OsmInfoGraphBuildRepository;
+import org.opentripplanner.service.osminfo.internal.DefaultOsmInfoGraphBuildRepository;
import org.opentripplanner.service.vehicleparking.VehicleParkingRepository;
import org.opentripplanner.service.vehicleparking.internal.DefaultVehicleParkingRepository;
import org.opentripplanner.service.worldenvelope.WorldEnvelopeRepository;
@@ -67,11 +69,13 @@ public class GraphSerializationTest {
@Test
public void testRoundTripSerializationForGTFSGraph() throws Exception {
TestOtpModel model = ConstantsForTests.buildNewPortlandGraph(true);
+ var osmGraphBuildRepository = new DefaultOsmInfoGraphBuildRepository();
var weRepo = new DefaultWorldEnvelopeRepository();
var emissionsDataModel = new EmissionsDataModel();
var parkingRepository = new DefaultVehicleParkingRepository();
testRoundTrip(
model.graph(),
+ osmGraphBuildRepository,
model.timetableRepository(),
weRepo,
parkingRepository,
@@ -85,11 +89,13 @@ public void testRoundTripSerializationForGTFSGraph() throws Exception {
@Test
public void testRoundTripSerializationForNetexGraph() throws Exception {
TestOtpModel model = ConstantsForTests.buildNewMinimalNetexGraph();
+ var osmGraphBuildRepository = new DefaultOsmInfoGraphBuildRepository();
var worldEnvelopeRepository = new DefaultWorldEnvelopeRepository();
var emissionsDataModel = new EmissionsDataModel();
var parkingRepository = new DefaultVehicleParkingRepository();
testRoundTrip(
model.graph(),
+ osmGraphBuildRepository,
model.timetableRepository(),
worldEnvelopeRepository,
parkingRepository,
@@ -191,6 +197,7 @@ private static void assertNoDifferences(Graph g1, Graph g2) {
*/
private void testRoundTrip(
Graph originalGraph,
+ OsmInfoGraphBuildRepository osmInfoGraphBuildRepository,
TimetableRepository originalTimetableRepository,
WorldEnvelopeRepository worldEnvelopeRepository,
VehicleParkingRepository vehicleParkingRepository,
@@ -202,6 +209,7 @@ private void testRoundTrip(
streetLimitationParameters.initMaxCarSpeed(40);
SerializedGraphObject serializedObj = new SerializedGraphObject(
originalGraph,
+ osmInfoGraphBuildRepository,
originalTimetableRepository,
worldEnvelopeRepository,
vehicleParkingRepository,
diff --git a/application/src/test/resources/org/opentripplanner/graph_builder/module/moorgate.osm.pbf b/application/src/test/resources/org/opentripplanner/graph_builder/module/moorgate.osm.pbf
new file mode 100644
index 00000000000..ee95a0e7c51
Binary files /dev/null and b/application/src/test/resources/org/opentripplanner/graph_builder/module/moorgate.osm.pbf differ