Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Process boarding location for linear platforms #6247

Open
wants to merge 12 commits into
base: dev-2.x
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -62,6 +64,7 @@ public static GraphBuilder create(
BuildConfig config,
GraphBuilderDataSources dataSources,
Graph graph,
OsmInfoGraphBuildRepository osmInfoGraphBuildRepository,
TimetableRepository timetableRepository,
WorldEnvelopeRepository worldEnvelopeRepository,
VehicleParkingRepository vehicleParkingService,
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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)
Expand All @@ -116,36 +130,66 @@ 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);
return true;
}
}

// 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<Platform, List<Edge>>();

for (var edge : index.getEdgesForEnvelope(envelope)) {
osmInfoGraphBuildService
.findPlatform(edge)
.ifPresent(platform -> {
if (matchesReference(stop, platform.references())) {
if (!nearbyEdges.containsKey(platform)) {
var list = new ArrayList<Edge>();
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)
Expand All @@ -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()),
Expand All @@ -182,6 +223,22 @@ private boolean connectVertexToStop(TransitStopVertex ts, StreetIndex index) {
return false;
}

private List<Edge> 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<>()
Expand All @@ -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);
Expand All @@ -210,4 +267,11 @@ private void linkBoardingLocationToStop(
boardingLocation.getCoordinate()
);
}

private boolean matchesReference(StationElement<?, ?> stop, Collection<String> references) {
var stopCode = stop.getCode();
var stopId = stop.getId().getId();

return (stopCode != null && references.contains(stopCode)) || references.contains(stopId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@
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;
import org.opentripplanner.street.model.StreetLimitationParameters;
import org.opentripplanner.transit.service.TimetableRepository;

@Singleton
@Component(modules = { GraphBuilderModules.class })
@Component(modules = { GraphBuilderModules.class, OsmInfoGraphBuildServiceModule.class })
public interface GraphBuilderFactory {
//DataImportIssueStore issueStore();
GraphBuilder graphBuilder();
Expand Down Expand Up @@ -80,6 +82,9 @@ interface Builder {
@BindsInstance
Builder timetableRepository(TimetableRepository timetableRepository);

@BindsInstance
Builder osmInfoGraphBuildRepository(OsmInfoGraphBuildRepository osmInfoGraphBuildRepository);

@BindsInstance
Builder worldEnvelopeRepository(WorldEnvelopeRepository worldEnvelopeRepository);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -60,7 +61,8 @@ static OsmModule provideOsmModule(
GraphBuilderDataSources dataSources,
BuildConfig config,
Graph graph,
VehicleParkingRepository parkingService,
OsmInfoGraphBuildRepository osmInfoGraphBuildRepository,
VehicleParkingRepository vehicleParkingRepository,
DataImportIssueStore issueStore,
StreetLimitationParameters streetLimitationParameters
) {
Expand All @@ -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)
Expand Down
Loading
Loading