use of org.opentripplanner.model.Stop in project OpenTripPlanner by opentripplanner.
the class TimetableHelper method createUpdatedTripTimes.
/**
* Apply the TripUpdate to the appropriate TripTimes from this Timetable. The existing TripTimes
* must not be modified directly because they may be shared with the underlying
* scheduledTimetable, or other updated Timetables. The {@link TimetableSnapshot} performs the
* protective copying of this Timetable. It is not done in this update method to avoid
* repeatedly cloning the same Timetable when several updates are applied to it at once. We
* assume here that all trips in a timetable are from the same feed, which should always be the
* case.
*
* @param journey SIRI-ET EstimatedVehicleJourney
* @param timeZone time zone of trip update
* @param tripId
* @return new copy of updated TripTimes after TripUpdate has been applied on TripTimes of trip
* with the id specified in the trip descriptor of the TripUpdate; null if something
* went wrong
*/
public static TripTimes createUpdatedTripTimes(final Graph graph, Timetable timetable, EstimatedVehicleJourney journey, TimeZone timeZone, FeedScopedId tripId) {
if (journey == null) {
return null;
}
int tripIndex = timetable.getTripIndex(tripId);
if (tripIndex == -1) {
LOG.debug("tripId {} not found in pattern.", tripId);
return null;
}
final TripTimes existingTripTimes = timetable.getTripTimes(tripIndex);
TripTimes oldTimes = new TripTimes(existingTripTimes);
if (journey.isCancellation() != null && journey.isCancellation()) {
oldTimes.cancel();
return oldTimes;
}
EstimatedVehicleJourney.EstimatedCalls journeyEstimatedCalls = journey.getEstimatedCalls();
EstimatedVehicleJourney.RecordedCalls journeyRecordedCalls = journey.getRecordedCalls();
if (journeyEstimatedCalls == null) {
return null;
}
List<EstimatedCall> estimatedCalls = journeyEstimatedCalls.getEstimatedCalls();
List<RecordedCall> recordedCalls;
if (journeyRecordedCalls != null) {
recordedCalls = journeyRecordedCalls.getRecordedCalls();
} else {
recordedCalls = new ArrayList<>();
}
boolean stopPatternChanged = false;
Stop[] modifiedStops = timetable.pattern.stopPattern.stops;
Trip trip = getTrip(tripId, timetable);
List<StopTime> modifiedStopTimes = createModifiedStopTimes(timetable, oldTimes, journey, trip, new RoutingService(graph));
if (modifiedStopTimes == null) {
return null;
}
TripTimes newTimes = new TripTimes(trip, modifiedStopTimes, graph.deduplicator);
// Populate missing data from existing TripTimes
newTimes.serviceCode = oldTimes.serviceCode;
int callCounter = 0;
ZonedDateTime departureDate = null;
Set<Object> alreadyVisited = new HashSet<>();
boolean isJourneyPredictionInaccurate = (journey.isPredictionInaccurate() != null && journey.isPredictionInaccurate());
int departureFromPreviousStop = 0;
int lastArrivalDelay = 0;
int lastDepartureDelay = 0;
for (Stop stop : modifiedStops) {
boolean foundMatch = false;
for (RecordedCall recordedCall : recordedCalls) {
if (alreadyVisited.contains(recordedCall)) {
continue;
}
// Current stop is being updated
foundMatch = stop.getId().getId().equals(recordedCall.getStopPointRef().getValue());
if (!foundMatch && stop.isPartOfStation()) {
Stop alternativeStop = graph.index.getStopForId(new FeedScopedId(stop.getId().getFeedId(), recordedCall.getStopPointRef().getValue()));
if (alternativeStop != null && stop.isPartOfSameStationAs(alternativeStop)) {
foundMatch = true;
stopPatternChanged = true;
}
}
if (foundMatch) {
if (departureDate == null) {
departureDate = recordedCall.getAimedDepartureTime();
if (departureDate == null) {
departureDate = recordedCall.getAimedArrivalTime();
}
if (oldTimes.getDepartureTime(0) > 86400) {
// The "departure-date" for this trip is set to "yesterday" (or before) even though it actually departs "today"
// calculate number of offset-days
int dayOffsetCount = oldTimes.getDepartureTime(0) / 86400;
departureDate = departureDate.minusDays(dayOffsetCount);
}
}
// Flag as recorded
newTimes.setRecorded(callCounter, true);
if (recordedCall.isCancellation() != null) {
newTimes.setCancelledStop(callCounter, recordedCall.isCancellation());
}
newTimes.setDropoffType(callCounter, timetable.pattern.stopPattern.dropoffs[callCounter]);
newTimes.setPickupType(callCounter, timetable.pattern.stopPattern.pickups[callCounter]);
int arrivalTime = newTimes.getArrivalTime(callCounter);
int realtimeArrivalTime = arrivalTime;
if (recordedCall.getActualArrivalTime() != null) {
realtimeArrivalTime = calculateSecondsSinceMidnight(departureDate, recordedCall.getActualArrivalTime());
} else if (recordedCall.getExpectedArrivalTime() != null) {
realtimeArrivalTime = calculateSecondsSinceMidnight(departureDate, recordedCall.getExpectedArrivalTime());
} else if (recordedCall.getAimedArrivalTime() != null) {
realtimeArrivalTime = calculateSecondsSinceMidnight(departureDate, recordedCall.getAimedArrivalTime());
}
int arrivalDelay = realtimeArrivalTime - arrivalTime;
newTimes.updateArrivalDelay(callCounter, arrivalDelay);
lastArrivalDelay = arrivalDelay;
int departureTime = newTimes.getDepartureTime(callCounter);
int realtimeDepartureTime = departureTime;
if (recordedCall.getActualDepartureTime() != null) {
realtimeDepartureTime = calculateSecondsSinceMidnight(departureDate, recordedCall.getActualDepartureTime());
} else if (recordedCall.getExpectedDepartureTime() != null) {
realtimeDepartureTime = calculateSecondsSinceMidnight(departureDate, recordedCall.getExpectedDepartureTime());
} else if (recordedCall.getAimedDepartureTime() != null) {
realtimeDepartureTime = calculateSecondsSinceMidnight(departureDate, recordedCall.getAimedDepartureTime());
}
if (realtimeDepartureTime < realtimeArrivalTime) {
realtimeDepartureTime = realtimeArrivalTime;
}
int departureDelay = realtimeDepartureTime - departureTime;
newTimes.updateDepartureDelay(callCounter, departureDelay);
lastDepartureDelay = departureDelay;
departureFromPreviousStop = newTimes.getDepartureTime(callCounter);
alreadyVisited.add(recordedCall);
break;
}
}
if (!foundMatch) {
for (EstimatedCall estimatedCall : estimatedCalls) {
if (alreadyVisited.contains(estimatedCall)) {
continue;
}
// Current stop is being updated
foundMatch = stop.getId().getId().equals(estimatedCall.getStopPointRef().getValue());
if (!foundMatch && stop.isPartOfStation()) {
Stop alternativeStop = graph.index.getStopForId(new FeedScopedId(stop.getId().getFeedId(), estimatedCall.getStopPointRef().getValue()));
if (alternativeStop != null && stop.isPartOfSameStationAs(alternativeStop)) {
foundMatch = true;
stopPatternChanged = true;
}
}
if (foundMatch) {
if (departureDate == null) {
departureDate = estimatedCall.getAimedDepartureTime();
if (departureDate == null) {
departureDate = estimatedCall.getAimedArrivalTime();
}
}
if (estimatedCall.isCancellation() != null) {
newTimes.setCancelledStop(callCounter, estimatedCall.isCancellation());
}
boolean isCallPredictionInaccurate = estimatedCall.isPredictionInaccurate() != null && estimatedCall.isPredictionInaccurate();
// Set flag for inaccurate prediction if either call OR journey has inaccurate-flag set.
newTimes.setPredictionInaccurate(callCounter, (isJourneyPredictionInaccurate | isCallPredictionInaccurate));
// Update dropoff-/pickuptype only if status is cancelled
CallStatusEnumeration arrivalStatus = estimatedCall.getArrivalStatus();
if (arrivalStatus == CallStatusEnumeration.CANCELLED) {
newTimes.setDropoffType(callCounter, PICKDROP_NONE);
}
CallStatusEnumeration departureStatus = estimatedCall.getDepartureStatus();
if (departureStatus == CallStatusEnumeration.CANCELLED) {
newTimes.setPickupType(callCounter, PICKDROP_NONE);
}
int arrivalTime = newTimes.getArrivalTime(callCounter);
int realtimeArrivalTime = -1;
if (estimatedCall.getExpectedArrivalTime() != null) {
realtimeArrivalTime = calculateSecondsSinceMidnight(departureDate, estimatedCall.getExpectedArrivalTime());
} else if (estimatedCall.getAimedArrivalTime() != null) {
realtimeArrivalTime = calculateSecondsSinceMidnight(departureDate, estimatedCall.getAimedArrivalTime());
}
int departureTime = newTimes.getDepartureTime(callCounter);
int realtimeDepartureTime = departureTime;
if (estimatedCall.getExpectedDepartureTime() != null) {
realtimeDepartureTime = calculateSecondsSinceMidnight(departureDate, estimatedCall.getExpectedDepartureTime());
} else if (estimatedCall.getAimedDepartureTime() != null) {
realtimeDepartureTime = calculateSecondsSinceMidnight(departureDate, estimatedCall.getAimedDepartureTime());
}
if (realtimeArrivalTime == -1) {
realtimeArrivalTime = realtimeDepartureTime;
}
if (realtimeDepartureTime < realtimeArrivalTime) {
realtimeDepartureTime = realtimeArrivalTime;
}
int arrivalDelay = realtimeArrivalTime - arrivalTime;
newTimes.updateArrivalDelay(callCounter, arrivalDelay);
lastArrivalDelay = arrivalDelay;
int departureDelay = realtimeDepartureTime - departureTime;
newTimes.updateDepartureDelay(callCounter, departureDelay);
lastDepartureDelay = departureDelay;
departureFromPreviousStop = newTimes.getDepartureTime(callCounter);
alreadyVisited.add(estimatedCall);
break;
}
}
}
if (!foundMatch) {
if (timetable.pattern.stopPattern.pickups[callCounter] == PICKDROP_NONE && timetable.pattern.stopPattern.dropoffs[callCounter] == PICKDROP_NONE) {
// When newTimes contains stops without pickup/dropoff - set both arrival/departure to previous stop's departure
// This necessary to accommodate the case when delay is reduced/eliminated between to stops with pickup/dropoff, and
// multiple non-pickup/dropoff stops are in between.
newTimes.updateArrivalTime(callCounter, departureFromPreviousStop);
newTimes.updateDepartureTime(callCounter, departureFromPreviousStop);
} else {
int arrivalDelay = lastArrivalDelay;
int departureDelay = lastDepartureDelay;
if (lastArrivalDelay == 0 && lastDepartureDelay == 0) {
// No match has been found yet (i.e. still in RecordedCalls) - keep existing delays
arrivalDelay = existingTripTimes.getArrivalDelay(callCounter);
departureDelay = existingTripTimes.getDepartureDelay(callCounter);
}
newTimes.updateArrivalDelay(callCounter, arrivalDelay);
newTimes.updateDepartureDelay(callCounter, departureDelay);
}
departureFromPreviousStop = newTimes.getDepartureTime(callCounter);
}
callCounter++;
}
if (stopPatternChanged) {
// This update modified stopPattern
newTimes.setRealTimeState(RealTimeState.MODIFIED);
} else {
// This is the first update, and StopPattern has not been changed
newTimes.setRealTimeState(RealTimeState.UPDATED);
}
if (journey.isCancellation() != null && journey.isCancellation()) {
LOG.debug("Trip is cancelled");
newTimes.cancel();
}
if (!newTimes.timesIncreasing()) {
LOG.info("TripTimes are non-increasing after applying SIRI delay propagation - LineRef {}, TripId {}.", journey.getLineRef().getValue(), tripId);
return null;
}
if (newTimes.getNumStops() != timetable.pattern.stopPattern.stops.length) {
return null;
}
LOG.debug("A valid TripUpdate object was applied using the Timetable class update method.");
return newTimes;
}
use of org.opentripplanner.model.Stop in project OpenTripPlanner by opentripplanner.
the class TimetableHelper method createUpdatedTripTimes.
/**
* Apply the TripUpdate to the appropriate TripTimes from this Timetable. The existing TripTimes
* must not be modified directly because they may be shared with the underlying
* scheduledTimetable, or other updated Timetables. The {@link TimetableSnapshot} performs the
* protective copying of this Timetable. It is not done in this update method to avoid
* repeatedly cloning the same Timetable when several updates are applied to it at once. We
* assume here that all trips in a timetable are from the same feed, which should always be the
* case.
*
* @param activity SIRI-VM VehicleActivity
* @param timeZone time zone of trip update
* @param tripId
* @return new copy of updated TripTimes after TripUpdate has been applied on TripTimes of trip
* with the id specified in the trip descriptor of the TripUpdate; null if something
* went wrong
*/
public static TripTimes createUpdatedTripTimes(Timetable timetable, Graph graph, VehicleActivityStructure activity, TimeZone timeZone, FeedScopedId tripId) {
if (activity == null) {
return null;
}
MonitoredVehicleJourneyStructure mvj = activity.getMonitoredVehicleJourney();
int tripIndex = timetable.getTripIndex(tripId);
if (tripIndex == -1) {
LOG.trace("tripId {} not found in pattern.", tripId);
return null;
}
final TripTimes existingTripTimes = timetable.getTripTimes(tripIndex);
TripTimes newTimes = new TripTimes(existingTripTimes);
MonitoredCallStructure update = mvj.getMonitoredCall();
if (update == null) {
return null;
}
final List<Stop> stops = timetable.pattern.getStops();
VehicleActivityStructure.MonitoredVehicleJourney monitoredVehicleJourney = activity.getMonitoredVehicleJourney();
Duration delay = null;
if (monitoredVehicleJourney != null) {
delay = monitoredVehicleJourney.getDelay();
int updatedDelay = 0;
if (delay != null) {
updatedDelay = delay.getSign() * (delay.getHours() * 3600 + delay.getMinutes() * 60 + delay.getSeconds());
}
MonitoredCallStructure monitoredCall = monitoredVehicleJourney.getMonitoredCall();
if (monitoredCall != null && monitoredCall.getStopPointRef() != null) {
boolean matchFound = false;
int arrivalDelay = 0;
int departureDelay = 0;
for (int index = 0; index < newTimes.getNumStops(); ++index) {
if (!matchFound) {
// Delay is set on a single stop at a time. When match is found - propagate delay on all following stops
final Stop stop = stops.get(index);
matchFound = stop.getId().getId().equals(monitoredCall.getStopPointRef().getValue());
if (!matchFound && stop.isPartOfStation()) {
FeedScopedId alternativeId = new FeedScopedId(stop.getId().getFeedId(), monitoredCall.getStopPointRef().getValue());
Stop alternativeStop = graph.index.getStopForId(alternativeId);
if (alternativeStop != null && alternativeStop.isPartOfStation()) {
matchFound = stop.isPartOfSameStationAs(alternativeStop);
}
}
if (matchFound) {
arrivalDelay = departureDelay = updatedDelay;
} else {
/*
* If updated delay is less than previously set delay, the existing delay needs to be adjusted to avoid
* non-increasing times causing updates to be rejected. Will only affect historical data.
*/
arrivalDelay = Math.min(existingTripTimes.getArrivalDelay(index), updatedDelay);
departureDelay = Math.min(existingTripTimes.getDepartureDelay(index), updatedDelay);
}
}
newTimes.updateArrivalDelay(index, arrivalDelay);
newTimes.updateDepartureDelay(index, departureDelay);
}
}
}
if (!newTimes.timesIncreasing()) {
LOG.info("TripTimes are non-increasing after applying SIRI delay propagation - delay: {}", delay);
return null;
}
// If state is already MODIFIED - keep existing state
if (newTimes.getRealTimeState() != RealTimeState.MODIFIED) {
// Make sure that updated trip times have the correct real time state
newTimes.setRealTimeState(RealTimeState.UPDATED);
}
return newTimes;
}
use of org.opentripplanner.model.Stop in project OpenTripPlanner by opentripplanner.
the class LegacyGraphQLPlaceInterfaceTypeResolver method getType.
@Override
public GraphQLObjectType getType(TypeResolutionEnvironment environment) {
Object o = environment.getObject();
GraphQLSchema schema = environment.getSchema();
if (o instanceof BikePark)
return schema.getObjectType("BikePark");
if (o instanceof BikeRentalStation)
return schema.getObjectType("BikeRentalStation");
// if (o instanceof CarPark) return schema.getObjectType("CarPark");
if (o instanceof PatternAtStop)
return schema.getObjectType("DepartureRow");
if (o instanceof Stop)
return schema.getObjectType("Stop");
return null;
}
use of org.opentripplanner.model.Stop in project OpenTripPlanner by opentripplanner.
the class LegacyGraphQLQueryTypeImpl method node.
@Override
public DataFetcher<Object> node() {
return environment -> {
var args = new LegacyGraphQLTypes.LegacyGraphQLQueryTypeNodeArgs(environment.getArguments());
String type = args.getLegacyGraphQLId().getType();
String id = args.getLegacyGraphQLId().getId();
RoutingService routingService = environment.<LegacyGraphQLRequestContext>getContext().getRoutingService();
BikeRentalStationService bikerentalStationService = routingService.getBikerentalStationService();
switch(type) {
case "Agency":
return routingService.getAgencyForId(FeedScopedId.parseId(id));
case "Alert":
// TODO
return null;
case "BikePark":
return bikerentalStationService == null ? null : bikerentalStationService.getBikeParks().stream().filter(bikePark -> bikePark.id.equals(id)).findAny().orElse(null);
case "BikeRentalStation":
return bikerentalStationService == null ? null : bikerentalStationService.getBikeRentalStations().stream().filter(bikeRentalStation -> bikeRentalStation.id.equals(id)).findAny().orElse(null);
case "CarPark":
// TODO
return null;
case "Cluster":
// TODO
return null;
case "DepartureRow":
return PatternAtStop.fromId(routingService, id);
case "Pattern":
return routingService.getTripPatternForId(FeedScopedId.parseId(id));
case "placeAtDistance":
{
String[] parts = id.split(";");
Relay.ResolvedGlobalId internalId = new Relay().fromGlobalId(parts[1]);
Object place = node().get(DataFetchingEnvironmentImpl.newDataFetchingEnvironment(environment).source(new Object()).arguments(Map.of("id", internalId)).build());
return new PlaceAtDistance(place, Double.parseDouble(parts[0]));
}
case "Route":
return routingService.getRouteForId(FeedScopedId.parseId(id));
case "Stop":
return routingService.getStopForId(FeedScopedId.parseId(id));
case "Stoptime":
// TODO
return null;
case "stopAtDistance":
{
String[] parts = id.split(";");
Stop stop = routingService.getStopForId(FeedScopedId.parseId(parts[1]));
// TODO: Add geometry
return new StopAtDistance(stop, Integer.parseInt(parts[0]), null, null, null);
}
case "TicketType":
// TODO
return null;
case "Trip":
return routingService.getTripForId().get(FeedScopedId.parseId(id));
default:
return null;
}
};
}
use of org.opentripplanner.model.Stop in project OpenTripPlanner by opentripplanner.
the class DirectTransferAnalyzer method buildGraph.
@Override
public void buildGraph(Graph graph, HashMap<Class<?>, Object> extra, DataImportIssueStore issueStore) {
/* Initialize graph index which is needed by the nearby stop finder. */
graph.index = new GraphIndex(graph);
LOG.info("Analyzing transfers (this can be time consuming)...");
List<TransferInfo> directTransfersTooLong = new ArrayList<>();
List<TransferInfo> directTransfersNotFound = new ArrayList<>();
DirectGraphFinder nearbyStopFinderEuclidian = new DirectGraphFinder(graph);
StreetGraphFinder nearbyStopFinderStreets = new StreetGraphFinder(graph);
int stopsAnalyzed = 0;
for (TransitStopVertex originStopVertex : Iterables.filter(graph.getVertices(), TransitStopVertex.class)) {
if (++stopsAnalyzed % 1000 == 0) {
LOG.info("{} stops analyzed", stopsAnalyzed);
}
/* Find nearby stops by euclidean distance */
Coordinate c0 = originStopVertex.getCoordinate();
Map<Stop, StopAtDistance> stopsEuclidean = nearbyStopFinderEuclidian.findClosestStops(c0.y, c0.x, radiusMeters).stream().filter(t -> t.stop instanceof Stop).collect(Collectors.toMap(t -> (Stop) t.stop, t -> t));
/* Find nearby stops by street distance */
Map<Stop, StopAtDistance> stopsStreets = nearbyStopFinderStreets.findClosestStops(c0.y, c0.x, radiusMeters * RADIUS_MULTIPLIER).stream().filter(t -> t.stop instanceof Stop).collect(Collectors.toMap(t -> (Stop) t.stop, t -> t));
Stop originStop = originStopVertex.getStop();
/* Get stops found by both street and euclidean search */
List<Stop> stopsConnected = stopsEuclidean.keySet().stream().filter(t -> stopsStreets.keySet().contains(t) && t != originStop).collect(Collectors.toList());
/* Get stops found by euclidean search but not street search */
List<Stop> stopsUnconnected = stopsEuclidean.keySet().stream().filter(t -> !stopsStreets.keySet().contains(t) && t != originStop).collect(Collectors.toList());
for (Stop destStop : stopsConnected) {
StopAtDistance euclideanStop = stopsEuclidean.get(destStop);
StopAtDistance streetStop = stopsStreets.get(destStop);
TransferInfo transferInfo = new TransferInfo(originStop, destStop, euclideanStop.distance, streetStop.distance);
/* Log transfer where the street distance is too long compared to the euclidean distance */
if (transferInfo.ratio > MIN_RATIO_TO_LOG && transferInfo.streetDistance > MIN_STREET_DISTANCE_TO_LOG) {
directTransfersTooLong.add(transferInfo);
}
}
for (Stop destStop : stopsUnconnected) {
StopAtDistance euclideanStop = stopsEuclidean.get(destStop);
/* Log transfers that are found by euclidean search but not by street search */
directTransfersNotFound.add(new TransferInfo(originStop, destStop, euclideanStop.distance, -1));
}
}
/* Sort by street distance to euclidean distance ratio before adding to issues */
directTransfersTooLong.sort(Comparator.comparingDouble(t -> t.ratio));
Collections.reverse(directTransfersTooLong);
for (TransferInfo transferInfo : directTransfersTooLong) {
issueStore.add(new TransferRoutingDistanceTooLong(transferInfo.origin, transferInfo.destination, transferInfo.directDistance, transferInfo.streetDistance, transferInfo.ratio));
}
/* Sort by direct distance before adding to issues */
directTransfersNotFound.sort(Comparator.comparingDouble(t -> t.directDistance));
for (TransferInfo transferInfo : directTransfersNotFound) {
issueStore.add(new TransferCouldNotBeRouted(transferInfo.origin, transferInfo.destination, transferInfo.directDistance));
}
LOG.info("Done analyzing transfers. {} transfers could not be routed and {} transfers had a too long routing" + " distance.", directTransfersNotFound.size(), directTransfersTooLong.size());
}
Aggregations