use of org.opentripplanner.routing.trippattern.TripTimes in project OpenTripPlanner by opentripplanner.
the class OnBoardDepartServiceImplTest method testOnBoardDepartureTime.
@Test
public final void testOnBoardDepartureTime() {
Coordinate[] coordinates = new Coordinate[5];
coordinates[0] = new Coordinate(0.0, 0.0);
coordinates[1] = new Coordinate(0.0, 1.0);
coordinates[2] = new Coordinate(2.0, 1.0);
coordinates[3] = new Coordinate(5.0, 1.0);
coordinates[4] = new Coordinate(5.0, 5.0);
PatternDepartVertex depart = mock(PatternDepartVertex.class);
PatternArriveVertex dwell = mock(PatternArriveVertex.class);
PatternArriveVertex arrive = mock(PatternArriveVertex.class);
Graph graph = mock(Graph.class);
RoutingRequest routingRequest = mock(RoutingRequest.class);
ServiceDay serviceDay = mock(ServiceDay.class);
// You're probably not supposed to do this to mocks (access their fields directly)
// But I know of no other way to do this since the mock object has only action-free stub methods.
routingRequest.modes = new TraverseModeSet("WALK,TRANSIT");
when(graph.getTimeZone()).thenReturn(TimeZone.getTimeZone("GMT"));
GeometryFactory geometryFactory = GeometryUtils.getGeometryFactory();
CoordinateSequenceFactory coordinateSequenceFactory = geometryFactory.getCoordinateSequenceFactory();
CoordinateSequence coordinateSequence = coordinateSequenceFactory.create(coordinates);
LineString geometry = new LineString(coordinateSequence, geometryFactory);
ArrayList<Edge> hops = new ArrayList<Edge>(2);
RoutingContext routingContext = new RoutingContext(routingRequest, graph, null, arrive);
AgencyAndId agencyAndId = new AgencyAndId("Agency", "ID");
Agency agency = new Agency();
Route route = new Route();
ArrayList<StopTime> stopTimes = new ArrayList<StopTime>(3);
StopTime stopDepartTime = new StopTime();
StopTime stopDwellTime = new StopTime();
StopTime stopArriveTime = new StopTime();
Stop stopDepart = new Stop();
Stop stopDwell = new Stop();
Stop stopArrive = new Stop();
Trip trip = new Trip();
routingContext.serviceDays = new ArrayList<ServiceDay>(Collections.singletonList(serviceDay));
agency.setId(agencyAndId.getAgencyId());
route.setId(agencyAndId);
route.setAgency(agency);
stopDepart.setId(agencyAndId);
stopDwell.setId(agencyAndId);
stopArrive.setId(agencyAndId);
stopDepartTime.setStop(stopDepart);
stopDepartTime.setDepartureTime(0);
stopDwellTime.setArrivalTime(20);
stopDwellTime.setStop(stopDwell);
stopDwellTime.setDepartureTime(40);
stopArriveTime.setArrivalTime(60);
stopArriveTime.setStop(stopArrive);
stopTimes.add(stopDepartTime);
stopTimes.add(stopDwellTime);
stopTimes.add(stopArriveTime);
trip.setId(agencyAndId);
trip.setTripHeadsign("The right");
trip.setRoute(route);
TripTimes tripTimes = new TripTimes(trip, stopTimes, new Deduplicator());
StopPattern stopPattern = new StopPattern(stopTimes);
TripPattern tripPattern = new TripPattern(route, stopPattern);
TripPattern.generateUniqueIds(Arrays.asList(tripPattern));
when(depart.getTripPattern()).thenReturn(tripPattern);
when(dwell.getTripPattern()).thenReturn(tripPattern);
PatternHop patternHop0 = new PatternHop(depart, dwell, stopDepart, stopDwell, 0);
PatternHop patternHop1 = new PatternHop(dwell, arrive, stopDwell, stopArrive, 1);
hops.add(patternHop0);
hops.add(patternHop1);
when(graph.getEdges()).thenReturn(hops);
when(depart.getCoordinate()).thenReturn(new Coordinate(0, 0));
when(dwell.getCoordinate()).thenReturn(new Coordinate(0, 0));
when(arrive.getCoordinate()).thenReturn(new Coordinate(0, 0));
routingRequest.from = new GenericLocation();
routingRequest.startingTransitTripId = agencyAndId;
when(serviceDay.secondsSinceMidnight(anyInt())).thenReturn(9);
patternHop0.setGeometry(geometry);
tripPattern.add(tripTimes);
graph.index = new GraphIndex(graph);
coordinates = new Coordinate[3];
coordinates[0] = new Coordinate(3.5, 1.0);
coordinates[1] = new Coordinate(5.0, 1.0);
coordinates[2] = new Coordinate(5.0, 5.0);
coordinateSequence = coordinateSequenceFactory.create(coordinates);
geometry = new LineString(coordinateSequence, geometryFactory);
Vertex vertex = onBoardDepartServiceImpl.setupDepartOnBoard(routingContext);
Edge edge = vertex.getOutgoing().toArray(new Edge[1])[0];
assertEquals(vertex, edge.getFromVertex());
assertEquals(dwell, edge.getToVertex());
assertEquals("The right", edge.getDirection());
assertEquals(geometry, edge.getGeometry());
assertEquals(coordinates[0].x, vertex.getX(), 0.0);
assertEquals(coordinates[0].y, vertex.getY(), 0.0);
}
use of org.opentripplanner.routing.trippattern.TripTimes in project OpenTripPlanner by opentripplanner.
the class AdjustDwellTime method apply.
@Override
public TripTimes apply(Trip trip, TripPattern tp, TripTimes tt) {
if (!matches(trip))
return tt;
if (tt.getNumStops() == 0)
return tt;
// convert trip times to marginals
int[] dwellTimes = new int[tt.getNumStops()];
int[] hopTimes = new int[tt.getNumStops() - 1];
int startTime = tt.getArrivalTime(0);
for (int i = 0; i < tt.getNumStops(); i++) {
// adjust dwell time in place as we loop over the stops
if (stopId == null || stopId.contains(tp.stopPattern.stops[i].getId().getId()))
dwellTimes[i] = dwellTime;
else
dwellTimes[i] = tt.getDepartureTime(i) - tt.getArrivalTime(i);
if (i < hopTimes.length)
hopTimes[i] = tt.getArrivalTime(i + 1) - tt.getDepartureTime(i);
}
// make a new triptimes
// Note that this copies the original times, not ones that have been modified by other modifications
// (suppose someone set the dwell time at some stops to one value because they have offboard fare collection
// and at other stops to a different value because they don't - this exists, for example, in San Francisco's
// Muni Metro, with offboard fare collection in the subway and onboard fare collection when running as a
// streetcar)
// However, this doesn't matter, because we've manually saved the modified times above.
TripTimes ret = new TripTimes(tt);
// Note: this requires us to use getArrivalTime not getScheduledArrivalTime when constructing the times
// This also means that one should include real-time data in the analysis graphs at their own peril
int cumulativeTime = startTime;
for (int i = 0; i < dwellTimes.length; i++) {
ret.updateArrivalTime(i, cumulativeTime);
cumulativeTime += dwellTimes[i];
ret.updateDepartureTime(i, cumulativeTime);
if (i < hopTimes.length)
cumulativeTime += hopTimes[i];
}
return ret;
}
use of org.opentripplanner.routing.trippattern.TripTimes in project OpenTripPlanner by opentripplanner.
the class ConvertToFrequency method apply.
public void apply(List<FrequencyEntry> frequencyEntries, List<TripTimes> scheduledTrips, Graph graph, BitSet servicesRunning, RaptorWorkerTimetable.BoardingAssumption assumption) {
// preserve existing frequency entries
this.frequencyEntries.addAll(frequencyEntries);
Set<String> routeIds = new HashSet<>();
if (routeId != null)
Stream.of(routeId).forEach(routeIds::add);
// loop over scheduled trips and figure out what to do with them
for (TripTimes tt : scheduledTrips) {
if (routeId == null || routeIds.contains(tt.trip.getRoute().getId().getId())) {
// put this in the appropriate group for frequency conversion
String key;
switch(groupBy) {
case ROUTE_DIRECTION:
key = tt.trip.getRoute().getId().getId() + "_" + tt.trip.getDirectionId();
break;
case ROUTE:
key = tt.trip.getRoute().getId().getId();
break;
case PATTERN:
key = graph.index.patternForTrip.get(tt.trip).getExemplar().getId().getId();
break;
default:
throw new RuntimeException("Unrecognized group by value");
}
tripsToConvert.put(key, tt);
} else {
// don't touch this trip
this.scheduledTrips.add(tt);
}
}
// loop over all the groups and create frequency entries
GROUPS: for (Map.Entry<String, Collection<TripTimes>> e : tripsToConvert.asMap().entrySet()) {
// get just the running services
List<TripTimes> group = e.getValue().stream().filter(tt -> servicesRunning.get(tt.serviceCode)).filter(tt -> windowStart < tt.getDepartureTime(0) && tt.getDepartureTime(0) < windowEnd).collect(Collectors.toList());
if (group.isEmpty())
continue GROUPS;
if (group.size() == 1) {
group.stream().forEach(scheduledTrips::add);
continue GROUPS;
}
// find the dominant pattern
TObjectIntMap<TripPattern> patternCount = new TObjectIntHashMap<>(5, 0.75f, 0);
group.forEach(tt -> patternCount.adjustOrPutValue(graph.index.patternForTrip.get(tt.trip), 1, 1));
int maxCount = 0;
TripPattern tripPattern = null;
for (TObjectIntIterator<TripPattern> it = patternCount.iterator(); it.hasNext(); ) {
it.advance();
if (it.value() > maxCount) {
maxCount = it.value();
tripPattern = it.key();
}
}
// find a stop that is common to all trip patterns. Sort the list so that the same common stop is always returned
NavigableSet<Stop> stops = new TreeSet<>((s1, s2) -> s1.getId().compareTo(s2.getId()));
stops.addAll(tripPattern.getStops());
patternCount.keySet().stream().forEach(p -> stops.retainAll(p.getStops()));
if (stops.isEmpty()) {
LOG.warn("Unable to find common stop for key {}, not converting to frequencies", e.getKey());
scheduledTrips.addAll(e.getValue());
continue GROUPS;
}
Stop stop = stops.stream().findFirst().get();
// determine the median frequency at this stop
// use a set to handle duplicated trips
TIntSet arrivalTimes = new TIntHashSet();
for (boolean filter : new boolean[] { true, false }) {
for (TripTimes tt : group) {
TripPattern tp = graph.index.patternForTrip.get(tt.trip);
int arrivalTime = tt.getArrivalTime(tp.getStops().indexOf(stop));
// however, if we apply the filter and end up with no trips at this stop, re-run with the filter disabled
if (windowStart < arrivalTime && arrivalTime < windowEnd || !filter)
arrivalTimes.add(arrivalTime);
}
// if we didn't find stops, continue, which will turn off the filter
if (arrivalTimes.size() > 1)
break;
}
// now convert to elapsed times
int[] arrivalTimeArray = arrivalTimes.toArray();
Arrays.sort(arrivalTimeArray);
int[] headway = new int[arrivalTimeArray.length - 1];
for (int i = 1; i < arrivalTimeArray.length; i++) {
headway[i - 1] = arrivalTimeArray[i] - arrivalTimeArray[i - 1];
}
Arrays.sort(headway);
// the headway that we will use
int aggregateHeadway;
if (assumption == RaptorWorkerTimetable.BoardingAssumption.WORST_CASE)
// simple: worst case analysis should use the worst case headway
aggregateHeadway = Ints.max(headway);
else {
// we want the average headway, but we we want the average of the headways weighted
// by themselves as if there is a two minute headway then a twenty-minute headway,
// customers are ten times as likely to experience the twenty minute headway
// (we want the average from the user's perspective, not the vehicle's perspective)
// This is a weighted average where the weight is the same as the headway so it simplifies
// to sum (headway^2) / sum(headway)
aggregateHeadway = IntStream.of(headway).map(h -> h * h).sum() / IntStream.of(headway).sum();
}
LOG.info("Headway for route {} ({}) in direction {}: {}min", tripPattern.route.getShortName(), tripPattern.route.getId().getId(), tripPattern.directionId, aggregateHeadway / 60);
// figure out running/dwell times based on the trips on this pattern
final TripPattern chosenTp = tripPattern;
List<TripTimes> candidates = group.stream().filter(tt -> graph.index.patternForTrip.get(tt.trip) == chosenTp).collect(Collectors.toList());
// transposed from what you'd expect: stops on the rows
int[][] hopTimes = new int[tripPattern.getStops().size() - 1][candidates.size()];
int[][] dwellTimes = new int[tripPattern.getStops().size()][candidates.size()];
int tripIndex = 0;
for (TripTimes tt : candidates) {
for (int stopIndex = 0; stopIndex < tripPattern.getStops().size(); stopIndex++) {
dwellTimes[stopIndex][tripIndex] = tt.getDwellTime(stopIndex);
if (stopIndex > 0)
hopTimes[stopIndex - 1][tripIndex] = tt.getArrivalTime(stopIndex) - tt.getDepartureTime(stopIndex - 1);
}
tripIndex++;
}
// collapse it down
int[] meanHopTimes = new int[tripPattern.getStops().size() - 1];
int hopIndex = 0;
for (int[] hop : hopTimes) {
meanHopTimes[hopIndex++] = IntStream.of(hop).sum() / hop.length;
}
int[] meanDwellTimes = new int[tripPattern.getStops().size()];
int dwellIndex = 0;
for (int[] dwell : dwellTimes) {
meanDwellTimes[dwellIndex++] = IntStream.of(dwell).sum() / dwell.length;
}
// phew! now let's make a frequency entry
TripTimes tt = new TripTimes(candidates.get(0));
int cumulative = 0;
for (int i = 0; i < tt.getNumStops(); i++) {
tt.updateArrivalTime(i, cumulative);
cumulative += meanDwellTimes[i];
tt.updateDepartureTime(i, cumulative);
if (i + 1 < tt.getNumStops())
cumulative += meanHopTimes[i];
}
FrequencyEntry fe = new FrequencyEntry(windowStart - 60 * 60 * 3, windowEnd + 60 * 60 * 3, aggregateHeadway, false, tt);
this.frequencyEntries.add(fe);
}
}
use of org.opentripplanner.routing.trippattern.TripTimes in project OpenTripPlanner by opentripplanner.
the class TripPattern method semanticHashString.
/**
* In most cases we want to use identity equality for Trips.
* However, in some cases we want a way to consistently identify trips across versions of a GTFS feed, when the
* feed publisher cannot ensure stable trip IDs. Therefore we define some additional hash functions.
* Hash collisions are theoretically possible, so these identifiers should only be used to detect when two
* trips are the same with a high degree of probability.
* An example application is avoiding double-booking of a particular bus trip for school field trips.
* Using Murmur hash function. see http://programmers.stackexchange.com/a/145633 for comparison.
*
* @param trip a trip object within this pattern, or null to hash the pattern itself independent any specific trip.
* @return the semantic hash of a Trip in this pattern as a printable String.
*
* TODO deal with frequency-based trips
*/
public String semanticHashString(Trip trip) {
HashFunction murmur = Hashing.murmur3_32();
BaseEncoding encoder = BaseEncoding.base64Url().omitPadding();
StringBuilder sb = new StringBuilder(50);
sb.append(encoder.encode(stopPattern.semanticHash(murmur).asBytes()));
if (trip != null) {
TripTimes tripTimes = scheduledTimetable.getTripTimes(trip);
if (tripTimes == null)
return null;
sb.append(':');
sb.append(encoder.encode(tripTimes.semanticHash(murmur).asBytes()));
}
return sb.toString();
}
use of org.opentripplanner.routing.trippattern.TripTimes in project OpenTripPlanner by opentripplanner.
the class PatternInterlineDwell method traverse.
@Override
public State traverse(State state0) {
RoutingRequest options = state0.getOptions();
Trip oldTrip = state0.getBackTrip();
Trip newTrip = options.arriveBy ? trips.inverse().get(oldTrip) : trips.get(oldTrip);
if (newTrip == null)
return null;
TripPattern newPattern;
TripTimes newTripTimes;
TripTimes oldTripTimes = state0.getTripTimes();
int arrivalTime;
int departureTime;
AgencyAndId tripId = state0.getTripId();
if (options.arriveBy) {
// traversing backward
newPattern = ((OnboardVertex) fromv).getTripPattern();
newTripTimes = newPattern.getResolvedTripTimes(newTrip, state0);
// FIXME with getLastTime method
arrivalTime = newTripTimes.getArrivalTime(newTripTimes.getNumStops() - 1);
departureTime = oldTripTimes.getDepartureTime(0);
} else {
// traversing forward
newPattern = ((OnboardVertex) tov).getTripPattern();
newTripTimes = newPattern.getResolvedTripTimes(newTrip, state0);
// FIXME with getLastTime method
arrivalTime = oldTripTimes.getArrivalTime(oldTripTimes.getNumStops() - 1);
departureTime = newTripTimes.getDepartureTime(0);
}
int boardStopIndex = options.arriveBy ? newPattern.stopPattern.size - 1 : 0;
if (!newTripTimes.tripAcceptable(state0, boardStopIndex))
return null;
int dwellTime = departureTime - arrivalTime;
if (dwellTime < 0)
return null;
StateEditor s1 = state0.edit(this);
s1.incrementTimeInSeconds(dwellTime);
// TODO check meaning
s1.setTripId(newTrip.getId());
// TODO check meaning
s1.setPreviousTrip(oldTrip);
s1.setTripTimes(newTripTimes);
s1.incrementWeight(dwellTime);
// Mode should not change.
return s1.makeState();
}
Aggregations