All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentripplanner.routing.graph.index.StreetIndex Maven / Gradle / Ivy

The newest version!
package org.opentripplanner.routing.graph.index;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.LineString;
import org.opentripplanner.framework.geometry.GeometryUtils;
import org.opentripplanner.framework.geometry.HashGridSpatialIndex;
import org.opentripplanner.framework.geometry.SphericalDistanceLibrary;
import org.opentripplanner.framework.geometry.SplitLineString;
import org.opentripplanner.framework.i18n.I18NString;
import org.opentripplanner.framework.i18n.LocalizedString;
import org.opentripplanner.framework.i18n.NonLocalizedString;
import org.opentripplanner.model.GenericLocation;
import org.opentripplanner.routing.api.request.StreetMode;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.linking.DisposableEdgeCollection;
import org.opentripplanner.routing.linking.Scope;
import org.opentripplanner.routing.linking.VertexLinker;
import org.opentripplanner.street.model.edge.Edge;
import org.opentripplanner.street.model.edge.LinkingDirection;
import org.opentripplanner.street.model.edge.StreetEdge;
import org.opentripplanner.street.model.edge.TemporaryFreeEdge;
import org.opentripplanner.street.model.edge.TemporaryPartialStreetEdge;
import org.opentripplanner.street.model.edge.TemporaryPartialStreetEdgeBuilder;
import org.opentripplanner.street.model.vertex.StationCentroidVertex;
import org.opentripplanner.street.model.vertex.StreetVertex;
import org.opentripplanner.street.model.vertex.TemporaryStreetLocation;
import org.opentripplanner.street.model.vertex.TransitStopVertex;
import org.opentripplanner.street.model.vertex.Vertex;
import org.opentripplanner.street.search.TraverseMode;
import org.opentripplanner.street.search.TraverseModeSet;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.model.site.RegularStop;
import org.opentripplanner.transit.service.SiteRepository;
import org.opentripplanner.utils.logging.ProgressTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Indexes all edges and transit vertices of the graph spatially. Has a variety of query methods
 * used during network linking and trip planning.
 * 

* Creates a TemporaryStreetLocation representing a location on a street that's not at an * intersection, based on input latitude and longitude. Instantiating this class is expensive, * because it creates a spatial index of all of the intersections in the graph. */ public class StreetIndex { private static final Logger LOG = LoggerFactory.getLogger(StreetIndex.class); private final SiteRepository siteRepository; private final VertexLinker vertexLinker; private final Map transitStopVertices; /** * This list contains transitStationVertices for the stations that are configured to route to centroid */ private final Map stationCentroidVertices; private final EdgeSpatialIndex edgeSpatialIndex; private final HashGridSpatialIndex verticesTree; /** * Should only be called by the graph. */ public StreetIndex(Graph graph, SiteRepository siteRepository) { this.siteRepository = siteRepository; this.edgeSpatialIndex = new EdgeSpatialIndex(); this.verticesTree = new HashGridSpatialIndex<>(); this.vertexLinker = new VertexLinker(graph, siteRepository, edgeSpatialIndex); this.transitStopVertices = toImmutableMap(graph.getVerticesOfType(TransitStopVertex.class)); this.stationCentroidVertices = createStationCentroidVertexMap(graph); postSetup(graph.getVertices()); } /** * Creates a TemporaryStreetLocation on the given street (set of PlainStreetEdges). How far along * is controlled by the location parameter, which represents a distance along the edge between 0 * (the from vertex) and 1 (the to vertex). * * @param edges A collection of nearby edges, which represent one street. * @return the new TemporaryStreetLocation */ public static TemporaryStreetLocation createTemporaryStreetLocationForTest( String label, I18NString name, Iterable edges, Coordinate nearestPoint, boolean endVertex, DisposableEdgeCollection tempEdges ) { boolean wheelchairAccessible = false; TemporaryStreetLocation location = new TemporaryStreetLocation(nearestPoint, name); for (StreetEdge street : edges) { Vertex fromv = street.getFromVertex(); Vertex tov = street.getToVertex(); wheelchairAccessible |= street.isWheelchairAccessible(); /* forward edges and vertices */ Vertex edgeLocation; if (SphericalDistanceLibrary.distance(nearestPoint, fromv.getCoordinate()) < 1) { // no need to link to area edges caught on-end edgeLocation = fromv; if (endVertex) { tempEdges.addEdge(TemporaryFreeEdge.createTemporaryFreeEdge(edgeLocation, location)); } else { tempEdges.addEdge(TemporaryFreeEdge.createTemporaryFreeEdge(location, edgeLocation)); } } else if (SphericalDistanceLibrary.distance(nearestPoint, tov.getCoordinate()) < 1) { // no need to link to area edges caught on-end edgeLocation = tov; if (endVertex) { tempEdges.addEdge(TemporaryFreeEdge.createTemporaryFreeEdge(edgeLocation, location)); } else { tempEdges.addEdge(TemporaryFreeEdge.createTemporaryFreeEdge(location, edgeLocation)); } } else { // creates links from street head -> location -> street tail. createHalfLocationForTest(location, name, nearestPoint, street, endVertex, tempEdges); } } location.setWheelchairAccessible(wheelchairAccessible); return location; } public VertexLinker getVertexLinker() { return vertexLinker; } @Nullable public TransitStopVertex findTransitStopVertices(FeedScopedId stopId) { return transitStopVertices.get(stopId); } /** * Returns the vertices intersecting with the specified envelope. */ public List getVerticesForEnvelope(Envelope envelope) { List vertices = verticesTree.query(envelope); // Here we assume vertices list modifiable vertices.removeIf(v -> !envelope.contains(new Coordinate(v.getLon(), v.getLat()))); return vertices; } /** * Return the edges whose geometry intersect with the specified envelope. Warning: edges disconnected from the graph * will not be indexed. */ public Collection getEdgesForEnvelope(Envelope envelope) { return edgeSpatialIndex .query(envelope, Scope.PERMANENT) .filter( e -> e.isReachableFromGraph() && envelope.intersects(edgeGeometryOrStraightLine(e).getEnvelopeInternal()) ) .toList(); } /** * Gets a set of vertices corresponding to the location provided. It first tries to match one of * the stop or station types by id, and if not successful it uses the coordinates if provided. * * @param endVertex: whether this is a start vertex (if it's false) or end vertex (if it's true) */ @Nullable public Set getStreetVerticesForLocation( GenericLocation location, StreetMode streetMode, boolean endVertex, Set tempEdges ) { // Differentiate between driving and non-driving, as driving is not available from transit stops TraverseMode nonTransitMode = getTraverseModeForLinker(streetMode, endVertex); if (nonTransitMode.isInCar()) { // Fetch coordinate from stop, if not given in request if (location.stopId != null && location.getCoordinate() == null) { var coordinate = siteRepository.getCoordinateById(location.stopId); if (coordinate != null) { location = new GenericLocation( location.label, location.stopId, coordinate.latitude(), coordinate.longitude() ); } } } else { // Check if Stop/StopCollection is found by FeedScopeId if (location.stopId != null) { var streetVertices = getStreetVerticesById(location.stopId); if (!streetVertices.isEmpty()) { return streetVertices; } } } // Check if coordinate is provided and connect it to graph if (location.getCoordinate() != null) { return Set.of( createVertexFromCoordinate( location.getCoordinate(), location.label, streetMode, endVertex, tempEdges ) ); } return null; } @Override public String toString() { return ( getClass().getName() + " -- edgeTree: " + edgeSpatialIndex.toString() + " -- verticesTree: " + verticesTree.toString() ); } /** * Create the appropriate vertex for this coordinate. * * @param endVertex: whether this is a start vertex (if it's false) or end vertex (if it's true) */ public Vertex createVertexForCoordinateForTest( Coordinate location, StreetMode streetMode, boolean endVertex, Set tempEdges ) { return createVertexFromCoordinate(location, null, streetMode, endVertex, tempEdges); } /** * @param id Id of Stop, Station, MultiModalStation or GroupOfStations * @return The associated TransitStopVertex or all underlying TransitStopVertices */ public Set getStopOrChildStopsVertices(FeedScopedId id) { return siteRepository .findStopOrChildStops(id) .stream() .filter(RegularStop.class::isInstance) .map(RegularStop.class::cast) .map(it -> transitStopVertices.get(it.getId())) .collect(Collectors.toSet()); } /** * Get the street vertices for an id. If the id corresponds to a regular stop we will return the * coordinate for the stop. * If the id corresponds to a station we will either return the coordinates of the child stops or * the station centroid if the station is configured to route to centroid. */ public Set getStreetVerticesById(FeedScopedId id) { var stationVertex = stationCentroidVertices.get(id); if (stationVertex != null) { return Set.of(stationVertex); } return Collections.unmodifiableSet(getStopOrChildStopsVertices(id)); } private static void createHalfLocationForTest( TemporaryStreetLocation base, I18NString name, Coordinate nearestPoint, StreetEdge street, boolean endVertex, DisposableEdgeCollection tempEdges ) { StreetVertex tov = (StreetVertex) street.getToVertex(); StreetVertex fromv = (StreetVertex) street.getFromVertex(); LineString geometry = street.getGeometry(); SplitLineString geometries = getGeometry(street, nearestPoint); double totalGeomLength = geometry.getLength(); double lengthRatioIn = geometries.beginning().getLength() / totalGeomLength; double lengthIn = street.getDistanceMeters() * lengthRatioIn; double lengthOut = street.getDistanceMeters() * (1 - lengthRatioIn); if (endVertex) { TemporaryPartialStreetEdge tpse = new TemporaryPartialStreetEdgeBuilder() .withParentEdge(street) .withFromVertex(fromv) .withToVertex(base) .withGeometry(geometries.beginning()) .withName(name) .withMeterLength(lengthIn) .withMotorVehicleNoThruTraffic(street.isMotorVehicleNoThruTraffic()) .withBicycleNoThruTraffic(street.isBicycleNoThruTraffic()) .withWalkNoThruTraffic(street.isWalkNoThruTraffic()) .withLink(street.isLink()) .buildAndConnect(); tempEdges.addEdge(tpse); } else { TemporaryPartialStreetEdge tpse = new TemporaryPartialStreetEdgeBuilder() .withParentEdge(street) .withFromVertex(base) .withToVertex(tov) .withGeometry(geometries.ending()) .withName(name) .withMeterLength(lengthOut) .withLink(street.isLink()) .withMotorVehicleNoThruTraffic(street.isMotorVehicleNoThruTraffic()) .withBicycleNoThruTraffic(street.isBicycleNoThruTraffic()) .withWalkNoThruTraffic(street.isWalkNoThruTraffic()) .buildAndConnect(); tempEdges.addEdge(tpse); } } private static SplitLineString getGeometry(StreetEdge e, Coordinate nearestPoint) { LineString geometry = e.getGeometry(); return GeometryUtils.splitGeometryAtPoint(geometry, nearestPoint); } private static LineString edgeGeometryOrStraightLine(Edge e) { LineString geometry = e.getGeometry(); if (geometry == null) { Coordinate[] coordinates = new Coordinate[] { e.getFromVertex().getCoordinate(), e.getToVertex().getCoordinate(), }; geometry = GeometryUtils.getGeometryFactory().createLineString(coordinates); } return geometry; } private Vertex createVertexFromCoordinate( Coordinate coordinate, @Nullable String label, StreetMode streetMode, boolean endVertex, Set tempEdges ) { if (endVertex) { LOG.debug("Creating end vertex for {}", coordinate); } else { LOG.debug("Creating start vertex for {}", coordinate); } I18NString name; if (label == null || label.isEmpty()) { if (endVertex) { name = new LocalizedString("destination"); } else { name = new LocalizedString("origin"); } } else { name = new NonLocalizedString(label); } var temporaryStreetLocation = new TemporaryStreetLocation(coordinate, name); TraverseMode nonTransitMode = getTraverseModeForLinker(streetMode, endVertex); tempEdges.add( vertexLinker.linkVertexForRequest( temporaryStreetLocation, new TraverseModeSet(nonTransitMode), endVertex ? LinkingDirection.OUTGOING : LinkingDirection.INCOMING, endVertex ? (vertex, streetVertex) -> List.of( TemporaryFreeEdge.createTemporaryFreeEdge( streetVertex, (TemporaryStreetLocation) vertex ) ) : (vertex, streetVertex) -> List.of( TemporaryFreeEdge.createTemporaryFreeEdge( (TemporaryStreetLocation) vertex, streetVertex ) ) ) ); if ( temporaryStreetLocation.getIncoming().isEmpty() && temporaryStreetLocation.getOutgoing().isEmpty() ) { LOG.warn("Couldn't link {}", coordinate); } temporaryStreetLocation.setWheelchairAccessible(true); return temporaryStreetLocation; } private TraverseMode getTraverseModeForLinker(StreetMode streetMode, boolean endVertex) { TraverseMode nonTransitMode = TraverseMode.WALK; // for park and ride we will start in car mode and walk to the end vertex boolean parkAndRideDepart = streetMode == StreetMode.CAR_TO_PARK && !endVertex; boolean onlyCarAvailable = streetMode == StreetMode.CAR; if (onlyCarAvailable || parkAndRideDepart) { nonTransitMode = TraverseMode.CAR; } return nonTransitMode; } private void postSetup(Collection vertices) { var progress = ProgressTracker.track("Index street vertex", 1000, vertices.size()); LOG.info(progress.startMessage()); for (Vertex gv : vertices) { for (Edge e : gv.getOutgoing()) { LineString geometry = edgeGeometryOrStraightLine(e); edgeSpatialIndex.insert(geometry, e, Scope.PERMANENT); } Envelope env = new Envelope(gv.getCoordinate()); verticesTree.insert(env, gv); //noinspection Convert2MethodRef progress.step(m -> LOG.info(m)); } // Trim the sizes of the indices edgeSpatialIndex.compact(); verticesTree.compact(); LOG.info(progress.completeMessage()); } private static Map toImmutableMap( Collection vertices ) { var map = new HashMap(); for (TransitStopVertex it : vertices) { map.put(it.getStop().getId(), it); } return Map.copyOf(map); } private static Map createStationCentroidVertexMap( Graph graph ) { return graph .getVerticesOfType(StationCentroidVertex.class) .stream() .filter(vertex -> vertex.getStation().shouldRouteToCentroid()) .collect(Collectors.toUnmodifiableMap(v -> v.getStation().getId(), v -> v)); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy