Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.opentripplanner.profile;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Iterables;
import gnu.trove.map.TIntIntMap;
import gnu.trove.map.TObjectIntMap;
import gnu.trove.map.TObjectLongMap;
import gnu.trove.map.hash.TObjectIntHashMap;
import gnu.trove.map.hash.TObjectLongHashMap;
import org.joda.time.DateTimeZone;
import org.opentripplanner.analyst.SampleSet;
import org.opentripplanner.analyst.TimeSurface;
import org.opentripplanner.analyst.cluster.ResultEnvelope;
import org.opentripplanner.analyst.cluster.TaskStatistics;
import org.opentripplanner.analyst.scenario.AddTripPattern;
import org.opentripplanner.api.parameter.QualifiedModeSet;
import org.opentripplanner.common.model.GenericLocation;
import org.opentripplanner.routing.algorithm.AStar;
import org.opentripplanner.routing.core.RoutingRequest;
import org.opentripplanner.routing.core.State;
import org.opentripplanner.routing.core.TraverseMode;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.graph.GraphIndex;
import org.opentripplanner.routing.graph.Vertex;
import org.opentripplanner.routing.spt.DominanceFunction;
import org.opentripplanner.routing.spt.ShortestPathTree;
import org.opentripplanner.routing.vertextype.TransitStop;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.DayOfWeek;
import java.util.Arrays;
/**
* Perform one-to-many profile routing using repeated RAPTOR searches. In this context, profile routing means finding
* the optimal itinerary for each departure moment in a given window, without trying to reconstruct the exact paths.
*
* In other contexts (Modeify-style point to point routing) we would want to include suboptimal but resonable paths
* and retain enough information to reconstruct all those paths accounting for common trunk frequencies and stop clusters.
*
* This method is conceptually very similar to the work of the Minnesota Accessibility Observatory
* (http://www.its.umn.edu/Publications/ResearchReports/pdfdownloadl.pl?id=2504)
* They run repeated searches for each departure time in the window. We take advantage of the fact that the street
* network is static (for the most part, assuming time-dependent turn restrictions and traffic are consistent across
* the time window) and only run a fast transit search for each minute in the window.
*/
public class RepeatedRaptorProfileRouter {
private static final Logger LOG = LoggerFactory.getLogger(RepeatedRaptorProfileRouter.class);
public static final int MAX_DURATION = 10 * 60 * 2; // seconds
public ProfileRequest request;
public Graph graph;
// The spacing in minutes between RAPTOR calls within the time window
public int stepMinutes = 1;
/** Three time surfaces for min, max, and average travel time over the given time window. */
public TimeSurface.RangeSet timeSurfaceRangeSet;
/** If not null, completely skip this agency during the calculations. */
public String banAgency = null;
/**
* If this is null we will generate a throw-away raptor data table. If it is set, the provided table will be used
* for routing. Ideally we'd handle the cacheing of reusable raptor tables inside this class, but this class is
* a throw-away calculator instance and doesn't have access to the job IDs which would be the cache keys.
*/
public RaptorWorkerData raptorWorkerData;
private ShortestPathTree preTransitSpt;
/** The sum of all earliest-arrival travel times to a given transit stop. Will be divided to create an average. */
TObjectLongMap accumulator = new TObjectLongHashMap();
/** The number of travel time observations added into the accumulator. The divisor when calculating an average. */
TObjectIntMap counts = new TObjectIntHashMap();
/** Samples to propagate times to */
private SampleSet sampleSet;
private PropagatedTimesStore propagatedTimesStore;
// Set this field to an existing taskStatistics before routing if you want to collect performance information.
public TaskStatistics ts = new TaskStatistics();
// Set this field to true before routing if you want the full travel times included in your response.
public boolean includeTimes = false;
/**
* Make a router to use for making time surfaces only.
*
* If you're building ResultSets, you should use the below method that uses a SampleSet;
* otherwise you maximum and average may not be correct.
*
* If you want isochrones, you should use this method, or use a sampleset that is very fine. The
* reason for this is that isochrones are interpolated from these points in Euclidean space, so the
* points need to be very fine. Consider the case of three roads in an equilateral triangle, the edges
* of which each take ten minutes to traverse. If you're standing at one vertex and want to go
* halfway down the opposite edge, it is a fifteen minute trip. However, interpolating between the
* vertices will not give fifteen minutes, it will give ten. The less granular your representation
* is, the worse this problem will be.
* TODO merge this with the 3-arg constructor, and just specify what happens when you have a null pointset.
* This should allow us to get rid of some conditionals in the calling code.
*/
@Deprecated // just call the one below with null
public RepeatedRaptorProfileRouter(Graph graph, ProfileRequest request) {
this(graph, request, null);
}
/**
* Make a router to use for making ResultSets. This propagates times all the way to the samples, so that
* average and maximum travel time are correct.
*
* Samples are linked to two vertices at the ends of an edge, and it is possible that the average for the sample
* (as well as the max) is lower than the average or the max at either of the vertices, because it may be that
* every time the max at one vertex is occurring, a lower value is occurring at the other. This initially
* seems improbable, but consider the case when there are two parallel transit services running out of phase.
* It may be that some of the time it makes sense to go out of your house and turn left, and sometimes it makes
* sense to turn right, depending on which is coming first.
*/
public RepeatedRaptorProfileRouter (Graph graph, ProfileRequest request, SampleSet sampleSet) {
this.request = request;
this.graph = graph;
this.sampleSet = sampleSet;
}
public ResultEnvelope route () {
boolean isochrone = (sampleSet == null); // When no sample set is provided, we're making isochrones.
boolean transit = (request.transitModes != null && request.transitModes.isTransit()); // Does the search involve transit at all?
long computationStartTime = System.currentTimeMillis();
LOG.info("Begin profile request");
// Data tables may have been supplied by the caller (if they are cached). Otherwise generate a throw away one.
// We only create data tables if transit is in use, otherwise they wouldn't serve any purpose.
if (raptorWorkerData == null && transit) {
long dataStart = System.currentTimeMillis();
raptorWorkerData = getRaptorWorkerData(request, graph, sampleSet, ts);
ts.raptorData = (int) (System.currentTimeMillis() - dataStart);
}
// Find the transit stops that are accessible from the origin, leaving behind an SPT behind of access
// times to all reachable vertices.
long initialStopStartTime = System.currentTimeMillis();
// This will return null if we have no transit data, but will leave behind a pre-transit SPT.
TIntIntMap transitStopAccessTimes = findInitialStops(false, raptorWorkerData);
// Create an array containing the best travel time in seconds to each vertex in the graph when not using transit.
int[] nonTransitTimes = new int[Vertex.getMaxIndex()];
Arrays.fill(nonTransitTimes, Integer.MAX_VALUE);
for (State state : preTransitSpt.getAllStates()) {
// Note that we are using the walk distance divided by speed here in order to be consistent with the
// least-walk optimization in the initial stop search (and the stop tree cache which is used at egress)
// TODO consider why this matters, I'm using reported travel time from the states
int time = (int) state.getElapsedTimeSeconds();
int vidx = state.getVertex().getIndex();
int otime = nonTransitTimes[vidx];
// There may be dominated states in the SPT. Make sure we don't include them here.
if (otime > time) {
nonTransitTimes[vidx] = time;
}
}
ts.initialStopSearch = (int) (System.currentTimeMillis() - initialStopStartTime);
long walkSearchStart = System.currentTimeMillis(); // FIXME wasn't the walk search already performed above?
// At this point we have an array of the travel times in seconds to each vertex in the graph without transit.
// In the event that a pointset was supplied, our real targets are the points in the pointset, not the vertices
// in the graph. Therefore we must replace the vertex-indexed array with a new point-indexed array.
if (sampleSet != null) {
nonTransitTimes = sampleSet.eval(nonTransitTimes);
}
ts.walkSearch = (int) (System.currentTimeMillis() - walkSearchStart);
if (transit) {
RaptorWorker worker = new RaptorWorker(raptorWorkerData, request);
propagatedTimesStore = worker.runRaptor(graph, transitStopAccessTimes, nonTransitTimes, ts);
ts.initialStopCount = transitStopAccessTimes.size();
} else {
// Nontransit case: skip transit routing and make a propagated times store based on only one row.
propagatedTimesStore = new PropagatedTimesStore(graph, request, nonTransitTimes.length);
int[][] singleRoundResults = new int[1][];
singleRoundResults[0] = nonTransitTimes;
propagatedTimesStore.setFromArray(singleRoundResults, new boolean[] {true},
PropagatedTimesStore.ConfidenceCalculationMethod.MIN_MAX);
}
for (int min : propagatedTimesStore.mins) {
if (min != RaptorWorker.UNREACHED) ts.targetsReached++;
}
ts.compute = (int) (System.currentTimeMillis() - computationStartTime);
LOG.info("Profile request finished in {} seconds", (ts.compute) / 1000.0);
// Turn the results of the search into isochrone geometries or accessibility data as requested.
long resultSetStart = System.currentTimeMillis();
ResultEnvelope envelope = new ResultEnvelope();
if (isochrone) {
// No destination point set was provided and we're just making isochrones based on travel time to vertices,
// rather than finding access times to a set of user-specified points.
envelope = propagatedTimesStore.makeIsochronesForVertices();
} else {
// A destination point set was provided. We've found access times to a set of specified points.
// TODO actually use those boolean params to calculate isochrones on a regular grid pointset
// TODO maybe there's a better way to pass includeTimes in here from the clusterRequest,
// maybe we should just provide the whole clusterRequest not just the wrapped profileRequest.
envelope = propagatedTimesStore.makeResults(sampleSet, includeTimes, true, false);
}
ts.resultSets = (int) (System.currentTimeMillis() - resultSetStart);
return envelope;
}
/**
* Find all transit stops accessible by streets around the origin, leaving behind a shortest path tree of the
* reachable area in the field preTransitSpt.
*
* @param data the raptor data table to use. If this is null (i.e. there is no transit) range is extended,
* and we don't care if we actually find any stops, we just want the tree of on-street distances.
*/
@VisibleForTesting
public TIntIntMap findInitialStops(boolean dest, RaptorWorkerData data) {
LOG.info("Finding initial stops");
double lat = dest ? request.toLat : request.fromLat;
double lon = dest ? request.toLon : request.fromLon;
QualifiedModeSet modes = dest ? request.egressModes : request.accessModes;
RoutingRequest rr = new RoutingRequest(modes);
rr.batch = true;
rr.from = new GenericLocation(lat, lon);
//rr.walkSpeed = request.walkSpeed;
rr.to = rr.from;
rr.setRoutingContext(graph);
rr.dateTime = request.date.toDateMidnight(DateTimeZone.forTimeZone(graph.getTimeZone())).getMillis() / 1000 +
request.fromTime;
rr.walkSpeed = request.walkSpeed;
rr.bikeSpeed = request.bikeSpeed;
//rr.modes.setWalk(true);
if (data == null) {
// Non-transit mode. Search out to the full 120 minutes.
// Should really use directModes.
rr.worstTime = rr.dateTime + RaptorWorker.MAX_DURATION;
rr.dominanceFunction = new DominanceFunction.EarliestArrival();
} else {
// Transit mode, limit pre-transit travel.
if (rr.modes.contains(TraverseMode.BICYCLE)) {
rr.dominanceFunction = new DominanceFunction.EarliestArrival();
rr.worstTime = rr.dateTime + request.maxBikeTime * 60;
} else {
// We use walk-distance limiting and a least-walk dominance function in order to be consistent with egress walking
// which is implemented this way because walk times can change when walk speed changes. Also, walk times are floating
// point and can change slightly when streets are split. Street lengths are internally fixed-point ints, which do not
// suffer from roundoff. Great care is taken when splitting to preserve sums.
// When cycling, this is not an issue; we already have an explicitly asymmetrical search (cycling at the origin, walking at the destination),
// so we need not preserve symmetry.
// We use the max walk time for the search at the origin, but we clamp it to MAX_WALK_METERS so that we don;t
// have every transit stop in the state as an initial transit stop if someone sets max walk time to four days,
// and so that symmetry is preserved.
rr.maxWalkDistance = Math.min(request.maxWalkTime * 60 * request.walkSpeed, GraphIndex.MAX_WALK_METERS); // FIXME kind of arbitrary
rr.softWalkLimiting = false;
rr.dominanceFunction = new DominanceFunction.LeastWalk();
}
}
rr.numItineraries = 1;
rr.longDistance = true;
AStar aStar = new AStar();
preTransitSpt = aStar.getShortestPathTree(rr, 5);
// Return nearest stops if we're using transit,
// otherwise return null and leave preTransitSpt around for later use.
if (data != null) {
TIntIntMap accessTimes = data.findStopsNear(preTransitSpt, graph, rr.modes.contains(TraverseMode.BICYCLE), request.walkSpeed);
LOG.info("Found {} transit stops", accessTimes.size());
return accessTimes;
} else {
return null;
}
}
/** Create RAPTOR worker data from a graph, profile request and sample set (the last of which may be null */
public static RaptorWorkerData getRaptorWorkerData (ProfileRequest request, Graph graph, SampleSet sampleSet, TaskStatistics ts) {
LOG.info("Make data...");
long startData = System.currentTimeMillis();
// assign indices for added transit stops
// note that they only need be unique in the context of this search.
// note also that there may be, before this search is over, vertices with higher indices (temp vertices)
// but they will not be transit stops.
if (request.scenario != null && request.scenario.modifications != null) {
for (AddTripPattern atp : Iterables
.filter(request.scenario.modifications, AddTripPattern.class)) {
atp.materialize(graph);
}
}
// convert from joda to java - ISO day of week with monday == 1
DayOfWeek dayOfWeek = DayOfWeek.of(request.date.getDayOfWeek());
TimeWindow window = new TimeWindow(request.fromTime, request.toTime + RaptorWorker.MAX_DURATION,
graph.index.servicesRunning(request.date), dayOfWeek);
RaptorWorkerData raptorWorkerData;
if (sampleSet == null)
raptorWorkerData = new RaptorWorkerData(graph, window, request, ts);
else
raptorWorkerData = new RaptorWorkerData(graph, window, request, sampleSet,
ts);
ts.raptorData = (int) (System.currentTimeMillis() - startData);
LOG.info("done");
return raptorWorkerData;
}
}