use of org.opentripplanner.routing.core.RoutingRequest in project OpenTripPlanner by opentripplanner.
the class LIsochrone method computeIsochrone.
/**
* Generic method to compute isochrones. Parse the request, call the adequate builder, and
* return a list of generic isochrone data.
*
* @return
* @throws Exception
*/
public List<IsochroneData> computeIsochrone() throws Exception {
if (debug == null)
debug = false;
if (precisionMeters < 10)
throw new IllegalArgumentException("Too small precisionMeters: " + precisionMeters);
if (offRoadDistanceMeters < 10)
throw new IllegalArgumentException("Too small offRoadDistanceMeters: " + offRoadDistanceMeters);
IsoChroneRequest isoChroneRequest = new IsoChroneRequest(cutoffSecList);
isoChroneRequest.includeDebugGeometry = debug;
isoChroneRequest.precisionMeters = precisionMeters;
isoChroneRequest.offRoadDistanceMeters = offRoadDistanceMeters;
if (coordinateOrigin != null)
isoChroneRequest.coordinateOrigin = new GenericLocation(null, coordinateOrigin).getCoordinate();
RoutingRequest sptRequest = buildRequest();
if (maxTimeSec != null) {
isoChroneRequest.maxTimeSec = maxTimeSec;
} else {
isoChroneRequest.maxTimeSec = isoChroneRequest.maxCutoffSec;
}
Router router = otpServer.getRouter(routerId);
return router.isoChroneSPTRenderer.getIsochrones(isoChroneRequest, sptRequest);
}
use of org.opentripplanner.routing.core.RoutingRequest in project OpenTripPlanner by opentripplanner.
the class PlannerResource method plan.
// We inject info about the incoming request so we can include the incoming query
// parameters in the outgoing response. This is a TriMet requirement.
// Jersey uses @Context to inject internal types and @InjectParam or @Resource for DI objects.
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML + Q, MediaType.TEXT_XML + Q })
public Response plan(@Context UriInfo uriInfo, @Context Request grizzlyRequest) {
/*
* TODO: add Lang / Locale parameter, and thus get localized content (Messages & more...)
* TODO: from/to inputs should be converted / geocoded / etc... here, and maybe send coords
* or vertex ids to planner (or error back to user)
* TODO: org.opentripplanner.routing.module.PathServiceImpl has COOORD parsing. Abstract that
* out so it's used here too...
*/
// Create response object, containing a copy of all request parameters. Maybe they should be in the debug section of the response.
Response response = new Response(uriInfo);
RoutingRequest request = null;
Router router = null;
List<GraphPath> paths = null;
try {
/* Fill in request fields from query parameters via shared superclass method, catching any errors. */
request = super.buildRequest();
router = otpServer.getRouter(request.routerId);
/* Find some good GraphPaths through the OTP Graph. */
// we could also get a persistent router-scoped GraphPathFinder but there's no setup cost here
GraphPathFinder gpFinder = new GraphPathFinder(router);
paths = gpFinder.graphPathFinderEntryPoint(request);
/* Convert the internal GraphPaths to a TripPlan object that is included in an OTP web service Response. */
TripPlan plan = GraphPathToTripPlanConverter.generatePlan(paths, request);
response.setPlan(plan);
} catch (Exception e) {
PlannerError error = new PlannerError(e);
if (!PlannerError.isPlanningError(e.getClass()))
LOG.warn("Error while planning path: ", e);
response.setError(error);
} finally {
if (request != null) {
if (request.rctx != null) {
response.debugOutput = request.rctx.debugOutput;
}
// TODO verify that this cleanup step is being done on Analyst web services
request.cleanup();
}
}
/* Populate up the elevation metadata */
response.elevationMetadata = new ElevationMetadata();
response.elevationMetadata.ellipsoidToGeoidDifference = router.graph.ellipsoidToGeoidDifference;
response.elevationMetadata.geoidElevation = request.geoidElevation;
/* Log this request if such logging is enabled. */
if (request != null && router != null && router.requestLogger != null) {
StringBuilder sb = new StringBuilder();
String clientIpAddress = grizzlyRequest.getRemoteAddr();
// sb.append(LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));
sb.append(clientIpAddress);
sb.append(' ');
sb.append(request.arriveBy ? "ARRIVE" : "DEPART");
sb.append(' ');
sb.append(LocalDateTime.ofInstant(Instant.ofEpochSecond(request.dateTime), ZoneId.systemDefault()));
sb.append(' ');
sb.append(request.modes.getAsStr());
sb.append(' ');
sb.append(request.from.lat);
sb.append(' ');
sb.append(request.from.lng);
sb.append(' ');
sb.append(request.to.lat);
sb.append(' ');
sb.append(request.to.lng);
sb.append(' ');
if (paths != null) {
for (GraphPath path : paths) {
sb.append(path.getDuration());
sb.append(' ');
sb.append(path.getTrips().size());
sb.append(' ');
}
}
router.requestLogger.info(sb.toString());
}
return response;
}
use of org.opentripplanner.routing.core.RoutingRequest in project OpenTripPlanner by opentripplanner.
the class SIsochrone method getIsochrone.
/**
* Calculates walksheds for a given location, based on time given to walk and the walk speed.
*
* Depending on the value for the "output" parameter (i.e. "POINTS", "SHED" or "EDGES"), a
* different type of GeoJSON geometry is returned. If a SHED is requested, then a ConcaveHull
* of the EDGES/roads is returned. If that fails, a ConvexHull will be returned.
* <p>
* The ConcaveHull parameter is set to 0.005 degrees. The offroad walkspeed is assumed to be
* 0.83333 m/sec (= 3km/h) until a road is hit.
* <p>
* Note that the set of EDGES/roads returned as well as POINTS returned may contain duplicates.
* If POINTS are requested, then not the end-points are returned at which the max time is
* reached, but instead all the graph nodes/crossings that are within the time limits.
* <p>
* In case there is no road near by within the given time, then a circle for the walktime limit
* is created and returned for the SHED parameter. Otherwise the edge with the direction
* towards the closest road. Note that the circle is calculated in Euclidian 2D coordinates,
* and distortions towards an ellipse will appear if it is transformed/projected to the user location.
* <p>
* An example request may look like this:
* localhost:8080/otp-rest-servlet/ws/iso?layers=traveltime&styles=mask&batch=true&fromPlace=51.040193121307176
* %2C-114.04471635818481&toPlace
* =51.09098935%2C-113.95179705&time=2012-06-06T08%3A00%3A00&mode=WALK&maxWalkDistance=10000&walkSpeed=1.38&walkTime=10.7&output=EDGES
* Though the first parameters (i) layer, (ii) styles and (iii) batch could be discarded.
*
* @param walkmins Maximum number of minutes to walk.
* @param output Can be set to "POINTS", "SHED" or "EDGES" to return different types of GeoJSON
* geometry. SHED returns a ConcaveHull or ConvexHull of the edges/roads. POINTS returns
* all graph nodes that are within the time limit.
* @return a JSON document containing geometries (either points, lineStrings or a polygon).
* @throws Exception
* @author sstein---geo.uzh.ch
*/
@GET
@Produces({ MediaType.APPLICATION_JSON })
public String getIsochrone(@QueryParam("walkTime") @DefaultValue("15") double walkmins, @QueryParam("output") @DefaultValue("POINTS") String output) throws Exception {
this.debugGeoms = new ArrayList();
this.tooFastTraversedEdgeGeoms = new ArrayList();
RoutingRequest sptRequestA = buildRequest();
String from = sptRequestA.from.toString();
int pos = 1;
float lat = 0;
float lon = 0;
for (String s : from.split(",")) {
if (s.isEmpty()) {
// no location
Response.status(Status.BAD_REQUEST).entity("no position").build();
return null;
}
try {
float num = Float.parseFloat(s);
if (pos == 1) {
lat = num;
}
if (pos == 2) {
lon = num;
}
} catch (Exception e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity("Could not parse position string to number. Require numerical lat & long coords.").build());
}
pos++;
}
GeometryFactory gf = new GeometryFactory();
Coordinate dropPoint = new Coordinate(lon, lat);
int walkInMin = (int) Math.floor(walkmins);
double walkInSec = walkmins * 60;
LOG.debug("given travel time: " + walkInMin + " mins + " + (walkInSec - (60 * walkInMin)) + " sec");
// graph dynamically by 1.3 * min -> this should save processing time
if (walkInMin < 30) {
sptRequestA.worstTime = sptRequestA.dateTime + (30 * 60);
} else {
sptRequestA.worstTime = sptRequestA.dateTime + Math.round(walkInMin * 1.3 * 60);
}
// set the switch-time for shed/area calculation, i.e. to decide if the hull is calculated based on points or on edges
TraverseModeSet modes = sptRequestA.modes;
LOG.debug("mode(s): " + modes);
if (modes.contains(TraverseMode.TRANSIT)) {
// 20min (use 20min for transit, since buses may not come all the time)
shedCalcMethodSwitchTimeInSec = 60 * 20;
} else if (modes.contains(TraverseMode.CAR)) {
// 10min
shedCalcMethodSwitchTimeInSec = 60 * 10;
} else if (modes.contains(TraverseMode.BICYCLE)) {
// 10min
shedCalcMethodSwitchTimeInSec = 60 * 10;
} else {
// 20min
shedCalcMethodSwitchTimeInSec = 60 * 20;
}
// set the maxUserSpeed, which is used later to check for u-type streets/crescents when calculating sub-edges;
// Note, that the car speed depends on the edge itself, so this value may be replaced later
this.usesCar = false;
int numberOfModes = modes.getModes().size();
if (numberOfModes == 1) {
if (modes.getWalk()) {
this.maxUserSpeed = sptRequestA.walkSpeed;
} else if (modes.getBicycle()) {
this.maxUserSpeed = sptRequestA.bikeSpeed;
} else if (modes.getCar()) {
this.maxUserSpeed = sptRequestA.carSpeed;
this.usesCar = true;
}
} else {
// for all other cases (multiple-modes)
// sstein: I thought I may set it to 36.111 m/sec = 130 km/h,
// but maybe it is better to assume walk speed for transit, i.e. treat it like if the
// person gets off the bus on the last crossing and walks the "last mile".
this.maxUserSpeed = sptRequestA.walkSpeed;
}
if (doSpeedTest) {
LOG.debug("performing angle and speed based test to detect u-shapes");
} else {
LOG.debug("performing only angle based test to detect u-shapes");
}
// TODO: OTP prefers to snap to car-roads/ways, which is not so nice, when walking,
// and a footpath is closer by. So far there is no option to switch that off
Router router = otpServer.getRouter(routerId);
// create the ShortestPathTree
try {
sptRequestA.setRoutingContext(router.graph);
} catch (Exception e) {
// if we get an exception here, and in particular a VertexNotFoundException,
// then it is likely that we chose a (transit) mode without having that (transit) modes data
LOG.debug("cannot set RoutingContext: " + e.toString());
LOG.debug("cannot set RoutingContext: setting mode=WALK");
// fall back to walk mode
sptRequestA.setMode(TraverseMode.WALK);
sptRequestA.setRoutingContext(router.graph);
}
ShortestPathTree sptA = new AStar().getShortestPathTree(sptRequestA);
StreetLocation origin = (StreetLocation) sptRequestA.rctx.fromVertex;
// remove inserted points
sptRequestA.cleanup();
// create a LineString for display
Coordinate[] pathToStreetCoords = new Coordinate[2];
pathToStreetCoords[0] = dropPoint;
pathToStreetCoords[1] = origin.getCoordinate();
LineString pathToStreet = gf.createLineString(pathToStreetCoords);
// get distance between origin and drop point for time correction
double distanceToRoad = SphericalDistanceLibrary.distance(origin.getY(), origin.getX(), dropPoint.y, dropPoint.x);
long offRoadTimeCorrection = (long) (distanceToRoad / this.offRoadWalkspeed);
//
// --- filter the states ---
//
Set<Coordinate> visitedCoords = new HashSet<Coordinate>();
ArrayList<Edge> allConnectingEdges = new ArrayList<Edge>();
Coordinate[] coords = null;
long maxTime = (long) walkInSec - offRoadTimeCorrection;
// if the initial walk is already to long, there is no need to parse...
if (maxTime <= 0) {
noRoadNearBy = true;
long timeToWalk = (long) walkInSec;
long timeBetweenStates = offRoadTimeCorrection;
long timeMissing = timeToWalk;
double fraction = (double) timeMissing / (double) timeBetweenStates;
pathToStreet = getSubLineString(pathToStreet, fraction);
LOG.debug("no street found within giving travel time (for off-road walkspeed: {} m/sec)", this.offRoadWalkspeed);
} else {
noRoadNearBy = false;
Map<ReversibleLineStringWrapper, Edge> connectingEdgesMap = Maps.newHashMap();
for (State state : sptA.getAllStates()) {
long et = state.getElapsedTimeSeconds();
if (et <= maxTime) {
// 250 points away (while 145 were finally displayed)
if (visitedCoords.contains(state.getVertex().getCoordinate())) {
continue;
} else {
visitedCoords.add(state.getVertex().getCoordinate());
}
// -- get all Edges needed later for the edge representation
// and to calculate an edge-based walkshed
// Note, it can happen that we get a null geometry here, e.g. for hop-edges!
Collection<Edge> vertexEdgesIn = state.getVertex().getIncoming();
for (Iterator<Edge> iterator = vertexEdgesIn.iterator(); iterator.hasNext(); ) {
Edge edge = (Edge) iterator.next();
Geometry edgeGeom = edge.getGeometry();
if (edgeGeom != null) {
// make sure we get only real edges
if (edgeGeom instanceof LineString) {
// allConnectingEdges.add(edge); // instead of this, use a map now, so we don't have similar edge many times
connectingEdgesMap.put(new ReversibleLineStringWrapper((LineString) edgeGeom), edge);
}
}
}
Collection<Edge> vertexEdgesOut = state.getVertex().getOutgoing();
for (Iterator<Edge> iterator = vertexEdgesOut.iterator(); iterator.hasNext(); ) {
Edge edge = (Edge) iterator.next();
Geometry edgeGeom = edge.getGeometry();
if (edgeGeom != null) {
if (edgeGeom instanceof LineString) {
// allConnectingEdges.add(edge); // instead of this, use a map now, so we don't similar edge many times
connectingEdgesMap.put(new ReversibleLineStringWrapper((LineString) edgeGeom), edge);
}
}
}
}
// end : if(et < maxTime)
}
// --
// points from list to array, for later
coords = new Coordinate[visitedCoords.size()];
int i = 0;
for (Coordinate c : visitedCoords) coords[i++] = c;
// connection edges from Map to List
allConnectingEdges.clear();
for (Edge tedge : connectingEdgesMap.values()) allConnectingEdges.add(tedge);
}
StringWriter sw = new StringWriter();
GeometryJSON geometryJSON = new GeometryJSON();
//
try {
if (output.equals(SIsochrone.RESULT_TYPE_POINTS)) {
// and return those points
if (noRoadNearBy) {
Geometry circleShape = createCirle(dropPoint, pathToStreet);
coords = circleShape.getCoordinates();
}
// -- the states/nodes with time elapsed <= X min.
LOG.debug("write multipoint geom with {} points", coords.length);
geometryJSON.write(gf.createMultiPoint(coords), sw);
LOG.debug("done");
} else if (output.equals(SIsochrone.RESULT_TYPE_SHED)) {
Geometry[] geomsArray = null;
// in case there was no road we create a circle
if (noRoadNearBy) {
Geometry circleShape = createCirle(dropPoint, pathToStreet);
geometryJSON.write(circleShape, sw);
} else {
if (maxTime > shedCalcMethodSwitchTimeInSec) {
// eg., walkshed > 20 min
// -- create a point-based walkshed
// less exact and should be used for large walksheds with many edges
LOG.debug("create point-based shed (not from edges)");
geomsArray = new Geometry[coords.length];
for (int j = 0; j < geomsArray.length; j++) {
geomsArray[j] = gf.createPoint(coords[j]);
}
} else {
// -- create an edge-based walkshed
// it is more exact and should be used for short walks
LOG.debug("create edge-based shed (not from points)");
Map<ReversibleLineStringWrapper, LineString> walkShedEdges = Maps.newHashMap();
// add the walk from the pushpin to closest street point
walkShedEdges.put(new ReversibleLineStringWrapper(pathToStreet), pathToStreet);
// get the edges and edge parts within time limits
ArrayList<LineString> withinTimeEdges = this.getLinesAndSubEdgesWithinMaxTime(maxTime, allConnectingEdges, sptA, angleLimitForUShapeDetection, distanceToleranceForUShapeDetection, maxUserSpeed, usesCar, doSpeedTest);
for (LineString ls : withinTimeEdges) {
walkShedEdges.put(new ReversibleLineStringWrapper(ls), ls);
}
geomsArray = new Geometry[walkShedEdges.size()];
int k = 0;
for (LineString ls : walkShedEdges.values()) geomsArray[k++] = ls;
}
// end if-else: maxTime condition
GeometryCollection gc = gf.createGeometryCollection(geomsArray);
// create the concave hull, but in case it fails we just return the convex hull
Geometry outputHull = null;
LOG.debug("create concave hull from {} geoms with edge length limit of about {} m (distance on meridian)", geomsArray.length, concaveHullAlpha * 111132);
// (see wikipedia: http://en.wikipedia.org/wiki/Latitude#The_length_of_a_degree_of_latitude)
try {
ConcaveHull hull = new ConcaveHull(gc, concaveHullAlpha);
outputHull = hull.getConcaveHull();
} catch (Exception e) {
outputHull = gc.convexHull();
LOG.debug("Could not generate ConcaveHull for WalkShed, using ConvexHull instead.");
}
LOG.debug("write shed geom");
geometryJSON.write(outputHull, sw);
LOG.debug("done");
}
} else if (output.equals(SIsochrone.RESULT_TYPE_EDGES)) {
// in case there was no road we return only the suggested path to the street
if (noRoadNearBy) {
geometryJSON.write(pathToStreet, sw);
} else {
// -- if we would use only the edges from the paths to the origin we will miss
// some edges that will be never on the shortest path (e.g. loops/crescents).
// However, we can retrieve all edges by checking the times for each
// edge end-point
Map<ReversibleLineStringWrapper, LineString> walkShedEdges = Maps.newHashMap();
// add the walk from the pushpin to closest street point
walkShedEdges.put(new ReversibleLineStringWrapper(pathToStreet), pathToStreet);
// get the edges and edge parts within time limits
ArrayList<LineString> withinTimeEdges = this.getLinesAndSubEdgesWithinMaxTime(maxTime, allConnectingEdges, sptA, angleLimitForUShapeDetection, distanceToleranceForUShapeDetection, maxUserSpeed, usesCar, doSpeedTest);
for (LineString ls : withinTimeEdges) {
walkShedEdges.put(new ReversibleLineStringWrapper(ls), ls);
}
Geometry mls = null;
LineString[] edges = new LineString[walkShedEdges.size()];
int k = 0;
for (LineString ls : walkShedEdges.values()) edges[k++] = ls;
LOG.debug("create multilinestring from {} geoms", edges.length);
mls = gf.createMultiLineString(edges);
LOG.debug("write geom");
geometryJSON.write(mls, sw);
LOG.debug("done");
}
} else if (output.equals("DEBUGEDGES")) {
// -- for debugging, i.e. display of detected u-shapes/crescents
ArrayList<LineString> withinTimeEdges = this.getLinesAndSubEdgesWithinMaxTime(maxTime, allConnectingEdges, sptA, angleLimitForUShapeDetection, distanceToleranceForUShapeDetection, maxUserSpeed, usesCar, doSpeedTest);
if (this.showTooFastEdgesAsDebugGeomsANDnotUShapes) {
LOG.debug("displaying edges that are traversed too fast");
this.debugGeoms = this.tooFastTraversedEdgeGeoms;
} else {
LOG.debug("displaying detected u-shaped roads/crescents");
}
LineString[] edges = new LineString[this.debugGeoms.size()];
int k = 0;
for (Iterator iterator = debugGeoms.iterator(); iterator.hasNext(); ) {
LineString ls = (LineString) iterator.next();
edges[k] = ls;
k++;
}
Geometry mls = gf.createMultiLineString(edges);
LOG.debug("write debug geom");
geometryJSON.write(mls, sw);
LOG.debug("done");
}
} catch (Exception e) {
LOG.error("Exception creating isochrone", e);
}
return sw.toString();
}
use of org.opentripplanner.routing.core.RoutingRequest 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.core.RoutingRequest in project OpenTripPlanner by opentripplanner.
the class TripTimesTest method testBikesAllowed.
@Test
public void testBikesAllowed() {
Graph graph = new Graph();
Trip trip = new Trip();
Route route = new Route();
trip.setRoute(route);
List<StopTime> stopTimes = Arrays.asList(new StopTime(), new StopTime());
TripTimes s = new TripTimes(trip, stopTimes, new Deduplicator());
RoutingRequest request = new RoutingRequest(TraverseMode.BICYCLE);
Vertex v = new SimpleConcreteVertex(graph, "", 0.0, 0.0);
request.setRoutingContext(graph, v, v);
State s0 = new State(request);
assertFalse(s.tripAcceptable(s0, 0));
BikeAccess.setForTrip(trip, BikeAccess.ALLOWED);
assertTrue(s.tripAcceptable(s0, 0));
BikeAccess.setForTrip(trip, BikeAccess.NOT_ALLOWED);
assertFalse(s.tripAcceptable(s0, 0));
}
Aggregations