All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentripplanner.model.impl.OtpTransitServiceBuilder Maven / Gradle / Ivy

There is a newer version: 2.6.0
Show newest version
package org.opentripplanner.model.impl;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import org.opentripplanner.ext.flex.trip.FlexTrip;
import org.opentripplanner.model.Agency;
import org.opentripplanner.model.BoardingArea;
import org.opentripplanner.model.Entrance;
import org.opentripplanner.model.FareAttribute;
import org.opentripplanner.model.FareRule;
import org.opentripplanner.model.FeedInfo;
import org.opentripplanner.model.FeedScopedId;
import org.opentripplanner.model.Frequency;
import org.opentripplanner.model.GroupOfStations;
import org.opentripplanner.model.FlexStopLocation;
import org.opentripplanner.model.FlexLocationGroup;
import org.opentripplanner.model.MultiModalStation;
import org.opentripplanner.model.Notice;
import org.opentripplanner.model.Operator;
import org.opentripplanner.model.OtpTransitService;
import org.opentripplanner.model.Pathway;
import org.opentripplanner.model.PathwayNode;
import org.opentripplanner.model.Route;
import org.opentripplanner.model.ShapePoint;
import org.opentripplanner.model.Station;
import org.opentripplanner.model.Stop;
import org.opentripplanner.model.StopPattern;
import org.opentripplanner.model.FareZone;
import org.opentripplanner.model.Transfer;
import org.opentripplanner.model.TransitEntity;
import org.opentripplanner.model.Trip;
import org.opentripplanner.model.TripPattern;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.opentripplanner.model.impl.GenerateMissingIds.generateNoneExistentIds;

/**
 * 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 fareAttributes = new ArrayList<>();

    private final List fareRules = new ArrayList<>();

    private final List feedInfos = new ArrayList<>();

    private final List frequencies = new ArrayList<>();

    private final EntityById groupsOfStationsById = new EntityById<>();

    private final EntityById multiModalStationsById = new EntityById<>();

    private final Multimap, Notice> 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 stationsById = new EntityById<>();

    private final EntityById stopsById = new EntityById<>();

    private final EntityById entrancesById = new EntityById<>();

    private final EntityById pathwayNodesById = new EntityById<>();

    private final EntityById boardingAreasById = new EntityById<>();

    private final EntityById locationsById = new EntityById<>();

    private final EntityById locationGroupsById = new EntityById<>();

    private final TripStopTimes stopTimesByTrip = new TripStopTimes();

    private final EntityById fareZonesById = new EntityById<>();

    private final List transfers = new ArrayList<>();

    private final EntityById tripsById = new EntityById<>();

    private final Multimap tripPatterns = ArrayListMultimap.create();

    private final EntityById flexTripsById = new EntityById<>();

    public OtpTransitServiceBuilder() {
    }

    /* Accessors */

    public EntityById getAgenciesById() {
        return agenciesById;
    }

    public List getCalendarDates() {
        return calendarDates;
    }

    public List getCalendars() {
        return calendars;
    }

    public List getFareAttributes() {
        return fareAttributes;
    }

    public List getFareRules() {
        return fareRules;
    }

    public List getFeedInfos() {
        return feedInfos;
    }

    public List getFrequencies() {
        return frequencies;
    }

    public EntityById getGroupsOfStationsById() {
        return groupsOfStationsById;
    }

    public EntityById getMultiModalStationsById() {
        return multiModalStationsById;
    }

    /**
     * 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, Notice> 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 stationsById;
    }

    public EntityById getStops() {
        return stopsById;
    }

    public EntityById getEntrances() {
        return entrancesById;
    }

    public EntityById getPathwayNodes() {
        return pathwayNodesById;
    }

    public EntityById getBoardingAreas() {
        return boardingAreasById;
    }

    public EntityById getLocations() {
        return locationsById;
    }

    public EntityById getLocationGroups() {
        return locationGroupsById;
    }

    public TripStopTimes getStopTimesSortedByTrip() {
        return stopTimesByTrip;
    }

    public EntityById getFareZonesById() { return fareZonesById; }

    public List getTransfers() {
        return transfers;
    }

    public EntityById getTripsById() {
        return tripsById;
    }

    public Multimap getTripPatterns() {
        return tripPatterns;
    }

    public EntityById getFlexTripsById() {
        return flexTripsById;
    }


    /**
     * 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;
    }

    public CalendarServiceData buildCalendarServiceData() {
        return CalendarServiceDataFactoryImpl.createCalendarServiceData(
                getAgenciesById().values(),
                getCalendarDates(),
                getCalendars()
        );
    }

    public OtpTransitService build() {
        generateNoneExistentIds(feedInfos);
        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.");
    }

    /**
     * Check all relations and remove entities witch 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();
    }

    /** Remove all trips witch reference none existing service ids */
    private void removeTripsWithNoneExistingServiceIds() {
        Set serviceIds = findAllServiceIds();
        int orgSize = tripsById.size();
        tripsById.removeIf(t -> !serviceIds.contains(t.getServiceId()));
        logRemove("Trip", orgSize, tripsById.size(), "Trip service id does not exist.");
    }

    /** Remove all stopTimes witch 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.getTrips().isEmpty()) {
                removePatterns.add(e);
            }
        }
        for (Map.Entry it : removePatterns) {
            tripPatterns.remove(it.getKey(), it.getValue());
        }
        logRemove("TripPattern", orgSize, tripPatterns.size(), "No trips for pattern exist.");
    }

    /** Remove all transfers witch reference none existing trips */
    private void removeTransfersForNoneExistingTrips() {
        int orgSize = transfers.size();
        transfers.removeIf(it -> noTripExist(it.getFromTrip()) || noTripExist(it.getToTrip()));
        logRemove("Trip", orgSize, transfers.size(), "Transfer to/from trip does not exist.");
    }

    /** Return true if the trip is a valid reference; {@code null} or exist. */
    private boolean noTripExist(Trip t) {
        return t != null && !tripsById.containsKey(t.getId());
    }

    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);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy