use of org.opentripplanner.routing.vertextype.PatternStopVertex in project OpenTripPlanner by opentripplanner.
the class OnBoardDepartServiceImpl method setupDepartOnBoard.
@Override
public Vertex setupDepartOnBoard(RoutingContext ctx) {
RoutingRequest opt = ctx.opt;
opt.rctx = ctx;
/* 1. Get the list of PatternHop for the given trip ID. */
AgencyAndId tripId = opt.startingTransitTripId;
Trip trip = ctx.graph.index.tripForId.get(tripId);
TripPattern tripPattern = ctx.graph.index.patternForTrip.get(trip);
if (tripPattern == null) {
// TODO Shouldn't we bailout on a normal trip plan here, returning null ?
throw new IllegalArgumentException("Unknown/invalid trip ID: " + tripId);
}
List<PatternHop> hops = tripPattern.getPatternHops();
// Origin point, optional
Double lon = opt.from.lng;
Double lat = opt.from.lat;
PatternStopVertex nextStop;
TripTimes bestTripTimes = null;
ServiceDay bestServiceDay = null;
int bestStopIndex = 0;
double fractionCovered;
LineString geomRemaining;
Coordinate point = lon == null || lat == null ? null : new Coordinate(lon, lat);
if (point != null) {
/*
* 2. Get the best hop from the list, given the parameters. Currently look for nearest hop,
* taking into account shape if available. If no shape are present, the computed hop and
* fraction may be a bit away from what it should be.
*/
PatternHop bestHop = null;
double minDist = Double.MAX_VALUE;
for (PatternHop hop : hops) {
LineString line = hop.getGeometry();
double dist = SphericalDistanceLibrary.fastDistance(point, line);
if (dist < minDist) {
minDist = dist;
bestHop = hop;
}
}
if (minDist > 1000)
LOG.warn("On-board depart: origin point suspiciously away from nearest trip shape ({} meters)", minDist);
else
LOG.info("On-board depart: origin point {} meters away from hop shape", minDist);
/*
* 3. Compute the fraction covered percentage of the current hop. This assume a constant
* trip speed alongside the whole hop: this should be quite precise for small hops
* (buses), a bit less for longer ones (long distance train). Shape linear distance is
* of no help here, as the unit is arbitrary (and probably usually a distance).
*/
LineString geometry = bestHop.getGeometry();
P2<LineString> geomPair = GeometryUtils.splitGeometryAtPoint(geometry, point);
geomRemaining = geomPair.second;
double total = SphericalDistanceLibrary.fastLength(geometry);
double remaining = SphericalDistanceLibrary.fastLength(geomRemaining);
fractionCovered = total > 0.0 ? (double) (1.0 - remaining / total) : 0.0;
nextStop = (PatternStopVertex) bestHop.getToVertex();
bestStopIndex = bestHop.getStopIndex();
/*
* 4. Compute service day based on given departure day/time relative to
* scheduled/real-time trip time for hop. This is needed as for some trips any service
* day can apply.
*/
int minDelta = Integer.MAX_VALUE;
int actDelta = 0;
for (ServiceDay serviceDay : ctx.serviceDays) {
TripPattern pattern = nextStop.getTripPattern();
Timetable timetable = pattern.getUpdatedTimetable(opt, serviceDay);
// Get the tripTimes including real-time updates for the serviceDay
TripTimes tripTimes = timetable.getTripTimes(timetable.getTripIndex(tripId));
int depTime = tripTimes.getDepartureTime(bestStopIndex);
int arrTime = tripTimes.getArrivalTime(bestStopIndex + 1);
int estTime = (int) Math.round(depTime * fractionCovered + arrTime * (1 - fractionCovered));
int time = serviceDay.secondsSinceMidnight(opt.dateTime);
/*
* TODO Weight differently early vs late time, as the probability of any transit
* being late is higher than being early. However, this has impact if your bus is
* more than 12h late, I don't think this would happen really often.
*/
int deltaTime = Math.abs(time - estTime);
if (deltaTime < minDelta) {
minDelta = deltaTime;
actDelta = time - estTime;
bestTripTimes = tripTimes;
bestServiceDay = serviceDay;
}
}
if (minDelta > 60000)
// Being more than 1h late should not happen often
LOG.warn("On-board depart: delta between scheduled/real-time and actual time suspiciously large: {} seconds.", actDelta);
else
LOG.info("On-board depart: delta between scheduled/real-time and actual time is {} seconds.", actDelta);
} else {
/* 2. Compute service day */
for (ServiceDay serviceDay : ctx.serviceDays) {
Timetable timetable = tripPattern.getUpdatedTimetable(opt, serviceDay);
// Get the tripTimes including real-time updates for the serviceDay
TripTimes tripTimes = timetable.getTripTimes(timetable.getTripIndex(tripId));
int depTime = tripTimes.getDepartureTime(0);
int arrTime = tripTimes.getArrivalTime(tripTimes.getNumStops() - 1);
int time = serviceDay.secondsSinceMidnight(opt.dateTime);
if (depTime <= time && time <= arrTime) {
bestTripTimes = tripTimes;
bestServiceDay = serviceDay;
}
}
if (bestServiceDay == null) {
throw new RuntimeException("Unable to determine on-board depart service day.");
}
int time = bestServiceDay.secondsSinceMidnight(opt.dateTime);
/*
* 3. Get the best hop from the list, given the parameters. This is done by finding the
* last hop that has not yet departed.
*/
PatternHop bestHop = null;
for (PatternHop hop : hops) {
int stopIndex = hop.getStopIndex();
int depTime = bestTripTimes.getDepartureTime(stopIndex);
int arrTime = bestTripTimes.getArrivalTime(stopIndex + 1);
if (time == arrTime) {
return ctx.graph.getVertex(hop.getEndStop().getId().toString());
} else if (depTime < time) {
bestHop = hop;
bestStopIndex = stopIndex;
} else if (time == depTime || bestTripTimes.getArrivalTime(bestStopIndex + 1) < time) {
return ctx.graph.getVertex(hop.getBeginStop().getId().toString());
} else {
break;
}
}
nextStop = (PatternStopVertex) bestHop.getToVertex();
LineString geometry = bestHop.getGeometry();
/*
* 4. Compute the fraction covered percentage of the current hop. Once again a constant
* trip speed is assumed. The linear distance of the shape is used, so the results are
* not 100% accurate. On the flip side, they are easy to compute and very well testable.
*/
int depTime = bestTripTimes.getDepartureTime(bestStopIndex);
int arrTime = bestTripTimes.getArrivalTime(bestStopIndex + 1);
fractionCovered = ((double) (time - depTime)) / ((double) (arrTime - depTime));
P2<LineString> geomPair = GeometryUtils.splitGeometryAtFraction(geometry, fractionCovered);
geomRemaining = geomPair.second;
if (geometry.isEmpty()) {
lon = Double.NaN;
lat = Double.NaN;
} else {
Coordinate start;
if (geomRemaining.isEmpty()) {
start = geometry.getCoordinateN(geometry.getNumPoints() - 1);
} else {
start = geomRemaining.getCoordinateN(0);
}
lon = start.x;
lat = start.y;
}
}
OnboardDepartVertex onboardDepart = new OnboardDepartVertex("on_board_depart", lon, lat);
OnBoardDepartPatternHop startHop = new OnBoardDepartPatternHop(onboardDepart, nextStop, bestTripTimes, bestServiceDay, bestStopIndex, fractionCovered);
startHop.setGeometry(geomRemaining);
return onboardDepart;
}
use of org.opentripplanner.routing.vertextype.PatternStopVertex in project OpenTripPlanner by opentripplanner.
the class TestOnBoardRouting method testOnBoardRouting.
/**
* Compute a set of path between two random stop locations in a test GTFS.
*
* For each departure/arrival location, compute a normal path (depart alighted). Then re-run the
* same itinerary but with departure while on-board at a randomly-picked up trip alongside the
* path.
*
* We assert that the two itineraries will arrive at the same time, at the same place, with at
* least one less boarding, and take a less or equals amount of time.
*/
@SuppressWarnings("deprecation")
public void testOnBoardRouting() throws Exception {
String feedId = graph.getFeedIds().iterator().next();
// Seed the random generator to make consistent set of tests
Random rand = new Random(42);
// Number of tests to run
final int NTESTS = 100;
int n = 0;
while (true) {
/* Compute a normal path between two random stops... */
Vertex origin, destination;
do {
/* See FAKE_GTFS for available locations */
origin = graph.getVertex(feedId + ":" + (char) (65 + rand.nextInt(20)));
destination = graph.getVertex(feedId + ":" + (char) (65 + rand.nextInt(20)));
} while (origin.equals(destination));
/* ...at a random date/time */
RoutingRequest options = new RoutingRequest();
options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 5 + rand.nextInt(4), 1 + rand.nextInt(20), 4 + rand.nextInt(10), rand.nextInt(60), 0);
ShortestPathTree spt;
GraphPath path;
options.setRoutingContext(graph, origin, destination);
spt = aStar.getShortestPathTree(options);
path = spt.getPath(destination, false);
if (path == null)
continue;
System.out.println("Testing path between " + origin.getLabel() + " and " + destination.getLabel() + " at " + new Date(options.dateTime * 1000));
long arrivalTime1 = 0L;
long elapsedTime1 = 0L;
int numBoardings1 = 0;
Vertex arrivalVertex1 = null;
if (verbose)
System.out.println("PATH 1 ---------------------");
for (State s : path.states) {
if (verbose)
System.out.println(s + " [" + s.getVertex().getClass().getName() + "]");
arrivalTime1 = s.getTimeSeconds();
arrivalVertex1 = s.getVertex();
elapsedTime1 = s.getElapsedTimeSeconds();
numBoardings1 = s.getNumBoardings();
}
/* Get a random transit hop from the computed path */
Stop end = null;
PatternStopVertex nextV = null;
TripTimes tripTimes = null;
int stopIndex = 0;
long newStart = 0L;
int nhop = 0;
for (State s : path.states) {
if (s.getVertex() instanceof PatternArriveVertex && s.getBackEdge() instanceof PatternHop)
nhop++;
}
int hop = rand.nextInt(nhop);
nhop = 0;
float k = rand.nextFloat();
for (State s : path.states) {
Vertex v = s.getVertex();
if (v instanceof PatternArriveVertex && s.getBackEdge() instanceof PatternHop) {
if (hop == nhop) {
PatternArriveVertex pav = (PatternArriveVertex) v;
end = pav.getStop();
nextV = pav;
PatternHop phe = (PatternHop) s.getBackEdge();
stopIndex = phe.getStopIndex();
tripTimes = s.getTripTimes();
int hopDuration = tripTimes.getRunningTime(stopIndex);
/*
* New start time at k% of hop. Note: do not try to make: round(time +
* k.hop) as it will be off few seconds due to floating-point rounding
* errors.
*/
newStart = s.getBackState().getTimeSeconds() + Math.round(hopDuration * k);
break;
}
nhop++;
}
}
System.out.println("Boarded depart: trip=" + tripTimes.trip + ", nextStop=" + nextV.getStop() + " stopIndex=" + stopIndex + " startTime=" + new Date(newStart * 1000L));
/* And use it for onboard departure */
double lat = end.getLat();
// Mock location, not really important here.
double lon = end.getLon();
OnboardDepartVertex onboardOrigin = new OnboardDepartVertex("OnBoard_Origin", lat, lon);
@SuppressWarnings("unused") OnBoardDepartPatternHop currentHop = new OnBoardDepartPatternHop(onboardOrigin, nextV, tripTimes, options.rctx.serviceDays.get(1), stopIndex, k);
options.dateTime = newStart;
options.setRoutingContext(graph, onboardOrigin, destination);
spt = aStar.getShortestPathTree(options);
/* Re-compute a new path starting boarded */
GraphPath path2 = spt.getPath(destination, false);
assertNotNull(path2);
if (verbose)
System.out.println("PATH 2 ---------------------");
long arrivalTime2 = 0L;
long elapsedTime2 = 0L;
int numBoardings2 = 0;
Vertex arrivalVertex2 = null;
for (State s : path2.states) {
if (verbose)
System.out.println(s + " [" + s.getVertex().getClass().getName() + "]");
arrivalTime2 = s.getTimeSeconds();
arrivalVertex2 = s.getVertex();
elapsedTime2 = s.getElapsedTimeSeconds();
numBoardings2 = s.getNumBoardings();
}
/* Arrival time and vertex *must* match */
assertEquals(arrivalTime1, arrivalTime2);
assertEquals(arrivalVertex1, destination);
assertEquals(arrivalVertex2, destination);
/* On-board *must* be shorter in time */
assertTrue(elapsedTime2 <= elapsedTime1);
/* On-board *must* have less boardings */
assertTrue(numBoardings2 < numBoardings1);
/* Cleanup edges */
for (Edge edge : onboardOrigin.getOutgoing()) {
graph.removeEdge(edge);
}
n++;
if (n > NTESTS)
break;
}
}
use of org.opentripplanner.routing.vertextype.PatternStopVertex in project OpenTripPlanner by opentripplanner.
the class PatternHop method optimisticTraverse.
public State optimisticTraverse(State state0) {
RoutingRequest options = state0.getOptions();
// Ignore this edge if either of its stop is banned hard
if (!options.bannedStopsHard.isEmpty()) {
if (options.bannedStopsHard.matches(((PatternStopVertex) fromv).getStop()) || options.bannedStopsHard.matches(((PatternStopVertex) tov).getStop())) {
return null;
}
}
int runningTime = getPattern().scheduledTimetable.getBestRunningTime(stopIndex);
StateEditor s1 = state0.edit(this);
s1.incrementTimeInSeconds(runningTime);
s1.setBackMode(getMode());
s1.incrementWeight(runningTime);
return s1.makeState();
}
use of org.opentripplanner.routing.vertextype.PatternStopVertex in project OpenTripPlanner by opentripplanner.
the class PatternHop method traverse.
public State traverse(State s0) {
RoutingRequest options = s0.getOptions();
// Ignore this edge if either of its stop is banned hard
if (!options.bannedStopsHard.isEmpty()) {
if (options.bannedStopsHard.matches(((PatternStopVertex) fromv).getStop()) || options.bannedStopsHard.matches(((PatternStopVertex) tov).getStop())) {
return null;
}
}
TripTimes tripTimes = s0.getTripTimes();
int runningTime = tripTimes.getRunningTime(stopIndex);
StateEditor s1 = s0.edit(this);
s1.incrementTimeInSeconds(runningTime);
if (s0.getOptions().arriveBy)
s1.setZone(getBeginStop().getZoneId());
else
s1.setZone(getEndStop().getZoneId());
// s1.setRoute(pattern.getExemplar().route.getId());
s1.incrementWeight(runningTime);
s1.setBackMode(getMode());
return s1.makeState();
}
Aggregations