com.publictransitanalytics.scoregenerator.schedule.patching.RouteReroute.java Source code

Java tutorial

Introduction

Here is the source code for com.publictransitanalytics.scoregenerator.schedule.patching.RouteReroute.java

Source

/*
 * Copyright 2017 Public Transit Analytics.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.publictransitanalytics.scoregenerator.schedule.patching;

import com.google.common.collect.ImmutableList;
import com.publictransitanalytics.scoregenerator.location.TransitStop;
import com.publictransitanalytics.scoregenerator.schedule.VehicleEvent;
import com.publictransitanalytics.scoregenerator.schedule.Trip;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 *
 * @author Public Transit Analytics
 */
@Slf4j
@RequiredArgsConstructor
public class RouteReroute implements Patch {

    private final String routeNumber;
    private final TransitStop referenceStop;
    private final ReferenceDirection type;
    private final List<RouteSequenceItem> sequence;
    private final TransitStop returnStop;
    private final Duration returnDelta;

    @Override
    public Optional<Trip> patch(final Trip original) {
        final List<VehicleEvent> schedule = original.getSchedule();
        final Trip newTrip;
        if (routeNumber.equals(original.getRouteNumber())) {
            if (schedule.isEmpty()) {
                newTrip = original;
                log.info("Trip {} was not extended because it is empty.", newTrip.getTripId());
            } else if (schedule.size() == 1) {
                final TransitStop stop = schedule.get(0).getLocation();
                if (referenceStop.equals(stop)) {
                    if (type.equals(ReferenceDirection.AFTER_LAST)) {
                        final List<VehicleEvent> newSchedule = Appending.appendToSchedule(schedule, sequence);
                        newTrip = Appending.makeReplacementTrip(original, newSchedule);
                    } else if (type.equals(ReferenceDirection.BEFORE_FIRST)) {
                        final List<VehicleEvent> newSchedule = Appending.prependToSchedule(schedule, sequence);
                        newTrip = Appending.makeReplacementTrip(original, newSchedule);
                    } else {
                        newTrip = original;
                    }
                } else {
                    newTrip = original;
                    log.info("Reference stop {} was not found in singleton trip {}", stop.getIdentifier(),
                            newTrip.getTripId());
                }
            } else {
                final ImmutableList.Builder<VehicleEvent> scheduleBuilder = ImmutableList.builder();
                if (type.equals(ReferenceDirection.AFTER_LAST)) {
                    final int divergingIndex = getLocationIndex(schedule, referenceStop);

                    if (divergingIndex == -1) {
                        newTrip = original;
                    } else {

                        final LocalDateTime divergingTime = makeForwardPreDivergingPortion(schedule, divergingIndex,
                                scheduleBuilder);

                        final LocalDateTime lastRerouteTime = makeForwardReroute(divergingTime, scheduleBuilder);

                        if (returnStop != null) {
                            final List<VehicleEvent> remainder = schedule.subList(divergingIndex, schedule.size());
                            makeForwardPostDivergingPortion(remainder, lastRerouteTime, scheduleBuilder);
                        }
                        newTrip = Appending.makeReplacementTrip(original, scheduleBuilder.build());
                    }
                } else if (type.equals(ReferenceDirection.BEFORE_FIRST)) {
                    final int divergingIndex = getLastLocationIndex(schedule, referenceStop);

                    if (divergingIndex == -1) {
                        newTrip = original;
                    } else {

                        final List<VehicleEvent> end = schedule.subList(divergingIndex, schedule.size());
                        final LocalDateTime divergingTime = end.get(0).getScheduledTime();

                        final List<VehicleEvent> reroute = makeBackwardReroute(divergingTime);
                        final LocalDateTime lastRerouteTime = reroute.get(0).getScheduledTime();

                        if (returnStop != null) {
                            final List<VehicleEvent> remainder = schedule.subList(0, divergingIndex + 1);
                            final List<VehicleEvent> beginning = makeBackwardPostDivergingPortion(remainder,
                                    lastRerouteTime);
                            scheduleBuilder.addAll(beginning);
                        }

                        scheduleBuilder.addAll(reroute);
                        scheduleBuilder.addAll(end);

                        newTrip = Appending.makeReplacementTrip(original, scheduleBuilder.build());
                    }
                } else {
                    newTrip = original;
                }
            }
        } else {
            newTrip = original;
        }

        return Optional.of(newTrip);
    }

    private void makeForwardPostDivergingPortion(final List<VehicleEvent> remainder,
            final LocalDateTime lastRerouteTime, final ImmutableList.Builder<VehicleEvent> scheduleBuilder) {

        final int returningIndex = getLocationIndex(remainder, returnStop);
        if (returningIndex != -1) {

            final VehicleEvent returningScheduledLocation = remainder.get(returningIndex);

            final TransitStop returningLocation = returningScheduledLocation.getLocation();
            final LocalDateTime originalReturnTime = returningScheduledLocation.getScheduledTime();
            LocalDateTime referenceTime = lastRerouteTime.plus(returnDelta);

            final VehicleEvent newReturnScheduledLocation = new VehicleEvent(returningLocation, referenceTime);

            scheduleBuilder.add(newReturnScheduledLocation);

            LocalDateTime priorTime = originalReturnTime;

            for (int i = returningIndex + 1; i < remainder.size(); i++) {
                final VehicleEvent scheduledLocation = remainder.get(i);

                final TransitStop location = scheduledLocation.getLocation();
                final LocalDateTime originalTime = scheduledLocation.getScheduledTime();

                final Duration delta = Duration.between(priorTime, originalTime);
                final LocalDateTime newTime = referenceTime.plus(delta);
                final VehicleEvent newScheduledLocation = new VehicleEvent(location, newTime);
                referenceTime = newTime;
                scheduleBuilder.add(newScheduledLocation);
            }
        }
    }

    private List<VehicleEvent> makeBackwardPostDivergingPortion(final List<VehicleEvent> remainder,
            final LocalDateTime lastRerouteTime) {

        final ImmutableList.Builder<VehicleEvent> portionBuilder = ImmutableList.builder();

        final int returningIndex = getLastLocationIndex(remainder, returnStop);
        if (returningIndex != -1) {

            final VehicleEvent returningScheduledLocation = remainder.get(returningIndex);

            final TransitStop returningLocation = returningScheduledLocation.getLocation();
            final LocalDateTime originalReturnTime = returningScheduledLocation.getScheduledTime();
            LocalDateTime referenceTime = lastRerouteTime.minus(returnDelta);

            final VehicleEvent newReturnScheduledLocation = new VehicleEvent(returningLocation, referenceTime);

            portionBuilder.add(newReturnScheduledLocation);

            LocalDateTime priorTime = originalReturnTime;

            for (int i = returningIndex - 1; i >= 0; i--) {
                final VehicleEvent scheduledLocation = remainder.get(i);

                final TransitStop location = scheduledLocation.getLocation();
                final LocalDateTime originalTime = scheduledLocation.getScheduledTime();

                final Duration delta = Duration.between(originalTime, priorTime);
                final LocalDateTime newTime = referenceTime.minus(delta);
                final VehicleEvent newScheduledLocation = new VehicleEvent(location, newTime);
                referenceTime = newTime;
                portionBuilder.add(newScheduledLocation);
            }
        }
        return portionBuilder.build().reverse();
    }

    private static LocalDateTime makeForwardPreDivergingPortion(final List<VehicleEvent> schedule,
            final int divergingIndex, final ImmutableList.Builder<VehicleEvent> scheduleBuilder) {
        final List<VehicleEvent> beginning = schedule.subList(0, divergingIndex + 1);

        scheduleBuilder.addAll(beginning);

        final VehicleEvent lastLocation = beginning.get(beginning.size() - 1);
        final LocalDateTime lastTime = lastLocation.getScheduledTime();
        return lastTime;
    }

    private LocalDateTime makeForwardReroute(final LocalDateTime priorTime,
            final ImmutableList.Builder<VehicleEvent> scheduleBuilder) {
        LocalDateTime referenceTime = priorTime;
        for (final RouteSequenceItem item : sequence) {
            final LocalDateTime time = referenceTime.plus(item.getDelta());
            final VehicleEvent scheduledLocation = new VehicleEvent(item.getStop(), time);
            scheduleBuilder.add(scheduledLocation);
            referenceTime = time;
        }
        return referenceTime;
    }

    private List<VehicleEvent> makeBackwardReroute(final LocalDateTime priorTime) {
        final ImmutableList.Builder<VehicleEvent> rerouteBuilder = ImmutableList.builder();
        LocalDateTime referenceTime = priorTime;
        for (final RouteSequenceItem item : sequence) {
            final LocalDateTime time = referenceTime.minus(item.getDelta());
            final VehicleEvent scheduledLocation = new VehicleEvent(item.getStop(), time);
            rerouteBuilder.add(scheduledLocation);
            referenceTime = time;
        }
        return rerouteBuilder.build().reverse();
    }

    private static int getLocationIndex(final List<VehicleEvent> schedule, final TransitStop location) {
        int index = -1;
        for (int i = 0; i < schedule.size(); i++) {
            final VehicleEvent scheduledLocation = schedule.get(i);
            if (scheduledLocation.getLocation().equals(location)) {
                index = i;
                break;
            }
        }
        return index;
    }

    private static int getLastLocationIndex(final List<VehicleEvent> schedule, final TransitStop location) {
        int index = -1;
        for (int i = schedule.size() - 1; i >= 0; i--) {
            final VehicleEvent scheduledLocation = schedule.get(i);
            if (scheduledLocation.getLocation().equals(location)) {
                index = i;
                break;
            }
        }
        return index;
    }

}