
org.opentripplanner.model.impl.OtpTransitServiceBuilder Maven / Gradle / Ivy
The newest version!
package org.opentripplanner.model.impl;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.opentripplanner.ext.flex.trip.FlexTrip;
import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore;
import org.opentripplanner.gtfs.mapping.StaySeatedNotAllowed;
import org.opentripplanner.model.FeedInfo;
import org.opentripplanner.model.Frequency;
import org.opentripplanner.model.OtpTransitService;
import org.opentripplanner.model.ShapePoint;
import org.opentripplanner.model.Timetable;
import org.opentripplanner.model.TripStopTimes;
import org.opentripplanner.model.calendar.CalendarServiceData;
import org.opentripplanner.model.calendar.ServiceCalendar;
import org.opentripplanner.model.calendar.ServiceCalendarDate;
import org.opentripplanner.model.calendar.ServiceDateInterval;
import org.opentripplanner.model.calendar.impl.CalendarServiceDataFactoryImpl;
import org.opentripplanner.model.transfer.ConstrainedTransfer;
import org.opentripplanner.model.transfer.TransferPoint;
import org.opentripplanner.routing.api.request.framework.TimePenalty;
import org.opentripplanner.service.vehicleparking.model.VehicleParking;
import org.opentripplanner.transit.model.basic.Notice;
import org.opentripplanner.transit.model.framework.AbstractTransitEntity;
import org.opentripplanner.transit.model.framework.DefaultEntityById;
import org.opentripplanner.transit.model.framework.EntityById;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.model.framework.ImmutableEntityById;
import org.opentripplanner.transit.model.network.GroupOfRoutes;
import org.opentripplanner.transit.model.network.Route;
import org.opentripplanner.transit.model.network.StopPattern;
import org.opentripplanner.transit.model.network.TripPattern;
import org.opentripplanner.transit.model.organization.Agency;
import org.opentripplanner.transit.model.organization.Branding;
import org.opentripplanner.transit.model.organization.Operator;
import org.opentripplanner.transit.model.site.AreaStop;
import org.opentripplanner.transit.model.site.BoardingArea;
import org.opentripplanner.transit.model.site.Entrance;
import org.opentripplanner.transit.model.site.FareZone;
import org.opentripplanner.transit.model.site.GroupOfStations;
import org.opentripplanner.transit.model.site.GroupStop;
import org.opentripplanner.transit.model.site.MultiModalStation;
import org.opentripplanner.transit.model.site.Pathway;
import org.opentripplanner.transit.model.site.PathwayNode;
import org.opentripplanner.transit.model.site.RegularStop;
import org.opentripplanner.transit.model.site.Station;
import org.opentripplanner.transit.model.timetable.Trip;
import org.opentripplanner.transit.model.timetable.TripOnServiceDate;
import org.opentripplanner.transit.model.timetable.TripTimes;
import org.opentripplanner.transit.service.SiteRepository;
import org.opentripplanner.transit.service.SiteRepositoryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class is responsible for building a {@link OtpTransitService}. The instance returned by the
* {@link #build()} method is read only, and this class provide a mutable collections to construct a
* {@link OtpTransitService} instance.
*/
public class OtpTransitServiceBuilder {
private static final Logger LOG = LoggerFactory.getLogger(OtpTransitServiceBuilder.class);
private final EntityById agenciesById = new DefaultEntityById<>();
private final List calendarDates = new ArrayList<>();
private final List calendars = new ArrayList<>();
private final List feedInfos = new ArrayList<>();
private final List frequencies = new ArrayList<>();
private final SiteRepositoryBuilder siteRepositoryBuilder;
private final Multimap noticeAssignments =
ArrayListMultimap.create();
private final EntityById operatorsById = new DefaultEntityById<>();
private final List pathways = new ArrayList<>();
private final EntityById routesById = new DefaultEntityById<>();
private final Multimap shapePoints = ArrayListMultimap.create();
private final EntityById entrancesById = new DefaultEntityById<>();
private final EntityById pathwayNodesById = new DefaultEntityById<>();
private final EntityById boardingAreasById = new DefaultEntityById<>();
private final TripStopTimes stopTimesByTrip = new TripStopTimes();
private final Map flexTimePenalties = new HashMap<>();
private final EntityById fareZonesById = new DefaultEntityById<>();
private final List transfers = new ArrayList<>();
private final List staySeatedNotAllowed = new ArrayList<>();
private final EntityById tripsById = new DefaultEntityById<>();
private final Multimap tripPatterns = ArrayListMultimap.create();
private final EntityById> flexTripsById = new DefaultEntityById<>();
private final EntityById brandingsById = new DefaultEntityById<>();
private final Multimap groupsOfRoutesByRouteId =
ArrayListMultimap.create();
private final EntityById tripOnServiceDates = new DefaultEntityById<>();
private final EntityById groupOfRouteById = new DefaultEntityById<>();
private final List vehicleParkings = new ArrayList<>();
private final Map stopsByScheduledStopPoints = new HashMap<>();
private final DataImportIssueStore issueStore;
public OtpTransitServiceBuilder(SiteRepository siteRepository, DataImportIssueStore issueStore) {
this.siteRepositoryBuilder = siteRepository.withContext();
this.issueStore = issueStore;
}
/* Accessors */
public EntityById getAgenciesById() {
return agenciesById;
}
public List getCalendarDates() {
return calendarDates;
}
public List getCalendars() {
return calendars;
}
public List getFeedInfos() {
return feedInfos;
}
public List getFrequencies() {
return frequencies;
}
public SiteRepositoryBuilder siteRepository() {
return siteRepositoryBuilder;
}
public ImmutableEntityById getGroupsOfStationsById() {
return siteRepositoryBuilder.groupOfStationById();
}
public ImmutableEntityById getMultiModalStationsById() {
return siteRepositoryBuilder.multiModalStationById();
}
/**
* get multimap of Notices by the TransitEntity id (Multiple types; hence the Serializable).
* Entities that might have Notices are Routes, Trips, Stops and StopTimes.
*/
public Multimap getNoticeAssignments() {
return noticeAssignments;
}
public EntityById getOperatorsById() {
return operatorsById;
}
public List getPathways() {
return pathways;
}
public EntityById getRoutes() {
return routesById;
}
public Multimap getShapePoints() {
return shapePoints;
}
public ImmutableEntityById getStations() {
return siteRepositoryBuilder.stationById();
}
public ImmutableEntityById getStops() {
return siteRepositoryBuilder.regularStopsById();
}
public EntityById getEntrances() {
return entrancesById;
}
public EntityById getPathwayNodes() {
return pathwayNodesById;
}
public EntityById getBoardingAreas() {
return boardingAreasById;
}
public ImmutableEntityById getAreaStops() {
return siteRepositoryBuilder.areaStopById();
}
public ImmutableEntityById getGroupStops() {
return siteRepositoryBuilder.groupStopById();
}
public TripStopTimes getStopTimesSortedByTrip() {
return stopTimesByTrip;
}
public Map getFlexTimePenalty() {
return flexTimePenalties;
}
public EntityById getFareZonesById() {
return fareZonesById;
}
public List getTransfers() {
return transfers;
}
public List getStaySeatedNotAllowed() {
return staySeatedNotAllowed;
}
public EntityById getTripsById() {
return tripsById;
}
public Multimap getTripPatterns() {
return tripPatterns;
}
public EntityById> getFlexTripsById() {
return flexTripsById;
}
public EntityById getBrandingsById() {
return brandingsById;
}
public Multimap getGroupsOfRoutesByRouteId() {
return groupsOfRoutesByRouteId;
}
public EntityById getGroupOfRouteById() {
return groupOfRouteById;
}
public EntityById getTripOnServiceDates() {
return tripOnServiceDates;
}
public CalendarServiceData buildCalendarServiceData() {
return CalendarServiceDataFactoryImpl.createCalendarServiceData(
getCalendarDates(),
getCalendars()
);
}
/**
* The list of parking lots contained in the transit data (so far only NeTEx).
* Note that parking lots can also be sourced from OSM data as well as realtime updaters.
*/
public List vehicleParkings() {
return vehicleParkings;
}
/**
* @see org.opentripplanner.transit.service.TimetableRepository#findStopByScheduledStopPoint(FeedScopedId)
*/
public Map stopsByScheduledStopPoints() {
return stopsByScheduledStopPoints;
}
public OtpTransitService build() {
return new OtpTransitServiceImpl(this);
}
/**
* Limit the transit service to a time period removing calendar dates and services outside the
* period. If a service is start before and/or ends after the period then the service is modified
* to match the period.
*/
public void limitServiceDays(ServiceDateInterval periodLimit) {
if (periodLimit.isUnbounded()) {
LOG.info("Limiting transit service is skipped, the period is unbounded.");
return;
}
LOG.warn("Limiting transit service days to time period: {}", periodLimit);
int orgSize = calendarDates.size();
calendarDates.removeIf(c -> !periodLimit.include(c.getDate()));
logRemove("ServiceCalendarDate", orgSize, calendarDates.size(), "Outside time period.");
List keepCal = new ArrayList<>();
for (ServiceCalendar calendar : calendars) {
if (calendar.getPeriod().overlap(periodLimit)) {
calendar.setPeriod(calendar.getPeriod().intersection(periodLimit));
keepCal.add(calendar);
}
}
orgSize = calendars.size();
if (orgSize != keepCal.size()) {
calendars.clear();
calendars.addAll(keepCal);
logRemove("ServiceCalendar", orgSize, calendars.size(), "Outside time period.");
}
removeEntitiesWithInvalidReferences();
LOG.info("Limiting transit service days to time period complete.");
}
/**
* Add a mapping from a scheduled stop point to the regular stop.
*/
public void addStopByScheduledStopPoint(FeedScopedId sspid, RegularStop stop) {
stopsByScheduledStopPoints.put(sspid, stop);
}
/**
* Find all serviceIds in both CalendarServices and CalendarServiceDates.
*/
Set findAllServiceIds() {
Set serviceIds = new HashSet<>();
for (ServiceCalendar calendar : getCalendars()) {
serviceIds.add(calendar.getServiceId());
}
for (ServiceCalendarDate date : getCalendarDates()) {
serviceIds.add(date.getServiceId());
}
return serviceIds;
}
private static void logRemove(String type, int orgSize, int newSize, String reason) {
if (orgSize == newSize) {
return;
}
LOG.info("{} of {} {}(s) removed. Reason: {}", orgSize - newSize, orgSize, type, reason);
}
/**
* Check all relations and remove entities which reference none existing entries. This may happen
* as a result of inconsistent data or by deliberate removal of elements in the builder.
*/
private void removeEntitiesWithInvalidReferences() {
removeTripsWithNoneExistingServiceIds();
removeStopTimesForNoneExistingTrips();
fixOrRemovePatternsWhichReferenceNoneExistingTrips();
removeTransfersForNoneExistingTrips();
removeTripOnServiceDateForNonExistingTrip();
}
/** Remove all trips which reference none existing service ids */
private void removeTripsWithNoneExistingServiceIds() {
Set serviceIds = findAllServiceIds();
int orgSize = tripsById.size();
tripsById.removeIf(
t -> !serviceIds.contains(t.getServiceId()),
t ->
issueStore.add(
"RemovedMissingServiceIdTrip",
"Removed trip %s as service id %s does not exist",
t.getId(),
t.getServiceId()
)
);
logRemove("Trip", orgSize, tripsById.size(), "Trip service id does not exist.");
}
/** Remove all stopTimes which reference none existing trips */
private void removeStopTimesForNoneExistingTrips() {
int orgSize = stopTimesByTrip.size();
stopTimesByTrip.removeIf(t -> !tripsById.containsKey(t.getId()));
logRemove("StopTime", orgSize, stopTimesByTrip.size(), "StopTime trip does not exist.");
}
/** Remove none existing trips from patterns and then remove empty patterns */
private void fixOrRemovePatternsWhichReferenceNoneExistingTrips() {
int orgSize = tripPatterns.size();
List> removePatterns = new ArrayList<>();
List updatedPatterns = new ArrayList<>();
for (Map.Entry e : tripPatterns.entries()) {
TripPattern ptn = e.getValue();
Set tripTimesToBeRemoved = ptn
.getScheduledTimetable()
.getTripTimes()
.stream()
.filter(tripTimes -> !tripsById.containsKey(tripTimes.getTrip().getId()))
.collect(Collectors.toUnmodifiableSet());
if (!tripTimesToBeRemoved.isEmpty()) {
removePatterns.add(e);
Timetable updatedTimetable = ptn
.getScheduledTimetable()
.copyOf()
.removeAllTripTimes(tripTimesToBeRemoved)
.build();
TripPattern updatedPattern = ptn.copy().withScheduledTimeTable(updatedTimetable).build();
if (!updatedTimetable.getTripTimes().isEmpty()) {
updatedPatterns.add(updatedPattern);
} else {
issueStore.add(
"RemovedEmptyTripPattern",
"Removed trip pattern %s as it contains no trips",
updatedPattern.getId()
);
}
}
}
for (Map.Entry it : removePatterns) {
tripPatterns.remove(it.getKey(), it.getValue());
}
for (TripPattern tripPattern : updatedPatterns) {
tripPatterns.put(tripPattern.getStopPattern(), tripPattern);
}
logRemove("TripPattern", orgSize, tripPatterns.size(), "No trips for pattern exist.");
}
/** Remove all transfers which reference none existing trips */
private void removeTransfersForNoneExistingTrips() {
int orgSize = transfers.size();
transfers.removeIf(this::transferTripReferencesDoNotExist);
logRemove("Trip", orgSize, transfers.size(), "Transfer to/from trip does not exist.");
}
/**
* Remove TripOnServiceDates if there are no trips using them
*/
private void removeTripOnServiceDateForNonExistingTrip() {
int orgSize = tripOnServiceDates.size();
for (TripOnServiceDate tripOnServiceDate : tripOnServiceDates.values()) {
if (!tripsById.containsKey(tripOnServiceDate.getTrip().getId())) {
logRemove(
"TripOnServiceDate",
orgSize,
tripOnServiceDates.size(),
"Trip for TripOnServiceDate does not exist."
);
}
}
}
/** Return {@code true} if the from/to trip reference is none null, but do not exist. */
private boolean transferTripReferencesDoNotExist(ConstrainedTransfer t) {
return (
transferPointTripReferenceDoesNotExist(t.getFrom()) ||
transferPointTripReferenceDoesNotExist(t.getTo())
);
}
/**
* Return {@code true} if the point is a trip-transfer-point and the trip reference is
* missing.
*/
private boolean transferPointTripReferenceDoesNotExist(TransferPoint point) {
if (!point.isTripTransferPoint()) {
return false;
}
var trip = point.asTripTransferPoint().getTrip();
return !tripsById.containsKey(trip.getId());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy