
org.opentripplanner.routing.impl.TransitAlertServiceImpl Maven / Gradle / Ivy
The newest version!
package org.opentripplanner.routing.impl;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import java.time.LocalDate;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.opentripplanner.routing.alertpatch.EntityKey;
import org.opentripplanner.routing.alertpatch.EntitySelector;
import org.opentripplanner.routing.alertpatch.StopCondition;
import org.opentripplanner.routing.alertpatch.TransitAlert;
import org.opentripplanner.routing.services.TransitAlertService;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.model.timetable.Direction;
import org.opentripplanner.transit.service.TimetableRepository;
/**
* This is the primary implementation of TransitAlertService, which actually retains its own set
* of TransitAlerts and indexes them for fast lookup by which transit entity is affected.
* The only other implementation exists just to combine several instances of this primary
* implementation into one.
* TODO RT_AB: investigate why each updater has its own service instead of taking turns
* sequentially writing to a single service. Original design was for all data and indexes to be
* associated with the Graph or transit model (i.e. the object graph of instances of the transit
* model) and for updaters to submit write tasks that would patch the current version in a
* sequential way, e.g. "add these 10 alerts", "remove these 5 alerts", etc.
*
* When an alert is added with more than one transit entity, e.g. a Stop and a Trip, both conditions
* must be met for the alert to be displayed. This is the case in both the Norwegian interpretation
* of SIRI, and the GTFS-RT alerts specification.
*/
public class TransitAlertServiceImpl implements TransitAlertService {
private final TimetableRepository timetableRepository;
private Multimap alerts = HashMultimap.create();
public TransitAlertServiceImpl(TimetableRepository timetableRepository) {
this.timetableRepository = timetableRepository;
}
@Override
public void setAlerts(Collection alerts) {
// FIXME RT_AB: this is patched live by updaters while in use (being read) by other threads
// performing trip planning. The single-action assignment helps a bit, but the map can be
// swapped out while the delegating service is in the middle of multiple calls that read from
// it. The consistent approach would be to duplicate the entire service, update it
// copy-on-write, and swap in the entire service after the update.
Multimap newAlerts = HashMultimap.create();
for (TransitAlert alert : alerts) {
for (EntitySelector entity : alert.entities()) {
newAlerts.put(entity.key(), alert);
}
}
this.alerts = newAlerts;
}
@Override
public Collection getAllAlerts() {
return new HashSet<>(alerts.values());
}
@Override
public TransitAlert getAlertById(FeedScopedId id) {
return alerts
.values()
.stream()
.filter(transitAlert -> transitAlert.getId().equals(id))
.findAny()
.orElse(null);
}
@Override
public Collection getStopAlerts(
FeedScopedId stopId,
Set stopConditions
) {
Set result = new HashSet<>();
EntitySelector.Stop entitySelector = new EntitySelector.Stop(stopId, stopConditions);
for (TransitAlert alert : alerts.get(entitySelector.key())) {
if (alert.entities().stream().anyMatch(selector -> selector.matches(entitySelector))) {
result.add(alert);
}
}
if (result.isEmpty()) {
// Search for alerts on parent-stop
if (timetableRepository != null) {
var quay = timetableRepository.getSiteRepository().getRegularStop(stopId);
if (quay != null) {
// TODO - SIRI: Add alerts from parent- and multimodal-stops
/*
if ( quay.isPartOfStation()) {
// Add alerts for parent-station
result.addAll(patchesByStop.getOrDefault(quay.getParentStationFeedScopedId(), Collections.emptySet()));
}
if (quay.getMultiModalStation() != null) {
// Add alerts for multimodal-station
result.addAll(patchesByStop.getOrDefault(new FeedScopedId(stop.getAgencyId(), quay.getMultiModalStation()), Collections.emptySet()));
}
*/
}
}
}
return result;
}
@Override
public Collection getRouteAlerts(FeedScopedId route) {
return alerts.get(new EntityKey.Route(route));
}
@Override
public Collection getTripAlerts(FeedScopedId trip, LocalDate serviceDate) {
Set result = new HashSet<>();
EntitySelector.Trip entitySelector = new EntitySelector.Trip(trip, serviceDate);
for (TransitAlert alert : alerts.get(entitySelector.key())) {
if (alert.entities().stream().anyMatch(selector -> selector.matches(entitySelector))) {
result.add(alert);
}
}
return result;
}
@Override
public Collection getAgencyAlerts(FeedScopedId agency) {
return alerts.get(new EntityKey.Agency(agency));
}
@Override
public Collection getStopAndRouteAlerts(
FeedScopedId stop,
FeedScopedId route,
Set stopConditions
) {
Set result = new HashSet<>();
EntitySelector.StopAndRoute entitySelector = new EntitySelector.StopAndRoute(
stop,
route,
stopConditions
);
for (TransitAlert alert : alerts.get(entitySelector.key())) {
if (alert.entities().stream().anyMatch(selector -> selector.matches(entitySelector))) {
result.add(alert);
}
}
return result;
}
@Override
public Collection getStopAndTripAlerts(
FeedScopedId stop,
FeedScopedId trip,
LocalDate serviceDate,
Set stopConditions
) {
Set result = new HashSet<>();
EntitySelector.StopAndTrip entitySelector = new EntitySelector.StopAndTrip(
stop,
trip,
serviceDate,
stopConditions
);
for (TransitAlert alert : alerts.get(entitySelector.key())) {
if (alert.entities().stream().anyMatch(selector -> selector.matches(entitySelector))) {
result.add(alert);
}
}
return result;
}
@Override
public Collection getRouteTypeAndAgencyAlerts(int routeType, FeedScopedId agency) {
return alerts.get(new EntityKey.RouteTypeAndAgency(agency, routeType));
}
@Override
public Collection getRouteTypeAlerts(int routeType, String feedId) {
return alerts.get(new EntityKey.RouteType(feedId, routeType));
}
@Override
public Collection getDirectionAndRouteAlerts(
Direction direction,
FeedScopedId route
) {
return alerts.get(new EntityKey.DirectionAndRoute(route, direction));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy