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

de.schildbach.pte.dto.Trip Maven / Gradle / Ivy

There is a newer version: 2
Show newest version
/*
 * Copyright the original author or authors.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */

package de.schildbach.pte.dto;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import javax.annotation.Nullable;

import com.google.common.base.MoreObjects;
import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.base.Objects;

/**
 * @author Andreas Schildbach
 */
public final class Trip implements Serializable {
    private static final long serialVersionUID = 2508466068307110312L;

    private String id;
    public final Location from;
    public final Location to;
    public final List legs;
    public final List fares;
    public final int[] capacity;
    public final Integer numChanges;

    public Trip(final String id, final Location from, final Location to, final List legs, final List fares,
            final int[] capacity, final Integer numChanges) {
        this.id = id;
        this.from = checkNotNull(from);
        this.to = checkNotNull(to);
        this.legs = checkNotNull(legs);
        this.fares = fares;
        this.capacity = capacity;
        this.numChanges = numChanges;

        checkArgument(!legs.isEmpty());
    }

    public Date getFirstDepartureTime() {
        return legs.get(0).getDepartureTime();
    }

    public @Nullable Public getFirstPublicLeg() {
        for (final Leg leg : legs)
            if (leg instanceof Public)
                return (Public) leg;

        return null;
    }

    public @Nullable Date getFirstPublicLegDepartureTime() {
        final Public firstPublicLeg = getFirstPublicLeg();
        if (firstPublicLeg != null)
            return firstPublicLeg.getDepartureTime();
        else
            return null;
    }

    public Date getLastArrivalTime() {
        return legs.get(legs.size() - 1).getArrivalTime();
    }

    public @Nullable Public getLastPublicLeg() {
        for (int i = legs.size() - 1; i >= 0; i--) {
            final Leg leg = legs.get(i);
            if (leg instanceof Public)
                return (Public) leg;
        }

        return null;
    }

    public @Nullable Date getLastPublicLegArrivalTime() {
        final Public lastPublicLeg = getLastPublicLeg();
        if (lastPublicLeg != null)
            return lastPublicLeg.getArrivalTime();
        else
            return null;
    }

    /**
     * Duration of whole trip in milliseconds, including leading and trailing individual legs.
     * 
     * @return duration in ms
     */
    public long getDuration() {
        final Date first = getFirstDepartureTime();
        final Date last = getLastArrivalTime();
        return last.getTime() - first.getTime();
    }

    /**
     * Duration of the public leg part in milliseconds. This includes individual legs between public legs, but
     * excludes individual legs that lead or trail the trip.
     * 
     * @return duration in ms, or null if there are no public legs
     */
    public @Nullable Long getPublicDuration() {
        final Date first = getFirstPublicLegDepartureTime();
        final Date last = getLastPublicLegArrivalTime();
        if (first != null && last != null)
            return last.getTime() - first.getTime();
        else
            return null;
    }

    /** Minimum time occurring in this trip. */
    public Date getMinTime() {
        Date minTime = null;

        for (final Leg leg : legs)
            if (minTime == null || leg.getMinTime().before(minTime))
                minTime = leg.getMinTime();

        return minTime;
    }

    /** Maximum time occurring in this trip. */
    public Date getMaxTime() {
        Date maxTime = null;

        for (final Leg leg : legs)
            if (maxTime == null || leg.getMaxTime().after(maxTime))
                maxTime = leg.getMaxTime();

        return maxTime;
    }

    /**
     * 

* Number of changes on the trip. *

* *

* Returns {@link #numChanges} if it isn't null. Otherwise, it tries to compute the number by counting * public legs of the trip. The number of changes for a Trip consisting of only individual Legs is null. *

* * @return number of changes on the trip, or null if no public legs are involved */ @Nullable public Integer getNumChanges() { if (numChanges == null) { Integer numCount = null; for (final Leg leg : legs) { if (leg instanceof Public) { if (numCount == null) { numCount = 0; } else { numCount++; } } } return numCount; } else { return numChanges; } } /** * Returns true if it looks like the trip can be traveled. Returns false if legs overlap, important * departures or arrivals are cancelled and that sort of thing. */ public boolean isTravelable() { Date time = null; for (final Leg leg : legs) { if (leg instanceof Public) { final Public publicLeg = (Public) leg; if (publicLeg.departureStop.departureCancelled || publicLeg.arrivalStop.arrivalCancelled) return false; } final Date departureTime = leg.getDepartureTime(); if (time != null && departureTime.before(time)) return false; time = departureTime; final Date arrivalTime = leg.getArrivalTime(); if (time != null && arrivalTime.before(time)) return false; time = arrivalTime; } return true; } /** If an individual leg overlaps, try to adjust so that it doesn't. */ public void adjustUntravelableIndividualLegs() { final int numLegs = legs.size(); if (numLegs < 1) return; for (int i = 1; i < numLegs; i++) { final Trip.Leg leg = legs.get(i); if (leg instanceof Trip.Individual) { final Trip.Leg previous = legs.get(i - 1); if (leg.getDepartureTime().before(previous.getArrivalTime())) legs.set(i, ((Trip.Individual) leg).movedClone(previous.getArrivalTime())); } } } public Set products() { final Set products = EnumSet.noneOf(Product.class); for (final Leg leg : legs) if (leg instanceof Public) products.add(((Public) leg).line.product); return products; } public String getId() { if (id == null) id = buildSubstituteId(); return id; } private String buildSubstituteId() { final StringBuilder builder = new StringBuilder(); for (final Leg leg : legs) { builder.append(leg.departure.hasId() ? leg.departure.id : leg.departure.coord).append('-'); builder.append(leg.arrival.hasId() ? leg.arrival.id : leg.arrival.coord).append('-'); if (leg instanceof Individual) { builder.append("individual"); } else if (leg instanceof Public) { final Public publicLeg = (Public) leg; final Date plannedDepartureTime = publicLeg.departureStop.plannedDepartureTime; if (plannedDepartureTime != null) builder.append(plannedDepartureTime.getTime()).append('-'); final Date plannedArrivalTime = publicLeg.arrivalStop.plannedArrivalTime; if (plannedArrivalTime != null) builder.append(plannedArrivalTime.getTime()).append('-'); final Line line = publicLeg.line; builder.append(line.productCode()); builder.append(line.label); } builder.append('|'); } builder.setLength(builder.length() - 1); return builder.toString(); } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Trip)) return false; final Trip other = (Trip) o; return Objects.equal(this.getId(), other.getId()); } @Override public int hashCode() { return Objects.hashCode(getId()); } @Override public String toString() { final ToStringHelper helper = MoreObjects.toStringHelper(this).addValue(getId()); final Date firstPublicLegDepartureTime = getFirstPublicLegDepartureTime(); final Date lastPublicLegArrivalTime = getLastPublicLegArrivalTime(); helper.addValue( firstPublicLegDepartureTime != null ? String.format(Locale.US, "%ta % path; // custom serialization, to save space public Leg(final Location departure, final Location arrival, final List path) { this.departure = checkNotNull(departure); this.arrival = checkNotNull(arrival); this.path = path; } /** Coarse departure time. */ public abstract Date getDepartureTime(); /** Coarse arrival time. */ public abstract Date getArrivalTime(); /** Minimum time occurring in this leg. */ public abstract Date getMinTime(); /** Maximum time occurring in this leg. */ public abstract Date getMaxTime(); private void writeObject(final ObjectOutputStream os) throws IOException { os.defaultWriteObject(); if (path != null) { os.writeInt(path.size()); for (final Point p : path) { os.writeInt(p.getLatAs1E6()); os.writeInt(p.getLonAs1E6()); } } else { os.writeInt(-1); } } private void readObject(final ObjectInputStream is) throws ClassNotFoundException, IOException { is.defaultReadObject(); try { final int pathSize = is.readInt(); if (pathSize >= 0) { path = new ArrayList<>(pathSize); for (int i = 0; i < pathSize; i++) path.add(Point.from1E6(is.readInt(), is.readInt())); } else { path = null; } } catch (final EOFException x) { path = null; } } } public final static class Public extends Leg { private static final long serialVersionUID = 1312066446239817422L; public final Line line; public final @Nullable Location destination; public final Stop departureStop; public final Stop arrivalStop; public final @Nullable List intermediateStops; public final @Nullable String message; public Public(final Line line, final Location destination, final Stop departureStop, final Stop arrivalStop, final List intermediateStops, final List path, final String message) { super(departureStop.location, arrivalStop.location, path); this.line = checkNotNull(line); this.destination = destination; this.departureStop = checkNotNull(departureStop); this.arrivalStop = checkNotNull(arrivalStop); this.intermediateStops = intermediateStops; this.message = message; checkNotNull(departureStop.getDepartureTime()); checkNotNull(arrivalStop.getArrivalTime()); } @Override public Date getDepartureTime() { return departureStop.getDepartureTime(false); } public Date getDepartureTime(final boolean preferPlanTime) { return departureStop.getDepartureTime(preferPlanTime); } public boolean isDepartureTimePredicted() { return departureStop.isDepartureTimePredicted(false); } public Long getDepartureDelay() { return departureStop.getDepartureDelay(); } public Position getDeparturePosition() { return departureStop.getDeparturePosition(); } public boolean isDeparturePositionPredicted() { return departureStop.isDeparturePositionPredicted(); } @Override public Date getArrivalTime() { return arrivalStop.getArrivalTime(false); } public Date getArrivalTime(final boolean preferPlanTime) { return arrivalStop.getArrivalTime(preferPlanTime); } public boolean isArrivalTimePredicted() { return arrivalStop.isArrivalTimePredicted(false); } public Long getArrivalDelay() { return arrivalStop.getArrivalDelay(); } public Position getArrivalPosition() { return arrivalStop.getArrivalPosition(); } public boolean isArrivalPositionPredicted() { return arrivalStop.isArrivalPositionPredicted(); } @Override public Date getMinTime() { return departureStop.getMinTime(); } @Override public Date getMaxTime() { return arrivalStop.getMaxTime(); } @Override public String toString() { return MoreObjects.toStringHelper(this).add("line", line).add("destination", destination) .add("departureStop", departureStop).add("arrivalStop", arrivalStop).omitNullValues().toString(); } } public final static class Individual extends Leg { public enum Type { WALK, BIKE, CAR, TRANSFER } private static final long serialVersionUID = -6651381862837233925L; public final Type type; public final Date departureTime; public final Date arrivalTime; public final int min; public final int distance; public Individual(final Type type, final Location departure, final Date departureTime, final Location arrival, final Date arrivalTime, final List path, final int distance) { super(departure, arrival, path); this.type = checkNotNull(type); this.departureTime = checkNotNull(departureTime); this.arrivalTime = checkNotNull(arrivalTime); this.min = (int) ((arrivalTime.getTime() - departureTime.getTime()) / 1000 / 60); this.distance = distance; } public Individual movedClone(final Date departureTime) { final Date arrivalTime = new Date( departureTime.getTime() + this.arrivalTime.getTime() - this.departureTime.getTime()); return new Trip.Individual(this.type, this.departure, departureTime, this.arrival, arrivalTime, this.path, this.distance); } @Override public Date getDepartureTime() { return departureTime; } @Override public Date getArrivalTime() { return arrivalTime; } @Override public Date getMinTime() { return departureTime; } @Override public Date getMaxTime() { return arrivalTime; } @Override public String toString() { return MoreObjects.toStringHelper(this).addValue(type).add("departure", departure).add("arrival", arrival) .add("min", min).add("distance", distance).omitNullValues().toString(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy