Skip to content

Commit

Permalink
link linear platforms to stops
Browse files Browse the repository at this point in the history
  • Loading branch information
miklcct committed Nov 13, 2024
1 parent f7ed65d commit 2d423a8
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 36 deletions.
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.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.opentripplanner.framework.geometry.GeometryUtils;
Expand All @@ -17,16 +21,20 @@
import org.opentripplanner.street.model.edge.AreaEdge;
import org.opentripplanner.street.model.edge.BoardingLocationToStopLink;
import org.opentripplanner.street.model.edge.Edge;
import org.opentripplanner.street.model.edge.LinearPlatform;
import org.opentripplanner.street.model.edge.LinearPlatformEdge;
import org.opentripplanner.street.model.edge.NamedArea;
import org.opentripplanner.street.model.edge.StreetEdge;
import org.opentripplanner.street.model.edge.StreetEdgeBuilder;
import org.opentripplanner.street.model.edge.StreetTransitStopLink;
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 @@ -99,14 +107,15 @@ 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();
var stopId = stop.getId().getId();
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 +125,65 @@ 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 nearbyLinearPlatformEdges = new HashMap<LinearPlatform, List<LinearPlatformEdge>>();

for (var edge : index.getEdgesForEnvelope(envelope)) {
if (edge instanceof LinearPlatformEdge platformEdge) {
var platform = platformEdge.platform;
if (matchesReference(stop, platform.references())) {
if (!nearbyLinearPlatformEdges.containsKey(platform)) {
var list = new ArrayList<LinearPlatformEdge>();
list.add(platformEdge);
nearbyLinearPlatformEdges.put(platform, list);
} else {
nearbyLinearPlatformEdges.get(platform).add(platformEdge);
}
}
}
}

for (var platformEdgeList : nearbyLinearPlatformEdges.entrySet()) {
LinearPlatform 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 +193,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 +217,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 +248,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 +261,11 @@ private void linkBoardingLocationToStop(
boardingLocation.getCoordinate()
);
}

private boolean matchesReference(StationElement<?, ?> stop, Set<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 @@ -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;
Expand Down Expand Up @@ -226,20 +227,38 @@ private DisposableEdgeCollection link(
return tempEdges;
}

public Set<StreetVertex> linkToSpecificStreetEdgesPermanently(
Vertex vertex,
TraverseModeSet traverseModes,
LinkingDirection direction,
Set<StreetEdge> 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<StreetVertex> 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);
Expand All @@ -257,6 +276,30 @@ private Set<StreetVertex> 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<StreetVertex> linkToCandidateEdges(
Vertex vertex,
TraverseModeSet traverseModes,
LinkingDirection direction,
Scope scope,
@Nullable DisposableEdgeCollection tempEdges,
List<DistanceTo<StreetEdge>> candidateEdges,
double xscale
) {
if (candidateEdges.isEmpty()) {
return Set.of();
}
Expand All @@ -269,7 +312,7 @@ private Set<StreetVertex> 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());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -12,24 +12,24 @@
*/
public class BoardingLocationToStopLink extends StreetTransitEntityLink<TransitStopVertex> {

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));
}

public static BoardingLocationToStopLink createBoardingLocationToStopLink(
TransitStopVertex fromv,
OsmBoardingLocationVertex tov
StreetVertex tov
) {
return connectToGraph(new BoardingLocationToStopLink(fromv, tov));
}
Expand Down

0 comments on commit 2d423a8

Please sign in to comment.