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

org.opentripplanner.model.plan.Itinerary Maven / Gradle / Ivy

package org.opentripplanner.model.plan;


import org.opentripplanner.model.SystemNotice;
import org.opentripplanner.model.base.ToStringBuilder;
import org.opentripplanner.routing.core.Fare;
import org.opentripplanner.transit.raptor.util.PathStringBuilder;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * An Itinerary is one complete way of getting from the start location to the end location.
 */
public class Itinerary {

    /** Total duration of the itinerary in seconds */
    public final int durationSeconds;

    /**
     * How much time is spent on transit, in seconds.
     */
    public final int transitTimeSeconds;

    /**
     * The number of transfers this trip has.
     */
    public final int nTransfers;

    /**
     * How much time is spent waiting for transit to arrive, in seconds.
     */
    public final int waitingTimeSeconds;

    /**
     * How much time is spent walking/biking/driving, in seconds.
     */
    public int nonTransitTimeSeconds;

    /**
     * How far the user has to walk, bike and/or drive, in meters.
     */
    public final double nonTransitDistanceMeters;

    /**
     * Indicates that the walk/bike/drive limit distance has been exceeded for this itinerary.
     */
    public boolean nonTransitLimitExceeded = false;

    /**
     * How much elevation is lost, in total, over the course of the trip, in meters. As an example,
     * a trip that went from the top of Mount Everest straight down to sea level, then back up K2,
     * then back down again would have an elevationLost of Everest + K2.
     */

    public Double elevationLost = 0.0;
    /**
     * How much elevation is gained, in total, over the course of the trip, in meters. See
     * elevationLost.
     */

    public Double elevationGained = 0.0;

    /**
     * If a generalized cost is used in the routing algorithm, this should be the total
     * cost computed by the algorithm. This is relevant for anyone who want to debug an search
     * and tuning the system. The unit should be equivalent to the cost of "one second of transit".
     */
    public int generalizedCost = 0;

    /**
     * This itinerary has a greater slope than the user requested (but there are no possible
     * itineraries with a good slope).
     */
    public boolean tooSloped = false;

     /** TRUE if mode is WALK from start ot end (all legs are walking). */
    public final boolean walkOnly;

    /** TRUE if mode is a non transit move from start ot end (all legs are non-transit). */
    public final boolean streetOnly;


    /**
     * System notices is used to tag itineraries with system information. For example if you run the
     * itinerary-filter in debug mode, the filters would tag itineraries instead of deleting them
     * from the result. More than one filter might apply, so there can be more than one notice for
     * an itinerary. This is very handy, when tuning the system or debugging - looking for missing
     * expected trips.
     */
    public final List systemNotices = new ArrayList<>();


    /**
     * The cost of this trip
     */
    public Fare fare = new Fare();

    /**
     * A list of Legs. Each Leg is either a walking (cycling, car) portion of the trip, or a transit
     * trip on a particular vehicle. So a trip where the use walks to the Q train, transfers to the
     * 6, then walks to their destination, has four legs.
     */
    public final List legs;


    public Itinerary(List legs) {
        this.legs = List.copyOf(legs);

        // Set aggregated data
        ItinerariesCalculateLegTotals totals = new ItinerariesCalculateLegTotals(legs);
        this.durationSeconds = totals.totalDurationSeconds;
        this.nTransfers = totals.transfers();
        this.transitTimeSeconds = totals.transitTimeSeconds;
        this.nonTransitTimeSeconds = totals.nonTransitTimeSeconds;
        this.nonTransitDistanceMeters = totals.nonTransitDistanceMeters;
        this.waitingTimeSeconds = totals.waitingTimeSeconds;
        this.walkOnly = totals.walkOnly;
        this.streetOnly = totals.streetOnly;
    }

    /**
     * Time that the trip departs.
     */
    public Calendar startTime() {
        return firstLeg().startTime;
    }

    /**
     * Time that the trip arrives.
     */
    public Calendar endTime() {
        return lastLeg().endTime;
    }


    /**
     * This is the amount of time used to travel. {@code waitingTime} is NOT
     * included.
     */
    public int effectiveDurationSeconds() {
        return transitTimeSeconds + nonTransitTimeSeconds;
    }

    /**
     * Total distance in meters.
     */
    public double distanceMeters() {
        return legs.stream().mapToDouble(it -> it.distanceMeters).sum();
    }

    /**
     * Return {@code true} if all legs are WALKING.
     */
    public boolean isWalkingAllTheWay() {
        return walkOnly;
    }

    /**
     * Return {@code true} if all legs are WALKING.
     */
    public boolean isOnStreetAllTheWay() {
        return streetOnly;
    }

    /** TRUE if alt least one leg is a transit leg. */
    public boolean hasTransit() {
        return transitTimeSeconds > 0;
    }

    public Leg firstLeg() {
        return legs.get(0);
    }

    public Leg lastLeg() {
        return legs.get(legs.size()-1);
    }

    /** Get the first transit leg if one exist */
    public Optional firstTransitLeg() {
        return legs.stream().filter(Leg::isTransitLeg).findFirst();
    }

    /**
     * A itinerary can be tagged with a system notice. System notices should only be added to a
     * response if explicit asked for in the request.
     * 

* For example when tuning or manually testing the itinerary-filter-chain it you can enable * the {@link org.opentripplanner.routing.api.request.RoutingRequest#debugItineraryFilter} and instead * of removing itineraries from the result the itineraries would be tagged by the filters * instead. This enable investigating, why an expected itinerary is missing from the result * set. */ public void addSystemNotice(SystemNotice notice) { systemNotices.add(notice); } public void timeShiftToStartAt(Calendar afterTime) { Calendar startTimeFirstLeg = firstLeg().startTime; int adjustmentMilliSeconds = (int)(afterTime.getTimeInMillis() - startTimeFirstLeg.getTimeInMillis()); timeShift(adjustmentMilliSeconds); } private void timeShift(int adjustmentMilliSeconds) { for (Leg leg : this.legs) { leg.startTime.add(Calendar.MILLISECOND, adjustmentMilliSeconds); leg.endTime.add(Calendar.MILLISECOND, adjustmentMilliSeconds); } } /** * Return {@code true} it the other object is the same object using the {@link * Object#equals(Object)}. An itinerary is a temporary object and the equals method should not * be used for comparision of 2 instances, only to check that to objects are the same instance. */ @Override public final boolean equals(Object o) { return super.equals(o); } /** @see #equals(Object) */ @Override public final int hashCode() { return super.hashCode(); } @Override public String toString() { return ToStringBuilder.of(Itinerary.class) .addStr("from", firstLeg().from.toStringShort()) .addStr("to", lastLeg().to.toStringShort()) .addCalTime("start", firstLeg().startTime) .addCalTime("end", lastLeg().endTime) .addNum("nTransfers", nTransfers, -1) .addDuration("duration", durationSeconds) .addNum("generalizedCost", generalizedCost) .addDuration("nonTransitTime", nonTransitTimeSeconds) .addDuration("transitTime", transitTimeSeconds) .addDuration("waitingTime", waitingTimeSeconds) .addNum("nonTransitDistance", nonTransitDistanceMeters, "m") .addBool("nonTransitLimitExceeded", nonTransitLimitExceeded) .addBool("tooSloped", tooSloped) .addNum("elevationLost", elevationLost, 0.0) .addNum("elevationGained", elevationGained, 0.0) .addCol("legs", legs) .addObj("fare", fare) .toString(); } /** * Used to convert a list of itineraries to a SHORT human readable string. * @see #toStr() *

* It is great for comparing lists of itineraries in a test: * {@code assertEquals(toStr(List.of(it1)), toStr(result))}. */ public static String toStr(List list) { return list.stream().map(Itinerary::toStr).collect(Collectors.joining(", ")); } /** * Used to convert an itinerary to a SHORT human readable string - including just a few of * the most important fields. It is much shorter and easier to read then the * {@link Itinerary#toString()}. *

* It is great for comparing to itineraries in a test: * {@code assertEquals(toStr(it1), toStr(it2))}. *

* Example: {@code A ~ Walk 2m ~ B ~ BUS 55 12:04 12:14 ~ C [cost: 1066]} *

* Reads: Start at A, walk 2 minutes to stop B, take bus 55, board at 12:04 and alight at * 12:14 ... */ public String toStr() { PathStringBuilder buf = new PathStringBuilder(); buf.stop(firstLeg().from.name); for (Leg leg : legs) { buf.sep(); if(leg.isWalkingLeg()) { buf.walk((int)leg.getDuration()); } else if(leg.isTransitLeg()) { buf.transit(leg.mode, leg.getTrip().getId().getId(), leg.startTime, leg.endTime); } else { buf.other(leg.mode, leg.startTime, leg.endTime); } buf.sep(); buf.stop(leg.to.name); } return buf.toString() + " [cost: " + generalizedCost + "]"; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy