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

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;
            }
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy