use of org.opentripplanner.routing.location.TemporaryStreetLocation in project OpenTripPlanner by opentripplanner.
the class StreetVertexIndexServiceImpl method createTemporaryStreetLocation.
/**
* 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 graph
*
* @param label
* @param name
* @param edges A collection of nearby edges, which represent one street.
* @param nearestPoint
*
* @return the new TemporaryStreetLocation
*/
public static TemporaryStreetLocation createTemporaryStreetLocation(Graph graph, String label, I18NString name, Iterable<StreetEdge> edges, Coordinate nearestPoint, boolean endVertex) {
boolean wheelchairAccessible = false;
TemporaryStreetLocation location = new TemporaryStreetLocation(label, nearestPoint, name, endVertex);
for (StreetEdge street : edges) {
Vertex fromv = street.getFromVertex();
Vertex tov = street.getToVertex();
wheelchairAccessible |= ((StreetEdge) 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) {
new TemporaryFreeEdge(edgeLocation, location);
} else {
new TemporaryFreeEdge(location, edgeLocation);
}
} else if (SphericalDistanceLibrary.distance(nearestPoint, tov.getCoordinate()) < 1) {
// no need to link to area edges caught on-end
edgeLocation = tov;
if (endVertex) {
new TemporaryFreeEdge(edgeLocation, location);
} else {
new TemporaryFreeEdge(location, edgeLocation);
}
} else {
// location is somewhere in the middle of the edge.
edgeLocation = location;
// creates links from street head -> location -> street tail.
createHalfLocation(location, name, nearestPoint, street, endVertex);
}
}
location.setWheelchairAccessible(wheelchairAccessible);
return location;
}
use of org.opentripplanner.routing.location.TemporaryStreetLocation in project OpenTripPlanner by opentripplanner.
the class SimpleStreetSplitter method getClosestVertex.
/**
* Used to link origin and destination points to graph non destructively.
*
* Split edges don't replace existing ones and only temporary edges and vertices are created.
*
* Will throw ThrivialPathException if origin and destination Location are on the same edge
*
* @param location
* @param options
* @param endVertex true if this is destination vertex
* @return
*/
public Vertex getClosestVertex(GenericLocation location, RoutingRequest options, boolean endVertex) {
if (destructiveSplitting) {
throw new RuntimeException("Origin and destination search is used with destructive splitting. Something is wrong!");
}
if (endVertex) {
LOG.debug("Finding end vertex for {}", location);
} else {
LOG.debug("Finding start vertex for {}", location);
}
Coordinate coord = location.getCoordinate();
// TODO: add nice name
String name;
if (location.name == null || location.name.isEmpty()) {
if (endVertex) {
name = "Destination";
} else {
name = "Origin";
}
} else {
name = location.name;
}
TemporaryStreetLocation closest = new TemporaryStreetLocation(UUID.randomUUID().toString(), coord, new NonLocalizedString(name), endVertex);
TraverseMode nonTransitMode = TraverseMode.WALK;
// It can be null in tests
if (options != null) {
TraverseModeSet modes = options.modes;
if (modes.getCar())
// for park and ride we will start in car mode and walk to the end vertex
if (endVertex && (options.parkAndRide || options.kissAndRide)) {
nonTransitMode = TraverseMode.WALK;
} else {
nonTransitMode = TraverseMode.CAR;
}
else if (modes.getWalk())
nonTransitMode = TraverseMode.WALK;
else if (modes.getBicycle())
nonTransitMode = TraverseMode.BICYCLE;
}
if (!link(closest, nonTransitMode, options)) {
LOG.warn("Couldn't link {}", location);
}
return closest;
}
use of org.opentripplanner.routing.location.TemporaryStreetLocation in project OpenTripPlanner by opentripplanner.
the class SimpleStreetSplitter method link.
/**
* Link this vertex into the graph
*/
public boolean link(Vertex vertex, TraverseMode traverseMode, RoutingRequest options) {
// find nearby street edges
// TODO: we used to use an expanding-envelope search, which is more efficient in
// dense areas. but first let's see how inefficient this is. I suspect it's not too
// bad and the gains in simplicity are considerable.
final double radiusDeg = SphericalDistanceLibrary.metersToDegrees(MAX_SEARCH_RADIUS_METERS);
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);
// Expand more in the longitude direction than the latitude direction to account for converging meridians.
env.expandBy(radiusDeg / xscale, radiusDeg);
final double DUPLICATE_WAY_EPSILON_DEGREES = SphericalDistanceLibrary.metersToDegrees(DUPLICATE_WAY_EPSILON_METERS);
final TraverseModeSet traverseModeSet;
if (traverseMode == TraverseMode.BICYCLE) {
traverseModeSet = new TraverseModeSet(traverseMode, TraverseMode.WALK);
} else {
traverseModeSet = new TraverseModeSet(traverseMode);
}
// We sort the list of candidate edges by distance to the stop
// This should remove any issues with things coming out of the spatial index in different orders
// Then we link to everything that is within DUPLICATE_WAY_EPSILON_METERS of of the best distance
// so that we capture back edges and duplicate ways.
List<StreetEdge> candidateEdges = idx.query(env).stream().filter(streetEdge -> streetEdge instanceof StreetEdge).map(edge -> (StreetEdge) edge).filter(edge -> edge.canTraverse(traverseModeSet) && // only link to edges still in the graph.
edge.getToVertex().getIncoming().contains(edge)).collect(Collectors.toList());
// Make a map of distances to all edges.
final TIntDoubleMap distances = new TIntDoubleHashMap();
for (StreetEdge e : candidateEdges) {
distances.put(e.getId(), distance(vertex, e, xscale));
}
// Sort the list.
Collections.sort(candidateEdges, (o1, o2) -> {
double diff = distances.get(o1.getId()) - distances.get(o2.getId());
// A Comparator must return an integer but our distances are doubles.
if (diff < 0)
return -1;
if (diff > 0)
return 1;
return 0;
});
// find the closest candidate edges
if (candidateEdges.isEmpty() || distances.get(candidateEdges.get(0).getId()) > radiusDeg) {
// We only link to stops if we are searching for origin/destination and for that we need transitStopIndex.
if (destructiveSplitting || transitStopIndex == null) {
return false;
}
LOG.debug("No street edge was found for {}", vertex);
// We search for closest stops (since this is only used in origin/destination linking if no edges were found)
// in the same way the closest edges are found.
List<TransitStop> candidateStops = new ArrayList<>();
transitStopIndex.query(env).forEach(candidateStop -> candidateStops.add((TransitStop) candidateStop));
final TIntDoubleMap stopDistances = new TIntDoubleHashMap();
for (TransitStop t : candidateStops) {
stopDistances.put(t.getIndex(), distance(vertex, t, xscale));
}
Collections.sort(candidateStops, (o1, o2) -> {
double diff = stopDistances.get(o1.getIndex()) - stopDistances.get(o2.getIndex());
if (diff < 0) {
return -1;
}
if (diff > 0) {
return 1;
}
return 0;
});
if (candidateStops.isEmpty() || stopDistances.get(candidateStops.get(0).getIndex()) > radiusDeg) {
LOG.debug("Stops aren't close either!");
return false;
} else {
List<TransitStop> bestStops = Lists.newArrayList();
// Add stops until there is a break of epsilon meters.
// we do this to enforce determinism. if there are a lot of stops that are all extremely close to each other,
// we want to be sure that we deterministically link to the same ones every time. Any hard cutoff means things can
// fall just inside or beyond the cutoff depending on floating-point operations.
int i = 0;
do {
bestStops.add(candidateStops.get(i++));
} while (i < candidateStops.size() && stopDistances.get(candidateStops.get(i).getIndex()) - stopDistances.get(candidateStops.get(i - 1).getIndex()) < DUPLICATE_WAY_EPSILON_DEGREES);
for (TransitStop stop : bestStops) {
LOG.debug("Linking vertex to stop: {}", stop.getName());
makeTemporaryEdges((TemporaryStreetLocation) vertex, stop);
}
return true;
}
} else {
// find the best edges
List<StreetEdge> bestEdges = Lists.newArrayList();
// add edges until there is a break of epsilon meters.
// we do this to enforce determinism. if there are a lot of edges that are all extremely close to each other,
// we want to be sure that we deterministically link to the same ones every time. Any hard cutoff means things can
// fall just inside or beyond the cutoff depending on floating-point operations.
int i = 0;
do {
bestEdges.add(candidateEdges.get(i++));
} while (i < candidateEdges.size() && distances.get(candidateEdges.get(i).getId()) - distances.get(candidateEdges.get(i - 1).getId()) < DUPLICATE_WAY_EPSILON_DEGREES);
for (StreetEdge edge : bestEdges) {
link(vertex, edge, xscale, options);
}
// Warn if a linkage was made, but the linkage was suspiciously long.
if (vertex instanceof TransitStop) {
double distanceDegreesLatitude = distances.get(candidateEdges.get(0).getId());
int distanceMeters = (int) SphericalDistanceLibrary.degreesLatitudeToMeters(distanceDegreesLatitude);
if (distanceMeters > WARNING_DISTANCE_METERS) {
// Registering an annotation but not logging because tests produce thousands of these warnings.
graph.addBuilderAnnotation(new StopLinkedTooFar((TransitStop) vertex, distanceMeters));
}
}
return true;
}
}
use of org.opentripplanner.routing.location.TemporaryStreetLocation in project OpenTripPlanner by opentripplanner.
the class PartialStreetEdgeTest method testTraversalOfSubdividedEdge.
@Test
public void testTraversalOfSubdividedEdge() {
Coordinate nearestPoint = new Coordinate(0.5, 2.0);
List<StreetEdge> edges = new ArrayList<StreetEdge>();
edges.add(e2);
TemporaryStreetLocation end = StreetVertexIndexServiceImpl.createTemporaryStreetLocation(_graph, "middle of e2", new NonLocalizedString("foo"), edges, nearestPoint, true);
TemporaryStreetLocation start = StreetVertexIndexServiceImpl.createTemporaryStreetLocation(_graph, "middle of e2", new NonLocalizedString("foo"), edges, nearestPoint, false);
RoutingRequest options = new RoutingRequest();
options.setMode(TraverseMode.CAR);
options.setRoutingContext(_graph, v1, v2);
// All intersections take 10 minutes - we'll notice if one isn't counted.
double turnDurationSecs = 10.0 * 60.0;
options.traversalCostModel = (new DummyCostModel(turnDurationSecs));
options.turnReluctance = (1.0);
State s0 = new State(options);
State s1 = e1.traverse(s0);
State s2 = e2.traverse(s1);
State s3 = e3.traverse(s2);
Edge partialE2First = end.getIncoming().iterator().next();
Edge partialE2Second = start.getOutgoing().iterator().next();
State partialS0 = new State(options);
State partialS1 = e1.traverse(partialS0);
State partialS2A = partialE2First.traverse(partialS1);
State partialS2B = partialE2Second.traverse(partialS2A);
State partialS3 = e3.traverse(partialS2B);
// Should start at the same time.
assertEquals(s0.getTimeSeconds(), partialS0.getTimeSeconds());
// Time and cost should be the same up to a rounding difference.
assertTrue(Math.abs(s3.getTimeSeconds() - partialS3.getTimeSeconds()) <= 1);
assertTrue(Math.abs(s3.getElapsedTimeSeconds() - partialS3.getElapsedTimeSeconds()) <= 1);
assertTrue(Math.abs(s3.getWeight() - partialS3.getWeight()) <= 1);
// All intersections take 0 seconds now.
options.traversalCostModel = (new DummyCostModel(0.0));
State s0NoCost = new State(options);
State s1NoCost = e1.traverse(s0NoCost);
State s2NoCost = e2.traverse(s1NoCost);
State s3NoCost = e3.traverse(s2NoCost);
State partialS0NoCost = new State(options);
State partialS1NoCost = e1.traverse(partialS0NoCost);
State partialS2ANoCost = partialE2First.traverse(partialS1NoCost);
State partialS2BNoCost = partialE2Second.traverse(partialS2ANoCost);
State partialS3NoCost = e3.traverse(partialS2BNoCost);
// Time and cost should be the same up to a rounding difference.
assertTrue(Math.abs(s3NoCost.getTimeSeconds() - partialS3NoCost.getTimeSeconds()) <= 1);
assertTrue(Math.abs(s3NoCost.getElapsedTimeSeconds() - partialS3NoCost.getElapsedTimeSeconds()) <= 1);
assertTrue(Math.abs(s3NoCost.getWeight() - partialS3NoCost.getWeight()) <= 1);
// Difference in duration and weight between now and before should be
// entirely due to the crossing of 2 intersections at v2 and v3.
double expectedDifference = 2 * 10 * 60.0;
double durationDiff = s3.getTimeSeconds() - s3NoCost.getTimeSeconds();
double partialDurationDiff = partialS3.getTimeSeconds() - partialS3NoCost.getTimeSeconds();
assertTrue(Math.abs(durationDiff - expectedDifference) <= 1);
assertTrue(Math.abs(partialDurationDiff - expectedDifference) <= 1);
// Turn reluctance is 1.0, so weight == duration.
double weightDiff = s3.getWeight() - s3NoCost.getWeight();
double partialWeightDiff = partialS3.getWeight() - partialS3NoCost.getWeight();
assertTrue(Math.abs(weightDiff - expectedDifference) <= 1);
assertTrue(Math.abs(partialWeightDiff - expectedDifference) <= 1);
}
use of org.opentripplanner.routing.location.TemporaryStreetLocation in project OpenTripPlanner by opentripplanner.
the class TestHalfEdges method testHalfEdges.
@Test
public void testHalfEdges() {
// the shortest half-edge from the start vertex takes you down, but the shortest total path
// is up and over
int nVertices = graph.getVertices().size();
int nEdges = graph.getEdges().size();
RoutingRequest options = new RoutingRequest();
HashSet<Edge> turns = new HashSet<Edge>();
turns.add(left);
turns.add(leftBack);
TemporaryStreetLocation start = StreetVertexIndexServiceImpl.createTemporaryStreetLocation(graph, "start", new NonLocalizedString("start"), filter(turns, StreetEdge.class), new LinearLocation(0, 0.4).getCoordinate(left.getGeometry()), false);
HashSet<Edge> endTurns = new HashSet<Edge>();
endTurns.add(right);
endTurns.add(rightBack);
TemporaryStreetLocation end = StreetVertexIndexServiceImpl.createTemporaryStreetLocation(graph, "end", new NonLocalizedString("end"), filter(endTurns, StreetEdge.class), new LinearLocation(0, 0.8).getCoordinate(right.getGeometry()), true);
assertTrue(start.getX() < end.getX());
assertTrue(start.getY() < end.getY());
Collection<Edge> edges = end.getIncoming();
assertEquals(2, edges.size());
long startTime = TestUtils.dateInSeconds("America/New_York", 2009, 11, 1, 12, 34, 25);
options.dateTime = startTime;
options.setRoutingContext(graph, br, end);
options.setMaxWalkDistance(Double.MAX_VALUE);
ShortestPathTree spt1 = aStar.getShortestPathTree(options);
GraphPath pathBr = spt1.getPath(end, false);
assertNotNull("There must be a path from br to end", pathBr);
options.setRoutingContext(graph, tr, end);
ShortestPathTree spt2 = aStar.getShortestPathTree(options);
GraphPath pathTr = spt2.getPath(end, false);
assertNotNull("There must be a path from tr to end", pathTr);
assertTrue("path from bottom to end must be longer than path from top to end", pathBr.getWeight() > pathTr.getWeight());
options.setRoutingContext(graph, start, end);
ShortestPathTree spt = aStar.getShortestPathTree(options);
GraphPath path = spt.getPath(end, false);
assertNotNull("There must be a path from start to end", path);
// the bottom is not part of the shortest path
for (State s : path.states) {
assertNotSame(s.getVertex(), graph.getVertex("bottom"));
assertNotSame(s.getVertex(), graph.getVertex("bottomBack"));
}
options.setArriveBy(true);
options.setRoutingContext(graph, start, end);
spt = aStar.getShortestPathTree(options);
path = spt.getPath(start, false);
assertNotNull("There must be a path from start to end (looking back)", path);
// the bottom edge is not part of the shortest path
for (State s : path.states) {
assertNotSame(s.getVertex(), graph.getVertex("bottom"));
assertNotSame(s.getVertex(), graph.getVertex("bottomBack"));
}
// Number of vertices and edges should be the same as before after a cleanup.
options.cleanup();
assertEquals(nVertices, graph.getVertices().size());
assertEquals(nEdges, graph.getEdges().size());
/*
* Now, the right edge is not bikeable. But the user can walk their bike. So here are some tests that prove (a) that walking bikes works, but
* that (b) it is not preferred to riding a tiny bit longer.
*/
options = new RoutingRequest(new TraverseModeSet(TraverseMode.BICYCLE));
start = StreetVertexIndexServiceImpl.createTemporaryStreetLocation(graph, "start1", new NonLocalizedString("start1"), filter(turns, StreetEdge.class), new LinearLocation(0, 0.95).getCoordinate(top.getGeometry()), false);
end = StreetVertexIndexServiceImpl.createTemporaryStreetLocation(graph, "end1", new NonLocalizedString("end1"), filter(turns, StreetEdge.class), new LinearLocation(0, 0.95).getCoordinate(bottom.getGeometry()), true);
options.setRoutingContext(graph, start, end);
spt = aStar.getShortestPathTree(options);
path = spt.getPath(start, false);
assertNotNull("There must be a path from top to bottom along the right", path);
// the left edge is not part of the shortest path (even though the bike must be walked along the right)
for (State s : path.states) {
assertNotSame(s.getVertex(), graph.getVertex("left"));
assertNotSame(s.getVertex(), graph.getVertex("leftBack"));
}
// Number of vertices and edges should be the same as before after a cleanup.
options.cleanup();
assertEquals(nVertices, graph.getVertices().size());
assertEquals(nEdges, graph.getEdges().size());
start = StreetVertexIndexServiceImpl.createTemporaryStreetLocation(graph, "start2", new NonLocalizedString("start2"), filter(turns, StreetEdge.class), new LinearLocation(0, 0.55).getCoordinate(top.getGeometry()), false);
end = StreetVertexIndexServiceImpl.createTemporaryStreetLocation(graph, "end2", new NonLocalizedString("end2"), filter(turns, StreetEdge.class), new LinearLocation(0, 0.55).getCoordinate(bottom.getGeometry()), true);
options.setRoutingContext(graph, start, end);
spt = aStar.getShortestPathTree(options);
path = spt.getPath(start, false);
assertNotNull("There must be a path from top to bottom", path);
// the right edge is not part of the shortest path, e
for (State s : path.states) {
assertNotSame(s.getVertex(), graph.getVertex("right"));
assertNotSame(s.getVertex(), graph.getVertex("rightBack"));
}
// Number of vertices and edges should be the same as before after a cleanup.
options.cleanup();
assertEquals(nVertices, graph.getVertices().size());
assertEquals(nEdges, graph.getEdges().size());
}
Aggregations