
org.opentripplanner.model.StopPattern Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of otp Show documentation
Show all versions of otp Show documentation
The OpenTripPlanner multimodal journey planning system
package org.opentripplanner.model;
import com.google.common.hash.HashCode;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hasher;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
/**
* This class represents what is called a JourneyPattern in Transmodel: the sequence of stops at
* which a trip (GTFS) or vehicle journey (Transmodel) calls, irrespective of the day on which
* service runs.
*
* An important detail: Routes in GTFS are not a structurally important element, they just serve as
* user-facing information. It is possible for the same journey pattern to appear in more than one
* route.
*
* OTP already has several classes that represent this same thing: A TripPattern in the context of
* routing. It represents all trips with the same stop pattern A ScheduledStopPattern in the GTFS
* loading process. A RouteVariant in the TransitIndex, which has a unique human-readable name and
* belongs to a particular route.
*
* We would like to combine all these different classes into one.
*
* Any two trips with the same stops in the same order, and that operate on the same days, can be
* combined using a TripPattern to simplify the graph. This saves memory and reduces search
* complexity since we only consider the trip that departs soonest for each pattern. Field
* calendarId has been removed. See issue #1320.
*
* A StopPattern is very closely related to a TripPattern -- it essentially serves as the unique
* key for a TripPattern. Should the route be included in the StopPattern?
*/
public class StopPattern implements Serializable {
private static final long serialVersionUID = 20140101L;
/* Constants for the GTFS pick up / drop off type fields. */
// It would be nice to have an enum for these, but the equivalence with integers is important.
public static final int PICKDROP_SCHEDULED = 0;
public static final int PICKDROP_NONE = 1;
public static final int PICKDROP_CALL_AGENCY = 2;
public static final int PICKDROP_COORDINATE_WITH_DRIVER = 3;
public final int size; // property could be derived from arrays
public final Stop[] stops;
public final int[] pickups;
public final int[] dropoffs;
private StopPattern (int size) {
this.size = size;
stops = new Stop[size];
pickups = new int[size];
dropoffs = new int[size];
}
/** Assumes that stopTimes are already sorted by time. */
public StopPattern (Collection stopTimes) {
this (stopTimes.size());
if (size == 0) return;
Iterator stopTimeIterator = stopTimes.iterator();
for (int i = 0; i < size; ++i) {
StopTime stopTime = stopTimeIterator.next();
stops[i] = (Stop) stopTime.getStop();
// should these just be booleans? anything but 1 means pick/drop is allowed.
// pick/drop messages could be stored in individual trips
pickups[i] = stopTime.getPickupType();
dropoffs[i] = stopTime.getDropOffType();
}
/*
* TriMet GTFS has many trips that differ only in the pick/drop status of their initial and
* final stops. This may have something to do with interlining. They are turning pickups off
* on the final stop of a trip to indicate that there is no interlining, because they supply
* block IDs for all trips, even those followed by dead runs. See issue 681. Enabling
* dropoffs at the initial stop and pickups at the final merges similar patterns while
* having no effect on routing.
*/
dropoffs[0] = 0;
pickups[size - 1] = 0;
}
/**
* @param stopId in agency_id format
*/
public boolean containsStop (String stopId) {
if (stopId == null) return false;
for (Stop stop : stops) if (stopId.equals(stop.getId().toString())) return true;
return false;
}
public boolean equals(Object other) {
if (other instanceof StopPattern) {
StopPattern that = (StopPattern) other;
return Arrays.equals(this.stops, that.stops) &&
Arrays.equals(this.pickups, that.pickups) &&
Arrays.equals(this.dropoffs, that.dropoffs);
} else {
return false;
}
}
public int hashCode() {
int hash = size;
hash += Arrays.hashCode(this.stops);
hash *= 31;
hash += Arrays.hashCode(this.pickups);
hash *= 31;
hash += Arrays.hashCode(this.dropoffs);
return hash;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("StopPattern: ");
for (int i = 0, j = stops.length; i < j; ++i) {
sb.append(String.format("%s_%d%d ", stops[i].getCode(), pickups[i], dropoffs[i]));
}
return sb.toString();
}
/**
* In most cases we want to use identity equality for StopPatterns. There is a single
* StopPattern instance for each semantic StopPattern, and we don't want to calculate
* complicated hashes or equality values during normal execution. However, in some cases we
* want a way to consistently identify trips across versions of a GTFS feed, when the feed
* publisher cannot ensure stable trip IDs. Therefore we define some additional hash functions.
*/
public HashCode semanticHash(HashFunction hashFunction) {
Hasher hasher = hashFunction.newHasher();
for (int s = 0; s < size; s++) {
Stop stop = stops[s];
// Truncate the lat and lon to 6 decimal places in case they move slightly between
// feed versions
hasher.putLong((long) (stop.getLat() * 1000000));
hasher.putLong((long) (stop.getLon() * 1000000));
}
// Use hops rather than stops because drop-off at stop 0 and pick-up at last stop are
// not important and have changed between OTP versions.
for (int hop = 0; hop < size - 1; hop++) {
hasher.putInt(pickups[hop]);
hasher.putInt(dropoffs[hop + 1]);
}
return hasher.hash();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy