@@ -326,6 +326,7 @@ func ParseRealtime(content []byte, opts *ParseRealtimeOptions) (*Realtime, error
326
326
}
327
327
result .Trips = append (result .Trips , * trip )
328
328
}
329
+
329
330
for vehicleID , vehicle := range vehiclesByID {
330
331
if tripID , ok := vehicleIDToTripID [vehicleID ]; ok {
331
332
vehicle .Trip = tripsById [tripID ]
@@ -525,23 +526,88 @@ func parseAlert(ID string, alert *gtfsrt.Alert, opts *ParseRealtimeOptions) (*Al
525
526
}
526
527
var informedEntities []AlertInformedEntity
527
528
var trips []Trip
529
+ var informedRoutes = make (map [string ]bool )
530
+ var informedRoutesFromTripIDs = make (map [string ]map [DirectionID ]bool )
528
531
for _ , entity := range alert .GetInformedEntity () {
529
532
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 {
531
556
AgencyID : entity .AgencyId ,
532
557
RouteID : entity .RouteId ,
533
558
RouteType : parseRouteType_GTFSRealtime (entity .RouteType ),
534
559
DirectionID : parseDirectionID_GTFSRealtime (entity .DirectionId ),
535
560
TripID : tripIDOrNil ,
536
561
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 ) {
539
570
trips = append (trips , Trip {
540
571
ID : * tripIDOrNil ,
541
572
IsEntityInMessage : false ,
542
573
})
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
+ })
543
608
}
544
609
}
610
+
545
611
gtfsAlert := & Alert {
546
612
ID : ID ,
547
613
ActivePeriods : activePeriods ,
@@ -573,3 +639,14 @@ func convertOptionalTimestamp(in *uint64, timezone *time.Location) *time.Time {
573
639
out := time .Unix (int64 (* in ), 0 ).In (timezone )
574
640
return & out
575
641
}
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