org.opentripplanner.updater.alerts.AlertsUpdateHandler Maven / Gradle / Ivy
package org.opentripplanner.updater.alerts;
import java.util.*;
import org.opentripplanner.model.FeedScopedId;
import org.opentripplanner.routing.alertpatch.Alert;
import org.opentripplanner.routing.alertpatch.AlertPatch;
import org.opentripplanner.routing.alertpatch.TimePeriod;
import org.opentripplanner.routing.services.AlertPatchService;
import org.opentripplanner.util.I18NString;
import org.opentripplanner.util.TranslatedString;
import org.opentripplanner.updater.GtfsRealtimeFuzzyTripMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.transit.realtime.GtfsRealtime;
import com.google.transit.realtime.GtfsRealtime.EntitySelector;
import com.google.transit.realtime.GtfsRealtime.FeedEntity;
import com.google.transit.realtime.GtfsRealtime.FeedMessage;
import com.google.transit.realtime.GtfsRealtime.TimeRange;
import com.google.transit.realtime.GtfsRealtime.TripDescriptor;
/**
* This updater only includes GTFS-Realtime Service Alert feeds.
* @author novalis
*
*/
public class AlertsUpdateHandler {
private static final Logger log = LoggerFactory.getLogger(AlertsUpdateHandler.class);
private String feedId;
private Set patchIds = new HashSet();
private AlertPatchService alertPatchService;
/** How long before the posted start of an event it should be displayed to users */
private long earlyStart;
/** Set only if we should attempt to match the trip_id from other data in TripDescriptor */
private GtfsRealtimeFuzzyTripMatcher fuzzyTripMatcher;
public void update(FeedMessage message) {
alertPatchService.expire(patchIds);
patchIds.clear();
for (FeedEntity entity : message.getEntityList()) {
if (!entity.hasAlert()) {
continue;
}
GtfsRealtime.Alert alert = entity.getAlert();
String id = entity.getId();
handleAlert(id, alert);
}
}
private void handleAlert(String id, GtfsRealtime.Alert alert) {
Alert alertText = new Alert();
alertText.alertDescriptionText = deBuffer(alert.getDescriptionText());
alertText.alertHeaderText = deBuffer(alert.getHeaderText());
alertText.alertUrl = deBuffer(alert.getUrl());
ArrayList periods = new ArrayList();
if(alert.getActivePeriodCount() > 0) {
long bestStartTime = Long.MAX_VALUE;
long lastEndTime = Long.MIN_VALUE;
for (TimeRange activePeriod : alert.getActivePeriodList()) {
final long realStart = activePeriod.hasStart() ? activePeriod.getStart() : 0;
final long start = activePeriod.hasStart() ? realStart - earlyStart : 0;
if (realStart > 0 && realStart < bestStartTime) {
bestStartTime = realStart;
}
final long end = activePeriod.hasEnd() ? activePeriod.getEnd() : Long.MAX_VALUE;
if (end > lastEndTime) {
lastEndTime = end;
}
periods.add(new TimePeriod(start, end));
}
if (bestStartTime != Long.MAX_VALUE) {
alertText.effectiveStartDate = new Date(bestStartTime * 1000);
}
if (lastEndTime != Long.MIN_VALUE) {
alertText.effectiveEndDate = new Date(lastEndTime * 1000);
}
} else {
// Per the GTFS-rt spec, if an alert has no TimeRanges, than it should always be shown.
periods.add(new TimePeriod(0, Long.MAX_VALUE));
}
for (EntitySelector informed : alert.getInformedEntityList()) {
if (fuzzyTripMatcher != null && informed.hasTrip()) {
TripDescriptor trip = fuzzyTripMatcher.match(feedId, informed.getTrip());
informed = informed.toBuilder().setTrip(trip).build();
}
String patchId = createId(id, informed);
String routeId = null;
if (informed.hasRouteId()) {
routeId = informed.getRouteId();
}
int direction;
if (informed.hasTrip() && informed.getTrip().hasDirectionId()) {
direction = informed.getTrip().getDirectionId();
} else {
direction = -1;
}
// TODO: The other elements of a TripDescriptor are ignored...
String tripId = null;
if (informed.hasTrip() && informed.getTrip().hasTripId()) {
tripId = informed.getTrip().getTripId();
}
String stopId = null;
if (informed.hasStopId()) {
stopId = informed.getStopId();
}
String agencyId = informed.getAgencyId();
if (informed.hasAgencyId()) {
agencyId = informed.getAgencyId().intern();
}
AlertPatch patch = new AlertPatch();
patch.setFeedId(feedId);
if (routeId != null) {
patch.setRoute(new FeedScopedId(feedId, routeId));
// Makes no sense to set direction if we don't have a route
if (direction != -1) {
patch.setDirectionId(direction);
}
}
if (tripId != null) {
patch.setTrip(new FeedScopedId(feedId, tripId));
}
if (stopId != null) {
patch.setStop(new FeedScopedId(feedId, stopId));
}
if (agencyId != null && routeId == null && tripId == null && stopId == null) {
patch.setAgencyId(agencyId);
}
patch.setTimePeriods(periods);
patch.setAlert(alertText);
patch.setId(patchId);
patchIds.add(patchId);
alertPatchService.apply(patch);
}
}
private String createId(String id, EntitySelector informed) {
return id + " "
+ (informed.hasAgencyId () ? informed.getAgencyId () : " null ") + " "
+ (informed.hasRouteId () ? informed.getRouteId () : " null ") + " "
+ (informed.hasTrip() && informed.getTrip().hasDirectionId() ?
informed.getTrip().hasDirectionId() : " null ") + " "
+ (informed.hasRouteType () ? informed.getRouteType () : " null ") + " "
+ (informed.hasStopId () ? informed.getStopId () : " null ") + " "
+ (informed.hasTrip() && informed.getTrip().hasTripId() ?
informed.getTrip().getTripId() : " null ");
}
/**
* convert a protobuf TranslatedString to a OTP TranslatedString
*
* @return A TranslatedString containing the same information as the input
*/
private I18NString deBuffer(GtfsRealtime.TranslatedString input) {
Map translations = new HashMap<>();
for (GtfsRealtime.TranslatedString.Translation translation : input.getTranslationList()) {
String language = translation.getLanguage();
String string = translation.getText();
translations.put(language, string);
}
return translations.isEmpty() ? null : TranslatedString.getI18NString(translations);
}
public void setFeedId(String feedId) {
if(feedId != null)
this.feedId = feedId.intern();
}
public void setAlertPatchService(AlertPatchService alertPatchService) {
this.alertPatchService = alertPatchService;
}
public long getEarlyStart() {
return earlyStart;
}
public void setEarlyStart(long earlyStart) {
this.earlyStart = earlyStart;
}
public void setFuzzyTripMatcher(GtfsRealtimeFuzzyTripMatcher fuzzyTripMatcher) {
this.fuzzyTripMatcher = fuzzyTripMatcher;
}
}