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..07c39cc2d8a 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,8 +1,12 @@ 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.stream.Collectors; +import javax.annotation.Nullable; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; import org.opentripplanner.framework.geometry.GeometryUtils; @@ -13,6 +17,8 @@ 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 +30,11 @@ 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.StationElement; import org.opentripplanner.transit.service.TimetableRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,14 +63,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,14 +113,14 @@ public void buildGraph() { } private boolean connectVertexToStop(TransitStopVertex ts, StreetIndex index) { - var stopCode = ts.getStop().getCode(); - var stopId = ts.getStop().getId().getId(); + var stop = ts.getStop(); + var stopCode = stop.getCode(); Envelope envelope = new Envelope(ts.getCoordinate()); double xscale = Math.cos(ts.getCoordinate().y * Math.PI / 180); envelope.expandBy(searchRadiusDegrees / xscale, searchRadiusDegrees); - // if the boarding location is an OSM node it's generated in the OSM processing step but we need + // if the boarding location is a node it's generated in the OSM processing step but we need // link it here var nearbyBoardingLocations = index .getVerticesForEnvelope(envelope) @@ -116,26 +130,14 @@ private boolean connectVertexToStop(TransitStopVertex ts, StreetIndex index) { .collect(Collectors.toSet()); for (var boardingLocation : nearbyBoardingLocations) { - if ( - (stopCode != null && boardingLocation.references.contains(stopCode)) || - boardingLocation.references.contains(stopId) - ) { + if (matchesReference(stop, boardingLocation.references)) { 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) - ); - } + (osmBoardingLocationVertex, splitVertex) -> + getConnectingEdges(boardingLocation, osmBoardingLocationVertex, splitVertex) ); } linkBoardingLocationToStop(ts, stopCode, boardingLocation); @@ -143,9 +145,51 @@ private boolean connectVertexToStop(TransitStopVertex ts, StreetIndex index) { } } - // if the boarding location is an OSM way (an area) then we are generating the vertex here and + // if the boarding location is a non-area way we are finding the vertex representing the + // center of the way, splitting if needed + var nearbyEdges = new HashMap>(); + + for (var edge : index.getEdgesForEnvelope(envelope)) { + 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 label = "platform-centroid/%s".formatted(stop.getId().toString()); + var centroid = platform.geometry().getCentroid(); + var boardingLocation = vertexFactory.osmBoardingLocation( + new Coordinate(centroid.getX(), centroid.getY()), + label, + 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, stopCode, vertex); + } + return true; + } + + // if the boarding location is 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 + var nearbyAreaEdgeList = index .getEdgesForEnvelope(envelope) .stream() .filter(AreaEdge.class::isInstance) @@ -155,18 +199,15 @@ 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 label = "platform-centroid/%s".formatted(stop.getId().toString()); var centroid = edgeList.getGeometry().getCentroid(); var boardingLocation = vertexFactory.osmBoardingLocation( new Coordinate(centroid.getX(), centroid.getY()), @@ -182,6 +223,22 @@ private boolean connectVertexToStop(TransitStopVertex ts, StreetIndex index) { return false; } + 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 +254,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 +267,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..a433f3882e1 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,38 @@ private DisposableEdgeCollection link( return tempEdges; } + 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 +276,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 +312,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..e30e0dd19a7 --- /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 after the OSM Graph Builder is done. + *

+ * 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