org.opentripplanner.routing.transit_index.RouteVariant Maven / Gradle / Ivy
package org.opentripplanner.routing.transit_index;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.opentripplanner.model.Route;
import org.opentripplanner.model.Stop;
import org.opentripplanner.model.Trip;
import org.opentripplanner.api.adapters.LineStringAdapter;
import org.opentripplanner.api.adapters.StopAgencyAndIdAdapter;
import org.opentripplanner.api.adapters.TripsModelInfo;
import org.opentripplanner.common.geometry.GeometryUtils;
import org.opentripplanner.gtfs.GtfsLibrary;
import org.opentripplanner.model.json_serialization.EncodedPolylineJSONSerializer;
import org.opentripplanner.routing.core.TraverseMode;
import org.opentripplanner.routing.edgetype.PatternInterlineDwell;
import org.opentripplanner.routing.graph.Edge;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.LineString;
/**
*
* THIS CLASS IS BEING MERGED INTO STOPPATTERN / TRIPPATTERN. It is being kept around for reference
* and is expected to be removed eventually.
*
* A "route variant" represents a particular stop pattern on a particular route. For example, the N
* train has at least four different variants: express (over the Manhattan bridge), and local (via
* lower Manhattan and the tunnel) x to Astoria and to Coney Island. During construction, it
* sometimes has a fifth variant: along the D line to Coney Island after 59th St (or from Coney
* Island to 59th).
*
* Route names are intended for very general customer information, but sometimes there is a need to
* know where a particular trip actually goes.
*
* Route Variant names are guaranteed to be unique (among variants for a single route) but not stable
* across graph builds especially based on different GTFS inputs. They are
* machine-generated on a best-effort basis. For instance, if a variant is the
* only variant of the N that ends at Coney Island, the name will be "N to Coney Island". But if
* multiple variants end at Coney Island (but have different stops elsewhere), that name would not
* be chosen. OTP also tries start and intermediate stations ("from Coney Island", or "via
* Whitehall", or even combinations ("from Coney Island via Whitehall"). But if there is no way to
* create a unique name from start/end/intermediate stops, then the best we can do is to create a
* "like [trip id]" name, which at least tells you where in the GTFS you can find a related trip.
*
* @author novalis
*
*/
@XmlRootElement(name = "RouteVariant")
public class RouteVariant implements Serializable {
private static final Logger LOG = LoggerFactory.getLogger(RouteVariant.class);
private static final long serialVersionUID = -3110443015998033630L;
/*
* This indicates that trips with multipledirection_ids are part of this variant. It should probably never be used, because generally trips making
* the same stops in the same order will have the same direction
*/
private static final String MULTIDIRECTION = "[multidirection]";
private String name; // "N via Whitehall"
private TraverseMode mode;
private ArrayList trips;
private ArrayList stops;
/** An unordered list of all segments for this route */
@JsonIgnore
private ArrayList segments;
/**
* An ordered list of segments that represents one characteristic trip (or trip pattern) on this variant
*/
@JsonIgnore
private ArrayList exemplarSegments;
@JsonIgnore
private ArrayList interlines;
private Route route;
private String direction;
private LineString geometry;
public RouteVariant() {
// needed for JAXB but unused
}
public RouteVariant(Route route, ArrayList stops) {
this.route = route;
this.stops = stops;
trips = new ArrayList();
segments = new ArrayList();
exemplarSegments = new ArrayList();
interlines = new ArrayList();
this.mode = GtfsLibrary.getTraverseMode(route);
}
public void addExemplarSegment(RouteSegment segment) {
exemplarSegments.add(segment);
}
public void addSegment(RouteSegment segment) {
segments.add(segment);
}
@JsonIgnore
public List getSegments() {
return segments;
}
public boolean isExemplarSet() {
return !exemplarSegments.isEmpty();
}
public void cleanup() {
trips.trimToSize();
stops.trimToSize();
exemplarSegments.trimToSize();
// topological sort on segments to make sure that they are in order
// since segments only know about their next edges, we must build a mapping from prior-edge
// to segment; while we're at it, we find the first segment.
HashMap successors = new HashMap();
RouteSegment segment = null;
for (RouteSegment s : exemplarSegments) {
if (s.hopIn == null) {
segment = s;
} else {
successors.put(s.hopIn, s);
}
}
int i = 0;
while (segment != null) {
exemplarSegments.set(i++, segment);
segment = successors.get(segment.hopOut);
}
if (i != exemplarSegments.size()) {
LOG.error("Failed to organize hops in route variant " + name);
}
}
@JsonIgnore
public List segmentsAfter(RouteSegment segment) {
HashMap successors = new HashMap();
for (RouteSegment s : segments) {
if (s.hopIn != null) {
successors.put(s.hopIn, s);
}
}
// skip this seg
segment = successors.get(segment.hopOut);
ArrayList out = new ArrayList();
while (segment != null) {
out.add(segment);
segment = successors.get(segment.hopOut);
}
return out;
}
@XmlElementWrapper
@XmlElement(name = "stop")
@XmlJavaTypeAdapter(StopAgencyAndIdAdapter.class)
public List getStops() {
return stops;
}
public Route getRoute() {
return route;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@XmlElementWrapper
@XmlElement(name = "trip")
public List getTrips() {
return trips;
}
public String toString() {
return "RouteVariant(" + name + ")";
}
public void setDirection(String direction) {
this.direction = direction;
}
@XmlElement
public String getDirection() {
return direction;
}
@JsonSerialize(using = EncodedPolylineJSONSerializer.class)
@XmlJavaTypeAdapter(LineStringAdapter.class)
public LineString getGeometry() {
if (geometry == null) {
List coords = new ArrayList();
for (RouteSegment segment : exemplarSegments) {
if (segment.hopOut != null) {
Geometry segGeometry = segment.getGeometry();
coords.addAll(Arrays.asList(segGeometry.getCoordinates()));
}
}
Coordinate[] coordArray = new Coordinate[coords.size()];
geometry = GeometryUtils.getGeometryFactory().createLineString(
coords.toArray(coordArray));
}
return geometry;
}
/**
* @param index The index of the segment in the list
* @return The partial geometry between this segment's stop and the next one.
*/
public LineString getGeometrySegment(int index) {
RouteSegment segment = exemplarSegments.get(index);
if (segment.hopOut != null) {
return GeometryUtils.getGeometryFactory().createLineString(
segment.getGeometry().getCoordinates());
}
return null;
}
@JsonIgnore
public TraverseMode getTraverseMode() {
return mode;
}
public void setGeometry(LineString geometry) {
this.geometry = geometry;
}
public void addInterline(PatternInterlineDwell dwell) {
interlines.add(dwell);
}
@JsonIgnore
public List getInterlines() {
return interlines;
}
public void addTrip(Trip trip, int number) {
this.trips.add(new TripsModelInfo(trip.getTripHeadsign(), number, trip.getServiceId()
.getId(), trip.getId()));
if (direction == null) {
direction = trip.getDirectionId();
} else {
if (!direction.equals(trip.getDirectionId())) {
direction = MULTIDIRECTION;
}
}
}
}