Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Allow trip updates to change stops within stations #342

Open
wants to merge 9 commits into
base: dev-1.x
Choose a base branch
from
4 changes: 2 additions & 2 deletions src/main/java/org/opentripplanner/index/IndexAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ public Response getStoptimesForStop (@PathParam("stopId") String stopIdString,
@QueryParam("omitCanceled") boolean omitCanceled) {
Stop stop = index.stopForId.get(GtfsLibrary.convertIdFromString(stopIdString));
if (stop == null) return Response.status(Status.NOT_FOUND).entity(MSG_404).build();
return Response.status(Status.OK).entity(index.stopTimesForStop(stop, startTime, timeRange, numberOfDepartures, omitNonPickups, omitCanceled )).build();
return Response.status(Status.OK).entity(index.stopTimesForStop(stop, startTime, timeRange, numberOfDepartures, omitNonPickups, omitCanceled, true)).build();
}

/**
Expand All @@ -288,7 +288,7 @@ public Response getStoptimesForStopAndDate (@PathParam("stopId") String stopIdSt
return Response.status(Status.BAD_REQUEST).entity(MSG_400).build();
}

List<StopTimesInPattern> ret = index.getStopTimesForStop(stop, sd, omitNonPickups, false);
List<StopTimesInPattern> ret = index.getStopTimesForStop(stop, sd, omitNonPickups, false, true);
return Response.status(Status.OK).entity(ret).build();
}

Expand Down
41 changes: 35 additions & 6 deletions src/main/java/org/opentripplanner/index/IndexGraphQLSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -1446,6 +1446,12 @@ public IndexGraphQLSchema(GraphIndex index) {
.type(Scalars.GraphQLBoolean)
.defaultValue(false)
.build())
.argument(GraphQLArgument.newArgument()
.name("omitDeviated")
.description("If false, returns also trips that have been deviated to this stop")
.type(Scalars.GraphQLBoolean)
.defaultValue(true)
.build())
.dataFetcher(environment -> {
ServiceDate date;
try { // TODO: Add our own scalar types for at least serviceDate and AgencyAndId
Expand All @@ -1456,15 +1462,16 @@ public IndexGraphQLSchema(GraphIndex index) {
Stop stop = environment.getSource();
boolean omitNonPickups = environment.getArgument("omitNonPickups");
boolean omitCanceled = environment.getArgument("omitCanceled");
boolean omitDeviated = environment.getArgument("omitDeviated");
if (stop.getLocationType() == 1) {
// Merge all stops if this is a station
return index.stopsForParentStation
.get(stop.getId())
.stream()
.flatMap(singleStop -> index.getStopTimesForStop(singleStop, date, omitNonPickups, omitCanceled).stream())
.flatMap(singleStop -> index.getStopTimesForStop(singleStop, date, omitNonPickups, omitCanceled, omitDeviated).stream())
.collect(Collectors.toList());
}
return index.getStopTimesForStop(stop, date, omitNonPickups, omitCanceled);
return index.getStopTimesForStop(stop, date, omitNonPickups, omitCanceled, omitDeviated);
})
.build())
.field(GraphQLFieldDefinition.newFieldDefinition()
Expand Down Expand Up @@ -1500,6 +1507,12 @@ public IndexGraphQLSchema(GraphIndex index) {
.type(Scalars.GraphQLBoolean)
.defaultValue(true)
.build())
.argument(GraphQLArgument.newArgument()
.name("omitDeviated")
.description("If false, returns also trips that have been deviated to this stop")
.type(Scalars.GraphQLBoolean)
.defaultValue(true)
.build())
.dataFetcher(environment -> {
Stop stop = environment.getSource();
if (stop.getLocationType() == 1) {
Expand All @@ -1513,7 +1526,8 @@ public IndexGraphQLSchema(GraphIndex index) {
environment.getArgument("timeRange"),
environment.getArgument("numberOfDepartures"),
environment.getArgument("omitNonPickups"),
environment.getArgument("omitCanceled"))
environment.getArgument("omitCanceled"),
environment.getArgument("omitDeviated"))
.stream()
)
.collect(Collectors.toList());
Expand All @@ -1523,7 +1537,8 @@ public IndexGraphQLSchema(GraphIndex index) {
environment.getArgument("timeRange"),
environment.getArgument("numberOfDepartures"),
environment.getArgument("omitNonPickups"),
environment.getArgument("omitCanceled"));
environment.getArgument("omitCanceled"),
environment.getArgument("omitDeviated"));

})
.build())
Expand Down Expand Up @@ -1560,6 +1575,12 @@ public IndexGraphQLSchema(GraphIndex index) {
.type(Scalars.GraphQLBoolean)
.defaultValue(true)
.build())
.argument(GraphQLArgument.newArgument()
.name("omitDeviated")
.description("If false, returns also trips that have been deviated to this stop")
.type(Scalars.GraphQLBoolean)
.defaultValue(true)
.build())
.dataFetcher(environment -> {
Stop stop = environment.getSource();
Stream<StopTimesInPattern> stream;
Expand All @@ -1573,7 +1594,8 @@ public IndexGraphQLSchema(GraphIndex index) {
environment.getArgument("timeRange"),
environment.getArgument("numberOfDepartures"),
environment.getArgument("omitNonPickups"),
environment.getArgument("omitCanceled"))
environment.getArgument("omitCanceled"),
environment.getArgument("omitDeviated"))
.stream()
);
} else {
Expand All @@ -1583,7 +1605,8 @@ public IndexGraphQLSchema(GraphIndex index) {
environment.getArgument("timeRange"),
environment.getArgument("numberOfDepartures"),
environment.getArgument("omitNonPickups"),
environment.getArgument("omitCanceled")
environment.getArgument("omitCanceled"),
environment.getArgument("omitDeviated")
).stream();
}
return stream.flatMap(stoptimesWithPattern -> stoptimesWithPattern.times.stream())
Expand Down Expand Up @@ -1718,6 +1741,12 @@ public IndexGraphQLSchema(GraphIndex index) {
.dataFetcher(
environment -> ((TripTimeShort) environment.getSource()).stopSequence)
.build())
.field(GraphQLFieldDefinition.newFieldDefinition()
.name("deviationStop")
.description("The stop where this stop time has been deviated to. \n`null` if the trip has not been deviated to another stop.")
.type(stopType)
.dataFetcher(environment -> index.stopForId.get(((TripTimeShort)environment.getSource()).deviationStop))
.build())
.build();

tripType = GraphQLObjectType.newObject()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class TripTimeShort {
public double serviceAreaRadius;
public String serviceArea;
public int stopSequence;
public FeedScopedId deviationStop;

/**
* This is stop-specific, so the index i is a stop index, not a hop index.
Expand All @@ -61,6 +62,7 @@ public TripTimeShort(TripTimes tt, int i, Stop stop) {
serviceAreaRadius = tt.getServiceAreaRadius(i);
serviceArea = tt.getServiceArea(i);
stopSequence = tt.getStopSequence(i);
deviationStop = tt.getDeviationStop(i);
}

public TripTimeShort(TripTimes tt, int i, Stop stop, ServiceDay sd) {
Expand Down
11 changes: 9 additions & 2 deletions src/main/java/org/opentripplanner/model/StopPattern.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import com.google.common.hash.HashCode;
Expand Down Expand Up @@ -54,6 +56,8 @@ public class StopPattern implements Serializable {
public final int[] pickups;
public final int[] dropoffs;

public final List<Stop>[] alternateStops;

/** GTFS-Flex specific fields; will be null unless GTFS-Flex dataset is in use. */
private StopPatternFlexFields flexFields;

Expand Down Expand Up @@ -95,24 +99,27 @@ public String toString() {
* @param stopTimes List of StopTimes; assumes that stopTimes are already sorted by time.
* @param deduplicator Deduplicator. If null, do not deduplicate arrays.
*/
public StopPattern (List<StopTime> stopTimes, Deduplicator deduplicator) {
public StopPattern (List<StopTime> stopTimes, Map<FeedScopedId, List<Stop>> alternateStops, Deduplicator deduplicator) {
this.size = stopTimes.size();
if (size == 0) {
this.stops = new Stop[size];
this.pickups = new int[0];
this.dropoffs = new int[0];
this.alternateStops = new List[0];
return;
}
stops = new Stop[size];
int[] pickups = new int[size];
int[] dropoffs = new int[size];
this.alternateStops = new List[size];
for (int i = 0; i < size; ++i) {
StopTime stopTime = stopTimes.get(i);
stops[i] = stopTime.getStop();
// should these just be booleans? anything but 1 means pick/drop is allowed.
// pick/drop messages could be stored in individual trips
pickups[i] = stopTime.getPickupType();
dropoffs[i] = stopTime.getDropOffType();
this.alternateStops[i] = alternateStops.getOrDefault(new FeedScopedId(stopTime.getStop().getId().getAgencyId(), stopTime.getStop().getParentStation()), Collections.emptyList());
}
/*
* TriMet GTFS has many trips that differ only in the pick/drop status of their initial and
Expand All @@ -139,7 +146,7 @@ public StopPattern (List<StopTime> stopTimes, Deduplicator deduplicator) {
* @param stopTimes List of StopTimes; assumes that stopTimes are already sorted by time.
*/
public StopPattern (List<StopTime> stopTimes) {
this(stopTimes, null);
this(stopTimes, Collections.emptyMap(), null);
}

/**
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/org/opentripplanner/routing/edgetype/Timetable.java
Original file line number Diff line number Diff line change
Expand Up @@ -550,13 +550,24 @@ public TripTimes createUpdatedTripTimes(TripUpdate tripUpdate, TimeZone timeZone
Integer delay = null;
Integer firstDelay = null;
boolean hasMatched = false;
FeedScopedId deviationStop = null;
for (int i = 0; i < numStops; i++) {
boolean match = false;
if (update != null) {
if (update.hasStopSequence()) {
match = update.getStopSequence() == newTimes.getStopSequence(i);
} else if (update.hasStopId()) {
match = pattern.getStop(i).getId().getId().equals(update.getStopId());
//Check if the trip has been deviated to another stop within the same station
if (!match && pattern.stopPattern.alternateStops[i] != null) {
for (Stop alternateStop : pattern.stopPattern.alternateStops[i]) {
if (alternateStop.getId().getId().equals(update.getStopId())) {
deviationStop = alternateStop.getId();
match = true;
break;
}
}
}
}
}

Expand All @@ -565,6 +576,9 @@ public TripTimes createUpdatedTripTimes(TripUpdate tripUpdate, TimeZone timeZone
StopTimeUpdate.ScheduleRelationship scheduleRelationship =
update.hasScheduleRelationship() ? update.getScheduleRelationship()
: StopTimeUpdate.ScheduleRelationship.SCHEDULED;
if (deviationStop != null) {
newTimes.updateDeviationStop(i, deviationStop);
}
if (scheduleRelationship ==
StopTimeUpdate.ScheduleRelationship.NO_DATA) {
newTimes.updateArrivalDelay(i, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimap;
import org.apache.commons.math3.util.Pair;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.Geometry;
Expand Down Expand Up @@ -82,16 +83,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

// Filtering out (removing) stoptimes from a trip forces us to either have two copies of that list,
// or do all the steps within one loop over trips. It would be clearer if there were multiple loops over the trips.
Expand Down Expand Up @@ -377,8 +371,11 @@ public void run(Graph graph) {

boolean hasFlexService = stopTimes.stream().anyMatch(this::stopTimeHasFlex);

Map<FeedScopedId, List<Stop>> alternateStops = transitService.getAllStops().stream()
.filter(stop -> stop.getLocationType() == 1)
.collect(Collectors.toMap(Stop::getId, transitService::getStopsForStation));
/* Get the existing TripPattern for this filtered StopPattern, or create one. */
StopPattern stopPattern = new StopPattern(stopTimes, graph.deduplicator);
StopPattern stopPattern = new StopPattern(stopTimes, alternateStops, graph.deduplicator);
if (hasFlexService) {
stopPattern.setFlexFields(new StopPatternFlexFields(stopTimes, flexAreasById, graph.deduplicator));
}
Expand Down
Loading