
org.opentripplanner.model.impl.OtpTransitServiceBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of otp Show documentation
Show all versions of otp Show documentation
The OpenTripPlanner multimodal journey planning system
package org.opentripplanner.model.impl;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.opentripplanner.ext.flex.trip.FlexTrip;
import org.opentripplanner.graph_builder.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.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.transit.model.basic.Notice;
import org.opentripplanner.transit.model.framework.AbstractTransitEntity;
import org.opentripplanner.transit.model.framework.EntityById;
import org.opentripplanner.transit.model.framework.FeedScopedId;
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.service.StopModel;
import org.opentripplanner.transit.service.StopModelBuilder;
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 EntityById<>();
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 StopModelBuilder stopModelBuilder;
private final Multimap noticeAssignments = ArrayListMultimap.create();
private final EntityById operatorsById = new EntityById<>();
private final List pathways = new ArrayList<>();
private final EntityById routesById = new EntityById<>();
private final Multimap shapePoints = ArrayListMultimap.create();
private final EntityById entrancesById = new EntityById<>();
private final EntityById pathwayNodesById = new EntityById<>();
private final EntityById boardingAreasById = new EntityById<>();
private final TripStopTimes stopTimesByTrip = new TripStopTimes();
private final EntityById fareZonesById = new EntityById<>();
private final List transfers = new ArrayList<>();
private final List staySeatedNotAllowed = new ArrayList<>();
private final EntityById tripsById = new EntityById<>();
private final Multimap tripPatterns = ArrayListMultimap.create();
private final EntityById> flexTripsById = new EntityById<>();
private final EntityById brandingsById = new EntityById<>();
private final Multimap groupsOfRoutesByRouteId = ArrayListMultimap.create();
private final EntityById tripOnServiceDates = new EntityById<>();
private final EntityById groupOfRouteById = new EntityById<>();
private final DataImportIssueStore issueStore;
public OtpTransitServiceBuilder(DataImportIssueStore issueStore) {
this.stopModelBuilder = StopModel.of();
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 StopModelBuilder stopModelBuilder() {
return stopModelBuilder;
}
public EntityById getGroupsOfStationsById() {
return stopModelBuilder().groupOfStationById();
}
public EntityById getMultiModalStationsById() {
return stopModelBuilder().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 EntityById getStations() {
return stopModelBuilder().stationById();
}
public EntityById getStops() {
return stopModelBuilder().regularStopsById();
}
public EntityById getEntrances() {
return entrancesById;
}
public EntityById getPathwayNodes() {
return pathwayNodesById;
}
public EntityById getBoardingAreas() {
return boardingAreasById;
}
public EntityById getAreaStops() {
return stopModelBuilder().areaStopById();
}
public EntityById getGroupStops() {
return stopModelBuilder().groupStopById();
}
public TripStopTimes getStopTimesSortedByTrip() {
return stopTimesByTrip;
}
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()
);
}
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.");
}
/**
* 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<>();
for (Map.Entry e : tripPatterns.entries()) {
TripPattern ptn = e.getValue();
ptn.removeTrips(t -> !tripsById.containsKey(t.getId()));
if (ptn.scheduledTripsAsStream().findAny().isEmpty()) {
removePatterns.add(e);
}
}
for (Map.Entry it : removePatterns) {
tripPatterns.remove(it.getKey(), it.getValue());
issueStore.add(
"RemovedEmptyTripPattern",
"Removed trip pattern %s as it contains no trips",
it.getValue().getId()
);
}
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 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