Skip to content

Commit 7d1e8f6

Browse files
committed
Handle informed trip descriptors with empty trip ids and non-empty route
1 parent 65dace7 commit 7d1e8f6

File tree

2 files changed

+588
-4
lines changed

2 files changed

+588
-4
lines changed

realtime.go

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ func ParseRealtime(content []byte, opts *ParseRealtimeOptions) (*Realtime, error
326326
}
327327
result.Trips = append(result.Trips, *trip)
328328
}
329+
329330
for vehicleID, vehicle := range vehiclesByID {
330331
if tripID, ok := vehicleIDToTripID[vehicleID]; ok {
331332
vehicle.Trip = tripsById[tripID]
@@ -525,23 +526,88 @@ func parseAlert(ID string, alert *gtfsrt.Alert, opts *ParseRealtimeOptions) (*Al
525526
}
526527
var informedEntities []AlertInformedEntity
527528
var trips []Trip
529+
var informedRoutes = make(map[string]bool)
530+
var informedRoutesFromTripIDs = make(map[string]map[DirectionID]bool)
528531
for _, entity := range alert.GetInformedEntity() {
529532
tripIDOrNil := parseOptionalTripDescriptor(entity.Trip, opts)
530-
informedEntities = append(informedEntities, AlertInformedEntity{
533+
534+
// Handle the case where the trip descriptor's doesn't map to a trip instance but the route ID is not.
535+
// In such cases, we can inform the route in one or both directions.
536+
// Such cases are not handled by the GTFS-realtime spec, but occur in some feeds such
537+
// as the MTA bus alerts feed. See: https://groups.google.com/g/mtadeveloperresources/c/pn_EupBj1nY
538+
if tripIDOrNil != nil && !tripIDUniquelyIdentifiesTrip(tripIDOrNil) && tripIDOrNil.RouteID != "" {
539+
if tripIDOrNil.DirectionID == DirectionID_Unspecified {
540+
informedRoutesFromTripIDs[tripIDOrNil.RouteID] = map[DirectionID]bool{
541+
DirectionID_False: true,
542+
DirectionID_True: true,
543+
}
544+
} else {
545+
if _, ok := informedRoutesFromTripIDs[tripIDOrNil.RouteID]; !ok {
546+
informedRoutesFromTripIDs[tripIDOrNil.RouteID] = map[DirectionID]bool{}
547+
}
548+
informedRoutesFromTripIDs[tripIDOrNil.RouteID][tripIDOrNil.DirectionID] = true
549+
}
550+
}
551+
if entity.RouteId != nil {
552+
informedRoutes[*entity.RouteId] = true
553+
}
554+
555+
informedEntity := AlertInformedEntity{
531556
AgencyID: entity.AgencyId,
532557
RouteID: entity.RouteId,
533558
RouteType: parseRouteType_GTFSRealtime(entity.RouteType),
534559
DirectionID: parseDirectionID_GTFSRealtime(entity.DirectionId),
535560
TripID: tripIDOrNil,
536561
StopID: entity.StopId,
537-
})
538-
if tripIDOrNil != nil {
562+
}
563+
564+
// Ensure at least one entity is informed
565+
if !alertInformedEntityInformsAtLeastOneEntity(informedEntity) {
566+
continue
567+
}
568+
569+
if tripIDUniquelyIdentifiesTrip(tripIDOrNil) {
539570
trips = append(trips, Trip{
540571
ID: *tripIDOrNil,
541572
IsEntityInMessage: false,
542573
})
574+
} else {
575+
// Clear the trip ID if it doesn't uniquely identify a trip
576+
informedEntity.TripID = nil
577+
}
578+
579+
informedEntities = append(informedEntities, informedEntity)
580+
}
581+
582+
for routeID, directions := range informedRoutesFromTripIDs {
583+
if informedRoutes[routeID] {
584+
continue
585+
}
586+
587+
routeIDCopy := routeID
588+
if directions[DirectionID_False] && directions[DirectionID_True] {
589+
// Inform the route in both directions
590+
informedEntities = append(informedEntities, AlertInformedEntity{
591+
RouteID: &routeIDCopy,
592+
RouteType: RouteType_Unknown,
593+
})
594+
} else {
595+
// Inform the route in one direction
596+
var informedDirection DirectionID
597+
if directions[DirectionID_False] {
598+
informedDirection = DirectionID_False
599+
} else {
600+
informedDirection = DirectionID_True
601+
}
602+
603+
informedEntities = append(informedEntities, AlertInformedEntity{
604+
RouteID: &routeIDCopy,
605+
RouteType: RouteType_Unknown,
606+
DirectionID: informedDirection,
607+
})
543608
}
544609
}
610+
545611
gtfsAlert := &Alert{
546612
ID: ID,
547613
ActivePeriods: activePeriods,
@@ -573,3 +639,14 @@ func convertOptionalTimestamp(in *uint64, timezone *time.Location) *time.Time {
573639
out := time.Unix(int64(*in), 0).In(timezone)
574640
return &out
575641
}
642+
643+
func alertInformedEntityInformsAtLeastOneEntity(alertInformedEntity AlertInformedEntity) bool {
644+
return (alertInformedEntity.AgencyID != nil || alertInformedEntity.RouteID != nil ||
645+
alertInformedEntity.RouteType != RouteType_Unknown || tripIDUniquelyIdentifiesTrip(alertInformedEntity.TripID) ||
646+
alertInformedEntity.StopID != nil)
647+
}
648+
649+
func tripIDUniquelyIdentifiesTrip(tripID *TripID) bool {
650+
return tripID != nil &&
651+
(tripID.ID != "" || (tripID.RouteID != "" && tripID.DirectionID != DirectionID_Unspecified && tripID.HasStartTime && tripID.HasStartDate))
652+
}

0 commit comments

Comments
 (0)