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

Add Stop Time Interpolation #399

Merged
merged 10 commits into from
Nov 21, 2023
Merged

Add Stop Time Interpolation #399

merged 10 commits into from
Nov 21, 2023

Conversation

philip-cline
Copy link
Collaborator

@philip-cline philip-cline commented Nov 5, 2023

Checklist

  • Appropriate branch selected (all PRs must first be merged to dev before they can be merged to master)
  • Any modified or new methods or classes have helpful JavaDoc and code is thoroughly commented
  • The description lists all applicable issues this PR seeks to resolve
  • The description lists any configuration setting(s) that differ from the default settings
  • All tests and CI builds passing

Description

This PR supports interpolating stop times based on timepoints in the normalizeStopTimesForPattern method.

Copy link

@AdrianaCeric AdrianaCeric left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code works well under a few test cases! I am not too familiar with Java, but we could probably make the error handling more robust in this function:

@@ -755,8 +755,9 @@ private int updateStopTimesForPatternStop(ObjectNode patternStop, int previousTr
*
* TODO? add param Set<String> serviceIdFilters service_id values to filter trips on
*/
private int updateStopTimesForPatternStops(List<PatternStop> patternStops) throws SQLException {
private int updateStopTimesForPatternStops(List<PatternStop> patternStops, boolean interpolateStopTimes) throws SQLException {
PatternStop firstPatternStop = patternStops.iterator().next();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Unlikely edge case) Say I accidentally make a pattern with zero stops and click Normalize stop times it throws a NoSuchElemenException and a long error.
image
Is there a way to check for an empty list and to return a cleaner error message?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is best addressed by preventing the behaviour client side, although I'll defer to other reviewers if they'd like some additional checks

timepointNumber++;
previousShapeDistTraveled = currentTimepoint.shape_dist_traveled;
PatternStop nextTimePoint = timepoints.get(timepointNumber);
timepointSpeed = (nextTimePoint.shape_dist_traveled - currentTimepoint.shape_dist_traveled) / nextTimePoint.default_travel_time;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check if default_travel_time can't be zero before doing this division?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, yeah I'll add a case for that

Copy link
Collaborator

@miles-grant-ibigroup miles-grant-ibigroup left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good enough for now, I'd either add a lot of tests and error cases or a comment that this could use a good refactor

int timepointNumber = 0;
double timepointSpeed = 0;
double previousShapeDistTraveled = 0;
PatternStop currentTimepoint = patternStops.get(0); // First stop must be a timepoint
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a check here to verify that the item exists and is a timepoint

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, added in b585c9d

boolean isTimepoint = patternStop.timepoint == 1;
// If we have reached a timepoint (which is not the last), we calculate the speed between it and the next timepoint.
if (isTimepoint && interpolateStopTimes && timepointNumber < timepoints.size()-1){
timepointNumber++;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Include the ++ in the if

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was gonna do this, but was worried it made the code less readable.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated this in b585c9d

if (isTimepoint && interpolateStopTimes && timepointNumber < timepoints.size()-1){
timepointNumber++;
previousShapeDistTraveled = currentTimepoint.shape_dist_traveled;
PatternStop nextTimePoint = timepoints.get(timepointNumber);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make sure this isn't null

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in b585c9d

Copy link
Collaborator

@br648 br648 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initial feedback. If we can keep all updates to JdbcTableWriter in separate methods it might help with managing this class. I might also get you to merge this update into dev-flex!

@@ -788,10 +789,28 @@ private int updateStopTimesForPatternStops(List<PatternStop> patternStops) throw
final BatchTracker stopTimesTracker = new BatchTracker("stop_times", updateStopTimeStatement);
for (String tripId : timesForTripIds.keySet()) {
// Initialize travel time with previous stop time value.
int cumulativeTravelTime = timesForTripIds.get(tripId);
int cumulativeTravelTime = timesForTripIds.get(tripId), timepointNumber = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declare timepointNumber on a separate line e.g. int timepointNumber = 0;

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 36f3043

@@ -788,10 +789,28 @@ private int updateStopTimesForPatternStops(List<PatternStop> patternStops) throw
final BatchTracker stopTimesTracker = new BatchTracker("stop_times", updateStopTimeStatement);
for (String tripId : timesForTripIds.keySet()) {
// Initialize travel time with previous stop time value.
int cumulativeTravelTime = timesForTripIds.get(tripId);
int cumulativeTravelTime = timesForTripIds.get(tripId), timepointNumber = 0;
double timepointSpeed = 0, previousShapeDistTraveled = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declare previousShapeDistTraveled on a separate line.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 36f3043

for (PatternStop patternStop : patternStops) {
boolean isTimepoint = patternStop.timepoint == 1;
// If we have reached a timepoint (which is not the last), we calculate the speed between it and the next timepoint.
if (isTimepoint && interpolateStopTimes && timepointNumber++ < timepoints.size()-1){
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of injecting the new code into this for loop, look to create a new interpolation method which will carry out the required tasks and return the updated travelTime value.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use a new method in 36f3043

PatternStop nextTimePoint = timepoints.get(timepointNumber);
if (nextTimePoint == null) throw new IllegalStateException("Stop time interpolation is not possible with null timepoints.");
timepointSpeed = nextTimePoint.default_travel_time == Entity.INT_MISSING ? 0
: (nextTimePoint.shape_dist_traveled - currentTimepoint.shape_dist_traveled) / nextTimePoint.default_travel_time;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit. Line up the ? and : makes this easier to read.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 36f3043

@Test
public void canNormalizePatternStopTimes() throws IOException, SQLException, InvalidNamespaceException {
// Store Table and Class values for use in test.
private static String normalizeStopsForPattern(PatternStopDTO[] patternStops, int updatedStopSequence, boolean interpolateStopTimes, int initialTravelTime, int updatedTravelTime, int startTime, String patternId) throws SQLException, InvalidNamespaceException, IOException {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reduce the length of this line by putting each param on a new line.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 36f3043

@br648 br648 assigned philip-cline and unassigned br648 Nov 17, 2023
@philip-cline philip-cline removed their assignment Nov 20, 2023
Copy link
Collaborator

@br648 br648 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is getting there, the method is much better. I just have some concerns over the impact of default values.

*/
public int normalizeStopTimesForPattern(int id, int beginWithSequence) throws SQLException {
return normalizeStopTimesForPattern(id, beginWithSequence, false);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit. New line after closing bracket.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in dbd09ce

@@ -745,6 +752,33 @@ private int updateStopTimesForPatternStop(ObjectNode patternStop, int previousTr
return travelTime + dwellTime;
}

private int interpolateTimesFromTimepoints(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method comment to describe what this is doing.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in dbd09ce

throw new IllegalStateException("All timepoints must have a default travel time specified.");
}

double timepointSpeed = (nextTimepoint.shape_dist_traveled - lastTimepoint.shape_dist_traveled) / nextTimepoint.default_travel_time;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If shape_dist_traveled is not defined and set to Double.MIN_VALUE, isn't that going to cause issues here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I added checks for every pattern stop and both timepoints in dbd09ce

// Gather travel/dwell time for pattern stop (being sure to check for missing values).
int travelTime = patternStop.default_travel_time == Entity.INT_MISSING ? 0 : patternStop.default_travel_time;
if (!isTimepoint && interpolateStopTimes) {
// Override travel time if we're interpolating between timepoints
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit. Full stop.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in dbd09ce

// Override travel time if we're interpolating between timepoints
travelTime = interpolateTimesFromTimepoints(patternStop, timepoints, timepointNumber, previousShapeDistTraveled);
}
previousShapeDistTraveled += patternStop.shape_dist_traveled;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If any shape_dist_traveled is set to Double.MIN_VALUE, will that cause an issue?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

likewise yes, I added a check here too dbd09ce

if (!isTimepoint && interpolateStopTimes) {
// We don't want to increment the true cumulative travel time because that adjusts the timepoint
// times later in the pattern.
// TODO? We ignore dwell times in interpolation calculations right now.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODOs have a habit of not being done. Can dwell times be incorporated easily now? If not, explain why they are ignored and remove the TODO.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added an explanation of why this is left out, and an explanation of how it could be implemented. Right now there's not much reason to since it doesn't fit the use case which is driving this feature.

@br648 br648 assigned philip-cline and unassigned br648 Nov 21, 2023
@philip-cline philip-cline assigned br648 and unassigned philip-cline Nov 21, 2023
Copy link
Collaborator

@br648 br648 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@br648 br648 assigned philip-cline and unassigned br648 Nov 21, 2023
@philip-cline philip-cline merged commit b657dfe into dev Nov 21, 2023
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants