-
Notifications
You must be signed in to change notification settings - Fork 2
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
Send bus notifications if first leg is bus #264
base: dev
Are you sure you want to change the base?
Changes from 8 commits
89d5074
4df263b
f135775
188f531
2b3e003
820584f
eeea569
c0d0672
b6a8536
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,7 @@ | |
import static org.opentripplanner.middleware.utils.GeometryUtils.getDistance; | ||
import static org.opentripplanner.middleware.utils.GeometryUtils.isPointBetween; | ||
import static org.opentripplanner.middleware.utils.ItineraryUtils.isBusLeg; | ||
import static org.opentripplanner.middleware.utils.ItineraryUtils.legsMatch; | ||
|
||
/** | ||
* Locate the traveler in relation to the nearest step or destination and provide the appropriate instructions. | ||
|
@@ -57,22 +58,31 @@ public static String getInstruction( | |
) { | ||
if (hasRequiredWalkLeg(travelerPosition)) { | ||
if (hasRequiredTripStatus(tripStatus)) { | ||
TripInstruction tripInstruction = alignTravelerToTrip(travelerPosition, isStartOfTrip, tripStatus); | ||
TripInstruction tripInstruction = alignTravelerToTrip(travelerPosition, isStartOfTrip); | ||
if (tripInstruction != null) { | ||
return tripInstruction.build(); | ||
} | ||
} | ||
|
||
if (tripStatus.equals(TripStatus.DEVIATED)) { | ||
TripInstruction tripInstruction = getBackOnTrack(travelerPosition, isStartOfTrip, tripStatus); | ||
TripInstruction tripInstruction = getBackOnTrack(travelerPosition, isStartOfTrip); | ||
if (tripInstruction != null) { | ||
return tripInstruction.build(); | ||
} | ||
} | ||
} else if (hasRequiredTransitLeg(travelerPosition) && hasRequiredTripStatus(tripStatus)) { | ||
TripInstruction tripInstruction = alignTravelerToTransitTrip(travelerPosition); | ||
if (tripInstruction != null) { | ||
return tripInstruction.build(); | ||
} else if (hasRequiredTransitLeg(travelerPosition)) { | ||
if (hasRequiredTripStatus(tripStatus)) { | ||
TripInstruction tripInstruction = alignTravelerToTransitTrip(travelerPosition); | ||
if (tripInstruction != null) { | ||
return tripInstruction.build(); | ||
} | ||
} | ||
|
||
if (tripStatus.equals(TripStatus.DEVIATED)) { | ||
TripInstruction tripInstruction = getBackOnTrack(travelerPosition, isStartOfTrip); | ||
if (tripInstruction != null) { | ||
return tripInstruction.build(); | ||
} | ||
} | ||
} | ||
return NO_INSTRUCTION; | ||
|
@@ -111,17 +121,38 @@ private static boolean hasRequiredTripStatus(TripStatus tripStatus) { | |
@Nullable | ||
private static TripInstruction getBackOnTrack( | ||
TravelerPosition travelerPosition, | ||
boolean isStartOfTrip, | ||
TripStatus tripStatus | ||
boolean isStartOfTrip | ||
) { | ||
TripInstruction instruction = alignTravelerToTrip(travelerPosition, isStartOfTrip, tripStatus); | ||
TripInstruction instruction = alignTravelerToTrip(travelerPosition, isStartOfTrip); | ||
if (instruction != null && instruction.hasInstruction()) { | ||
return instruction; | ||
} | ||
Step nearestStep = snapToWaypoint(travelerPosition, travelerPosition.expectedLeg.steps); | ||
return (nearestStep != null) | ||
? new DeviatedInstruction(nearestStep.streetName, travelerPosition.locale) | ||
: null; | ||
return getDeviatedInstruction(travelerPosition); | ||
} | ||
|
||
/** | ||
* If the traveler has deviated, attempt to provide instructions to get back on track. | ||
*/ | ||
@Nullable | ||
private static TripInstruction getDeviatedInstruction(TravelerPosition travelerPosition) { | ||
if (!isBusLeg(travelerPosition.expectedLeg)) { | ||
Step nearestStep = snapToWaypoint(travelerPosition, travelerPosition.expectedLeg.steps); | ||
return (nearestStep != null) | ||
? new DeviatedInstruction(nearestStep.streetName, travelerPosition.locale) | ||
: null; | ||
} else if (atStartOfTransitTrip(travelerPosition)) { | ||
// Only provide instruction if at the start of a trip. | ||
String busStopName = getBusStopName(travelerPosition.expectedLeg); | ||
return (busStopName != null) | ||
? new DeviatedInstruction(busStopName, travelerPosition.locale) | ||
: null; | ||
} | ||
return null; | ||
} | ||
|
||
@Nullable | ||
private static String getBusStopName(Leg busLeg) { | ||
return (busLeg.from != null && busLeg.from.name != null) ? busLeg.from.name : null; | ||
} | ||
|
||
/** | ||
|
@@ -130,16 +161,12 @@ private static TripInstruction getBackOnTrack( | |
@Nullable | ||
public static TripInstruction alignTravelerToTrip( | ||
TravelerPosition travelerPosition, | ||
boolean isStartOfTrip, | ||
TripStatus tripStatus | ||
boolean isStartOfTrip | ||
) { | ||
Locale locale = travelerPosition.locale; | ||
|
||
if (isApproachingEndOfLeg(travelerPosition)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is at the start of a trip where the first leg is transit, there is no approaching the end of the leg. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @binh-dam-ibigroup If possible can you provide a trip which starts with a transit leg? I'm going to edit an existing trip (walk-to-bus-transition.json) to test with, but would prefer a real-world exampl There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @binh-dam-ibigroup this has grown a bit from my initial take on it! I think this covers the what is needed. |
||
if (isBusLeg(travelerPosition.nextLeg) && isWithinOperationalNotifyWindow(travelerPosition)) { | ||
BusOperatorActions | ||
.getDefault() | ||
.handleSendNotificationAction(tripStatus, travelerPosition); | ||
if (sendBusNotification(travelerPosition)) { | ||
// Regardless of whether the notification is sent or qualifies, provide a 'wait for bus' instruction. | ||
return new WaitForTransitInstruction(travelerPosition.nextLeg, travelerPosition.currentTime, locale); | ||
} | ||
|
@@ -157,6 +184,31 @@ public static TripInstruction alignTravelerToTrip( | |
return null; | ||
} | ||
|
||
/** | ||
* Send bus notification if the first leg is a bus leg or approaching a bus leg and within the notify window. | ||
*/ | ||
public static boolean sendBusNotification(TravelerPosition travelerPosition) { | ||
Leg busLeg = atStartOfTransitTrip(travelerPosition) ? travelerPosition.expectedLeg : travelerPosition.nextLeg; | ||
if (isBusLeg(busLeg) && isWithinOperationalNotifyWindow(travelerPosition.currentTime, busLeg)) { | ||
BusOperatorActions | ||
.getDefault() | ||
.handleSendNotificationAction(travelerPosition, busLeg); | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
/** | ||
* A trip which starts with a transit leg and the traveler is on that leg. | ||
*/ | ||
private static boolean atStartOfTransitTrip(TravelerPosition travelerPosition) { | ||
return | ||
travelerPosition.expectedLeg != null && | ||
travelerPosition.firstLegOfTrip != null && | ||
travelerPosition.firstLegOfTrip.transitLeg && | ||
legsMatch(travelerPosition.expectedLeg, travelerPosition.firstLegOfTrip); | ||
} | ||
|
||
/** | ||
* Align the traveler's position to the nearest transit stop or destination. | ||
*/ | ||
|
@@ -166,6 +218,11 @@ public static TripInstruction alignTravelerToTransitTrip(TravelerPosition travel | |
Leg expectedLeg = travelerPosition.expectedLeg; | ||
String finalStop = expectedLeg.to.name; | ||
|
||
if (sendBusNotification(travelerPosition)) { | ||
// Regardless of whether the notification is sent or qualifies, provide a 'wait for bus' instruction. | ||
return new WaitForTransitInstruction(expectedLeg, travelerPosition.currentTime, locale); | ||
} | ||
|
||
if (isApproachingEndOfLeg(travelerPosition)) { | ||
return new GetOffHereTransitInstruction(finalStop, locale); | ||
} | ||
|
@@ -211,6 +268,14 @@ private static boolean isApproachingEndOfLeg(TravelerPosition travelerPosition) | |
return getDistanceToEndOfLeg(travelerPosition) <= TRIP_INSTRUCTION_UPCOMING_RADIUS; | ||
} | ||
|
||
/** | ||
* Is the traveler at the start of a leg. | ||
*/ | ||
public static boolean isAtStartOfLeg(TravelerPosition travelerPosition) { | ||
Coordinates legDestination = new Coordinates(travelerPosition.expectedLeg.from); | ||
return getDistance(travelerPosition.currentPosition, legDestination) <= TRIP_INSTRUCTION_UPCOMING_RADIUS; | ||
} | ||
|
||
/** | ||
* Is the traveler at the leg destination. | ||
*/ | ||
|
@@ -222,11 +287,11 @@ public static boolean isAtEndOfLeg(TravelerPosition travelerPosition) { | |
* Make sure the traveler is on schedule or ahead of schedule (but not too far) to be within an operational window | ||
* for the bus service. | ||
*/ | ||
public static boolean isWithinOperationalNotifyWindow(TravelerPosition travelerPosition) { | ||
var busDepartureTime = getBusDepartureTime(travelerPosition.nextLeg); | ||
public static boolean isWithinOperationalNotifyWindow(Instant currentTime, Leg busLeg) { | ||
var busDepartureTime = getBusDepartureTime(busLeg); | ||
return | ||
(travelerPosition.currentTime.equals(busDepartureTime) || travelerPosition.currentTime.isBefore(busDepartureTime)) && | ||
ACCEPTABLE_AHEAD_OF_SCHEDULE_IN_MINUTES >= getMinutesAheadOfDeparture(travelerPosition.currentTime, busDepartureTime); | ||
(currentTime.equals(busDepartureTime) || currentTime.isBefore(busDepartureTime)) && | ||
ACCEPTABLE_AHEAD_OF_SCHEDULE_IN_MINUTES >= getMinutesAheadOfDeparture(currentTime, busDepartureTime); | ||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,12 @@ | ||
package org.opentripplanner.middleware.triptracker.interactions.busnotifiers; | ||
|
||
import org.opentripplanner.middleware.otp.response.Leg; | ||
import org.opentripplanner.middleware.triptracker.TravelerPosition; | ||
import org.opentripplanner.middleware.triptracker.TripStatus; | ||
|
||
public interface BusOperatorInteraction { | ||
|
||
void sendNotification(TripStatus tripStatus, TravelerPosition travelerPosition); | ||
void sendNotification(TravelerPosition travelerPosition, Leg busLeg); | ||
|
||
void cancelNotification(TravelerPosition travelerPosition); | ||
void cancelNotification(TravelerPosition travelerPosition, Leg busLeg); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bonus points question: I didn't see an equivalent to "Head to " on an itinerary where the first leg is bus and tracking is started at a location away from the bus stop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@binh-dam-ibigroup Update here: eeea569
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@binh-dam-ibigroup I will hold off merging until you have "approved" this addition.