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

com.conveyal.gtfs.model.Service Maven / Gradle / Ivy

Go to download

A library to load and index GTFS feeds of arbitrary size using disk-backed storage

There is a newer version: 6.2.0
Show newest version
package com.conveyal.gtfs.model;

import com.google.common.collect.Maps;

import java.time.DayOfWeek;
import java.time.LocalDate;

import java.io.Serializable;
import java.util.EnumSet;
import java.util.Map;

import static java.time.DayOfWeek.*;

/**
 * This table does not exist in GTFS. It is a join of calendars and calendar_dates on service_id.
 * There should only be one Calendar per service_id. There should only be one calendar_date per tuple of
 * (service_id, date), which means there can be many calendar_dates per service_id.
 * TODO verify that the same service ID is not allowed to appear more than once in calendar.txt
 */
public class Service implements Serializable {

    private static final long serialVersionUID = 7966238549509747091L;
    public String   service_id;
    public Calendar calendar;
    public Map calendar_dates = Maps.newHashMap();

    public Service(String service_id) {
        this.service_id = service_id;
    }

    /**
     * @param service_id the service_id to assign to the newly created copy.
     * @param daysToRemove the days of the week on which to deactivate service in the copy.
     * @return a copy of this Service with any service on the specified days of the week deactivated.
     */
    public Service removeDays(String service_id, EnumSet daysToRemove) {
        Service service = new Service(service_id);
        // First, duplicate any Calendar in this Service, minus the specified days of the week.
        if (this.calendar != null) {
            Calendar calendar = new Calendar();
            //  TODO calendar.getDaysOfWeek/setDaysOfWeek which allow simplifying this section and activeOn below.
            calendar.monday    = daysToRemove.contains(MONDAY)    ? 0 : this.calendar.monday;
            calendar.tuesday   = daysToRemove.contains(TUESDAY)   ? 0 : this.calendar.tuesday;
            calendar.wednesday = daysToRemove.contains(WEDNESDAY) ? 0 : this.calendar.wednesday;
            calendar.thursday  = daysToRemove.contains(THURSDAY)  ? 0 : this.calendar.thursday;
            calendar.friday    = daysToRemove.contains(FRIDAY)    ? 0 : this.calendar.friday;
            calendar.saturday  = daysToRemove.contains(SATURDAY)  ? 0 : this.calendar.saturday;
            calendar.sunday    = daysToRemove.contains(SUNDAY)    ? 0 : this.calendar.sunday;
            // The new calendar should cover exactly the same time range as the existing one.
            calendar.start_date = this.calendar.start_date;
            calendar.end_date   = this.calendar.end_date;
            // Create the bidirectional reference between Calendar and Service.
            service.calendar = calendar;
        }
        // Copy over all exceptions whose dates fall on days of the week that are retained.
        this.calendar_dates.forEach((date, exception) -> {
            DayOfWeek dow = date.getDayOfWeek();
            if (!daysToRemove.contains(dow)) {
                CalendarDate newException = exception.clone();
                service.calendar_dates.put(date, newException);
            }
        });
        return service;
    }

    /**
     * @return whether this Service is ever active at all, either from calendar or calendar_dates.
     */
    public boolean hasAnyService() {

        // Look for any service defined in calendar (on days of the week).
        boolean hasAnyService = calendar != null && (
                calendar.monday == 1 ||
                calendar.tuesday == 1 ||
                calendar.wednesday == 1 ||
                calendar.thursday == 1 ||
                calendar.friday == 1 ||
                calendar.saturday == 1 ||
                calendar.sunday == 1 );

        // Also look for any exceptions of type 1 (added service).
        hasAnyService |= calendar_dates.values().stream().anyMatch(cd -> cd.exception_type == 1);

        return hasAnyService;
    }

    /**
     * Is this service active on the specified date?
     */
    public boolean activeOn (LocalDate date) {
        // first check for exceptions
        CalendarDate exception = calendar_dates.get(date);

        if (exception != null)
            return exception.exception_type == 1;

        else if (calendar == null)
            return false;

        else {
            boolean outsideValidityRange = date.isAfter(calendar.end_date) || date.isBefore(calendar.start_date);
            if (outsideValidityRange) return false;
            switch (date.getDayOfWeek()) {
                case MONDAY:
                    return calendar.monday == 1;
                case TUESDAY:
                    return calendar.tuesday == 1;
                case WEDNESDAY:
                    return calendar.wednesday == 1;
                case THURSDAY:
                    return calendar.thursday == 1;
                case FRIDAY:
                    return calendar.friday == 1;
                case SATURDAY:
                    return calendar.saturday == 1;
                case SUNDAY:
                    return calendar.sunday == 1;
                default:
                    throw new IllegalArgumentException("unknown day of week constant!");
            }
        }
    }

    /**
     * Checks for overlapping days of week between two service calendars
     * @param s1
     * @param s2
     * @return true if both calendars simultaneously operate on at least one day of the week
     */
    public static boolean checkOverlap (Service s1, Service s2) {
        if (s1.calendar == null || s2.calendar == null) {
            return false;
        }
        // overlap exists if at least one day of week is shared by two calendars
        boolean overlappingDays = s1.calendar.monday == 1 && s2.calendar.monday == 1 ||
                s1.calendar.tuesday == 1 && s2.calendar.tuesday == 1 ||
                s1.calendar.wednesday == 1 && s2.calendar.wednesday == 1 ||
                s1.calendar.thursday == 1 && s2.calendar.thursday == 1 ||
                s1.calendar.friday == 1 && s2.calendar.friday == 1 ||
                s1.calendar.saturday == 1 && s2.calendar.saturday == 1 ||
                s1.calendar.sunday == 1 && s2.calendar.sunday == 1;
        return overlappingDays;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy