/**
* Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org>
* Copyright (C) 2011 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onebusaway.transit_data_federation.impl.beans.itineraries;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.ObjectUtils;
import org.onebusaway.collections.CollectionsLibrary;
import org.onebusaway.exceptions.NoSuchTripServiceException;
import org.onebusaway.exceptions.ServiceException;
import org.onebusaway.geospatial.model.CoordinatePoint;
import org.onebusaway.geospatial.model.EncodedPolylineBean;
import org.onebusaway.geospatial.services.PolylineEncoder;
import org.onebusaway.geospatial.services.SphericalGeometryLibrary;
import org.onebusaway.gtfs.model.AgencyAndId;
import org.onebusaway.transit_data.model.ListBean;
import org.onebusaway.transit_data.model.StopBean;
import org.onebusaway.transit_data.model.oba.LocalSearchResult;
import org.onebusaway.transit_data.model.oba.MinTravelTimeToStopsBean;
import org.onebusaway.transit_data.model.oba.TimedPlaceBean;
import org.onebusaway.transit_data.model.schedule.FrequencyBean;
import org.onebusaway.transit_data.model.tripplanning.ConstraintsBean;
import org.onebusaway.transit_data.model.tripplanning.EdgeNarrativeBean;
import org.onebusaway.transit_data.model.tripplanning.ItinerariesBean;
import org.onebusaway.transit_data.model.tripplanning.ItineraryBean;
import org.onebusaway.transit_data.model.tripplanning.LegBean;
import org.onebusaway.transit_data.model.tripplanning.LocationBean;
import org.onebusaway.transit_data.model.tripplanning.Modes;
import org.onebusaway.transit_data.model.tripplanning.StreetLegBean;
import org.onebusaway.transit_data.model.tripplanning.TransitLegBean;
import org.onebusaway.transit_data.model.tripplanning.TransitLocationBean;
import org.onebusaway.transit_data.model.tripplanning.TransitShedConstraintsBean;
import org.onebusaway.transit_data.model.tripplanning.VertexBean;
import org.onebusaway.transit_data.model.trips.TripBean;
import org.onebusaway.transit_data_federation.impl.beans.ApplicationBeanLibrary;
import org.onebusaway.transit_data_federation.impl.beans.FrequencyBeanLibrary;
import org.onebusaway.transit_data_federation.impl.otp.OBAState;
import org.onebusaway.transit_data_federation.impl.otp.OBATraverseOptions;
import org.onebusaway.transit_data_federation.impl.otp.graph.AbstractBlockVertex;
import org.onebusaway.transit_data_federation.impl.otp.graph.AbstractStopVertex;
import org.onebusaway.transit_data_federation.impl.otp.graph.ArrivalVertex;
import org.onebusaway.transit_data_federation.impl.otp.graph.BlockArrivalVertex;
import org.onebusaway.transit_data_federation.impl.otp.graph.BlockDepartureVertex;
import org.onebusaway.transit_data_federation.impl.otp.graph.DepartureVertex;
import org.onebusaway.transit_data_federation.impl.otp.graph.tp.TPBlockArrivalVertex;
import org.onebusaway.transit_data_federation.impl.otp.graph.tp.TPBlockDepartureVertex;
import org.onebusaway.transit_data_federation.impl.otp.graph.tp.TPTransferEdge;
import org.onebusaway.transit_data_federation.model.ShapePoints;
import org.onebusaway.transit_data_federation.model.narrative.StopTimeNarrative;
import org.onebusaway.transit_data_federation.services.AgencyAndIdLibrary;
import org.onebusaway.transit_data_federation.services.ArrivalAndDepartureQuery;
import org.onebusaway.transit_data_federation.services.ArrivalAndDepartureService;
import org.onebusaway.transit_data_federation.services.beans.ItinerariesBeanService;
import org.onebusaway.transit_data_federation.services.beans.StopBeanService;
import org.onebusaway.transit_data_federation.services.beans.TripBeanService;
import org.onebusaway.transit_data_federation.services.blocks.BlockCalendarService;
import org.onebusaway.transit_data_federation.services.blocks.BlockInstance;
import org.onebusaway.transit_data_federation.services.blocks.BlockTripInstance;
import org.onebusaway.transit_data_federation.services.blocks.BlockTripInstanceLibrary;
import org.onebusaway.transit_data_federation.services.narrative.NarrativeService;
import org.onebusaway.transit_data_federation.services.otp.OTPConfigurationService;
import org.onebusaway.transit_data_federation.services.otp.TransitShedPathService;
import org.onebusaway.transit_data_federation.services.realtime.ArrivalAndDepartureInstance;
import org.onebusaway.transit_data_federation.services.realtime.BlockLocation;
import org.onebusaway.transit_data_federation.services.shapes.ShapePointService;
import org.onebusaway.transit_data_federation.services.transit_graph.BlockEntry;
import org.onebusaway.transit_data_federation.services.transit_graph.BlockStopTimeEntry;
import org.onebusaway.transit_data_federation.services.transit_graph.BlockTripEntry;
import org.onebusaway.transit_data_federation.services.transit_graph.FrequencyEntry;
import org.onebusaway.transit_data_federation.services.transit_graph.StopEntry;
import org.onebusaway.transit_data_federation.services.transit_graph.StopTimeEntry;
import org.onebusaway.transit_data_federation.services.transit_graph.TransitGraphDao;
import org.onebusaway.transit_data_federation.services.transit_graph.TripEntry;
import org.onebusaway.transit_data_federation.services.tripplanner.ItinerariesService;
import org.opentripplanner.routing.core.Edge;
import org.opentripplanner.routing.core.EdgeNarrative;
import org.opentripplanner.routing.core.Graph;
import org.opentripplanner.routing.core.GraphVertex;
import org.opentripplanner.routing.core.HasEdges;
import org.opentripplanner.routing.core.State;
import org.opentripplanner.routing.core.TraverseMode;
import org.opentripplanner.routing.core.Vertex;
import org.opentripplanner.routing.edgetype.StreetEdge;
import org.opentripplanner.routing.edgetype.StreetTraversalPermission;
import org.opentripplanner.routing.edgetype.StreetVertex;
import org.opentripplanner.routing.services.GraphService;
import org.opentripplanner.routing.services.StreetVertexIndexService;
import org.opentripplanner.routing.spt.BasicShortestPathTree;
import org.opentripplanner.routing.spt.GraphPath;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.LineString;
@Component
public class ItinerariesBeanServiceImpl implements ItinerariesBeanService {
private static final String MODE_WALK = "walk";
private static final String MODE_BICYCLE = "bicycle";
private static final String MODE_TRANSIT = "transit";
private ItinerariesService _itinerariesService;
private TransitShedPathService _transitShedPathService;
private StreetVertexIndexService _streetVertexIndexService;
private GraphService _graphService;
private OTPConfigurationService _otpConfigurationService;
private TripBeanService _tripBeanService;
private NarrativeService _narrativeService;
private StopBeanService _stopBeanService;
private ShapePointService _shapePointService;
private TransitGraphDao _transitGraphDao;
private ArrivalAndDepartureService _arrivalAndDepartureService;
private BlockCalendarService _blockCalendarService;
private boolean _enabled = true;
@Autowired
public void setItinerariesService(ItinerariesService itinerariesService) {
_itinerariesService = itinerariesService;
}
@Autowired
public void setTransitShedPathService(
TransitShedPathService transitShedPathService) {
_transitShedPathService = transitShedPathService;
}
@Autowired
public void setStreetVertexIndexService(
StreetVertexIndexService streetVertexIndexService) {
_streetVertexIndexService = streetVertexIndexService;
}
@Autowired
public void setGraphService(GraphService graphService) {
_graphService = graphService;
}
@Autowired
public void setOtpConfigurationService(
OTPConfigurationService otpConfigurationService) {
_otpConfigurationService = otpConfigurationService;
}
@Autowired
public void setTripBeanService(TripBeanService tripBeanService) {
_tripBeanService = tripBeanService;
}
@Autowired
public void setStopBeanService(StopBeanService stopBeanService) {
_stopBeanService = stopBeanService;
}
@Autowired
public void setNarrativeService(NarrativeService narrativeService) {
_narrativeService = narrativeService;
}
@Autowired
public void setShapePointService(ShapePointService shapePointService) {
_shapePointService = shapePointService;
}
@Autowired
public void setTransitGraphDao(TransitGraphDao transitGraphDao) {
_transitGraphDao = transitGraphDao;
}
@Autowired
public void setArrivalAndDepartureService(
ArrivalAndDepartureService arrivalAndDepartureService) {
_arrivalAndDepartureService = arrivalAndDepartureService;
}
@Autowired
public void setBlockCalendarService(BlockCalendarService blockCalendarService) {
_blockCalendarService = blockCalendarService;
}
public void setEnabled(boolean enabled) {
_enabled = enabled;
}
/****
* {@link ItinerariesBeanService} Interface
****/
@Override
public ItinerariesBean getItinerariesBetween(TransitLocationBean from,
TransitLocationBean to, long targetTime, ConstraintsBean constraints)
throws ServiceException {
if (!_enabled)
throw new ServiceException("service disabled");
OBATraverseOptions options = createTraverseOptions();
applyConstraintsToOptions(constraints, options);
List<GraphPath> paths = _itinerariesService.getItinerariesBetween(from, to,
targetTime, options);
LocationBean fromBean = getPointAsLocation(from);
LocationBean toBean = getPointAsLocation(to);
ItinerariesBean itineraries = getPathsAsItineraries(paths, fromBean,
toBean, options);
ensureSelectedItineraryIsIncluded(from, to, targetTime, itineraries,
constraints.getSelectedItinerary(), options);
if (options.isArriveBy())
Collections.sort(itineraries.getItineraries(), new SortByArrival());
else
Collections.sort(itineraries.getItineraries(), new SortByDeparture());
return itineraries;
}
@Override
public ListBean<VertexBean> getStreetGraphForRegion(double latFrom,
double lonFrom, double latTo, double lonTo) {
if (!_enabled)
throw new ServiceException("service disabled");
double x1 = Math.min(lonFrom, lonTo);
double x2 = Math.max(lonFrom, lonTo);
double y1 = Math.min(latFrom, latTo);
double y2 = Math.max(latFrom, latTo);
Envelope env = new Envelope(x1, x2, y1, y2);
Collection<Vertex> vertices = _streetVertexIndexService.getVerticesForEnvelope(env);
Map<Vertex, VertexBean> beansByVertex = new HashMap<Vertex, VertexBean>();
for (Vertex vertex : vertices)
getVertexAsBean(beansByVertex, vertex);
for (Vertex vertex : vertices) {
Collection<Edge> edges = null;
if (vertex instanceof HasEdges) {
HasEdges hasEdges = (HasEdges) vertex;
edges = hasEdges.getOutgoing();
} else {
Graph graph = _graphService.getGraph();
GraphVertex gv = graph.getGraphVertex(vertex.getLabel());
if (gv != null)
edges = gv.getOutgoing();
}
if (edges != null) {
VertexBean from = getVertexAsBean(beansByVertex, vertex);
List<EdgeNarrativeBean> edgeNarratives = new ArrayList<EdgeNarrativeBean>();
for (Edge edge : edges) {
if (edge instanceof EdgeNarrative) {
EdgeNarrative narrative = (EdgeNarrative) edge;
EdgeNarrativeBean narrativeBean = new EdgeNarrativeBean();
narrativeBean.setName(narrative.getName());
Geometry geom = narrative.getGeometry();
if (geom != null) {
List<CoordinatePoint> path = new ArrayList<CoordinatePoint>();
appendGeometryToPath(geom, path, true);
EncodedPolylineBean polyline = PolylineEncoder.createEncodings(path);
narrativeBean.setPath(polyline.getPoints());
}
narrativeBean.setFrom(from);
narrativeBean.setTo(getVertexAsBean(beansByVertex,
narrative.getToVertex()));
Map<String, Object> tags = new HashMap<String, Object>();
if (edge instanceof StreetEdge) {
StreetEdge streetEdge = (StreetEdge) edge;
StreetTraversalPermission permission = streetEdge.getPermission();
if (permission != null)
tags.put("access", permission.toString().toLowerCase());
}
if (!tags.isEmpty())
narrativeBean.setTags(tags);
edgeNarratives.add(narrativeBean);
}
}
if (!edgeNarratives.isEmpty())
from.setOutgoing(edgeNarratives);
}
}
List<VertexBean> beans = new ArrayList<VertexBean>(beansByVertex.values());
return new ListBean<VertexBean>(beans, false);
}
@Override
public MinTravelTimeToStopsBean getMinTravelTimeToStopsFrom(
CoordinatePoint location, long time,
TransitShedConstraintsBean constraints) {
if (!_enabled)
throw new ServiceException("service disabled");
OBATraverseOptions options = createTraverseOptions();
applyConstraintsToOptions(constraints.getConstraints(), options);
Coordinate c = new Coordinate(location.getLon(), location.getLat());
Vertex origin = _streetVertexIndexService.getClosestVertex(c, options);
State originState = new OBAState(time, origin, options);
BasicShortestPathTree tree = _transitShedPathService.getTransitShed(origin,
originState, options);
Map<StopEntry, Long> results = new HashMap<StopEntry, Long>();
for (State state : tree.getAllStates()) {
OBAState obaState = (OBAState) state;
Vertex v = state.getVertex();
if (v instanceof AbstractStopVertex) {
AbstractStopVertex stopVertex = (AbstractStopVertex) v;
StopEntry stop = stopVertex.getStop();
long initialWaitTime = obaState.getInitialWaitTime();
long duration = Math.abs(state.getTime() - time) - initialWaitTime;
if (!results.containsKey(stop) || results.get(stop) > duration)
results.put(stop, duration);
} else if (v instanceof AbstractBlockVertex) {
AbstractBlockVertex blockVertex = (AbstractBlockVertex) v;
ArrivalAndDepartureInstance instance = blockVertex.getInstance();
StopEntry stop = instance.getStop();
long initialWaitTime = obaState.getInitialWaitTime();
long duration = Math.abs(state.getTime() - time) - initialWaitTime;
if (!results.containsKey(stop) || results.get(stop) > duration)
results.put(stop, duration);
}
}
return getStopTravelTimesAsResultsBean(results, options.speed);
}
@Override
public List<TimedPlaceBean> getLocalPaths(ConstraintsBean constraints,
MinTravelTimeToStopsBean travelTimes, List<LocalSearchResult> localResults)
throws ServiceException {
if (!_enabled)
throw new ServiceException("service disabled");
long maxTripLength = constraints.getMaxTripDuration() * 1000;
List<TimedPlaceBean> beans = new ArrayList<TimedPlaceBean>();
double walkingVelocity = travelTimes.getWalkingVelocity() / 1000;
ConstraintsBean walkConstraints = new ConstraintsBean(constraints);
walkConstraints.setModes(CollectionsLibrary.set(Modes.WALK));
for (LocalSearchResult result : localResults) {
double placeLat = result.getLat();
double placeLon = result.getLon();
List<TripToStop> closestStops = new ArrayList<TripToStop>();
for (int index = 0; index < travelTimes.getSize(); index++) {
String stopIdAsString = travelTimes.getStopId(index);
AgencyAndId stopId = AgencyAndIdLibrary.convertFromString(stopIdAsString);
long currentTripDuration = travelTimes.getTravelTime(index);
double stopLat = travelTimes.getStopLat(index);
double stopLon = travelTimes.getStopLon(index);
double d = SphericalGeometryLibrary.distance(stopLat, stopLon,
placeLat, placeLon);
double t = currentTripDuration + d / walkingVelocity;
if (d <= constraints.getMaxWalkingDistance() && t < maxTripLength) {
closestStops.add(new TripToStop(stopId, currentTripDuration, t, index));
}
}
if (closestStops.isEmpty())
continue;
Collections.sort(closestStops);
double minTime = 0;
TripToStop minStop = null;
TransitLocationBean place = new TransitLocationBean();
place.setLat(result.getLat());
place.setLon(result.getLon());
for (TripToStop o : closestStops) {
long currentTripDuration = o.getTransitTimeToStop();
double minTimeToPlace = o.getMinTansitTimeToPlace();
// Short circuit if there is no way any of the remaining trips is going
// to be better than our current winner
if (minStop != null && minTimeToPlace > minTime)
break;
int remainingTime = (int) ((maxTripLength - currentTripDuration) / 1000);
walkConstraints.setMaxTripDuration(remainingTime);
int index = o.getIndex();
TransitLocationBean stopLocation = new TransitLocationBean();
stopLocation.setLat(travelTimes.getStopLat(index));
stopLocation.setLon(travelTimes.getStopLon(index));
ItinerariesBean itineraries = getItinerariesBetween(stopLocation,
place, System.currentTimeMillis(), walkConstraints);
for (ItineraryBean plan : itineraries.getItineraries()) {
double t = currentTripDuration
+ (plan.getEndTime() - plan.getStartTime());
if (minStop == null || t < minTime) {
minTime = t;
minStop = o;
}
}
}
if (minStop != null && minTime <= maxTripLength) {
TimedPlaceBean bean = new TimedPlaceBean();
bean.setPlaceId(result.getId());
bean.setStopId(ApplicationBeanLibrary.getId(minStop.getStopId()));
bean.setTime((int) (minTime / 1000));
beans.add(bean);
}
}
return beans;
}
/****
* Private Methods
****/
private OBATraverseOptions createTraverseOptions() {
OBATraverseOptions options = _otpConfigurationService.createTraverseOptions();
return options;
}
private void applyConstraintsToOptions(ConstraintsBean constraints,
OBATraverseOptions options) {
_otpConfigurationService.applyConstraintsToTraverseOptions(constraints,
options);
}
private LocationBean getPointAsLocation(TransitLocationBean p) {
LocationBean bean = new LocationBean();
bean.setLocation(new CoordinatePoint(p.getLat(), p.getLon()));
return bean;
}
private ItinerariesBean getPathsAsItineraries(List<GraphPath> paths,
LocationBean from, LocationBean to, OBATraverseOptions options) {
ItinerariesBean bean = new ItinerariesBean();
bean.setFrom(from);
bean.setTo(to);
List<ItineraryBean> beans = new ArrayList<ItineraryBean>();
bean.setItineraries(beans);
boolean computationTimeLimitReached = false;
if (!CollectionsLibrary.isEmpty(paths)) {
for (GraphPath path : paths) {
ItineraryBean itinerary = getPathAsItinerary(path, options);
beans.add(itinerary);
}
}
bean.setComputationTimeLimitReached(computationTimeLimitReached);
return bean;
}
private ItineraryBean getPathAsItinerary(GraphPath path,
OBATraverseOptions options) {
ItineraryBean itinerary = new ItineraryBean();
State startState = path.states.getFirst();
State endState = path.states.getLast();
itinerary.setStartTime(startState.getTime());
itinerary.setEndTime(endState.getTime());
List<LegBean> legs = new ArrayList<LegBean>();
itinerary.setLegs(legs);
/**
* We set the current state index to 1, skipping the first state, since it
* has no back edge
*/
List<State> states = new ArrayList<State>(path.states);
int currentIndex = 1;
while (currentIndex < states.size()) {
State state = states.get(currentIndex);
EdgeNarrative edgeNarrative = state.getBackEdgeNarrative();
TraverseMode mode = edgeNarrative.getMode();
if (mode.isTransit()) {
currentIndex = extendTransitLeg(states, currentIndex, options, legs);
} else {
currentIndex = extendStreetLeg(states, currentIndex, mode, legs);
}
}
return itinerary;
}
private int extendTransitLeg(List<State> states, int currentIndex,
OBATraverseOptions options, List<LegBean> legs) {
TransitLegBuilder builder = new TransitLegBuilder();
while (currentIndex < states.size()) {
State state = states.get(currentIndex);
Edge edge = state.getBackEdge();
EdgeNarrative narrative = state.getBackEdgeNarrative();
TraverseMode mode = narrative.getMode();
if (!mode.isTransit())
break;
Vertex vFrom = narrative.getFromVertex();
Vertex vTo = narrative.getToVertex();
if (vFrom instanceof BlockDepartureVertex) {
builder = extendTransitLegWithDepartureAndArrival(legs, builder,
(BlockDepartureVertex) vFrom, (BlockArrivalVertex) vTo);
} else if (vFrom instanceof TPBlockDepartureVertex) {
builder = extendTransitLegWithTPDepartureAndArrival(legs, builder,
(TPBlockDepartureVertex) vFrom, (TPBlockArrivalVertex) vTo);
} else if (vFrom instanceof BlockArrivalVertex) {
builder = extendTransitLegWithArrival(legs, builder,
(BlockArrivalVertex) vFrom, vTo, state, options);
} else if (vFrom instanceof ArrivalVertex) {
if (vTo instanceof BlockDepartureVertex) {
/**
* This vertex combination occurs when we are doing an "arrive by"
* trip and we need to do a transfer between two stops.
*/
ArrivalVertex fromStopVertex = (ArrivalVertex) vFrom;
StopEntry fromStop = fromStopVertex.getStop();
BlockDepartureVertex toStopVertex = (BlockDepartureVertex) vTo;
ArrivalAndDepartureInstance departureInstance = toStopVertex.getInstance();
StopEntry toStop = departureInstance.getStop();
addTransferLegIfNeeded(state, fromStop, toStop, options, legs);
}
} else if (edge instanceof TPTransferEdge) {
TPTransferEdge transferEdge = (TPTransferEdge) edge;
StopEntry fromStop = transferEdge.getFromStop();
StopEntry toStop = transferEdge.getToStop();
addTransferLegIfNeeded(state, fromStop, toStop, options, legs);
}
currentIndex++;
}
return currentIndex;
}
private void addTransferLegIfNeeded(State state, StopEntry fromStop,
StopEntry toStop, OBATraverseOptions options, List<LegBean> legs) {
if (!fromStop.equals(toStop)) {
long timeFrom = state.getBackState().getTime();
long timeTo = state.getTime();
GraphPath path = _itinerariesService.getWalkingItineraryBetweenStops(
fromStop, toStop, new Date(timeFrom), options);
if (path != null) {
ItineraryBean walk = getPathAsItinerary(path, options);
scaleItinerary(walk, timeFrom, timeTo);
legs.addAll(walk.getLegs());
}
}
}
private TransitLegBuilder extendTransitLegWithDepartureAndArrival(
List<LegBean> legs, TransitLegBuilder builder,
BlockDepartureVertex vFrom, BlockArrivalVertex vTo) {
ArrivalAndDepartureInstance from = vFrom.getInstance();
ArrivalAndDepartureInstance to = vTo.getInstance();
return extendTransitLegWithDepartureAndArrival(legs, builder, from, to);
}
private TransitLegBuilder extendTransitLegWithTPDepartureAndArrival(
List<LegBean> legs, TransitLegBuilder builder,
TPBlockDepartureVertex vFrom, TPBlockArrivalVertex vTo) {
ArrivalAndDepartureInstance from = vFrom.getDeparture();
ArrivalAndDepartureInstance to = vFrom.getArrival();
builder = extendTransitLegWithDepartureAndArrival(legs, builder, from, to);
return getTransitLegBuilderAsLeg(builder, legs);
}
private TransitLegBuilder extendTransitLegWithDepartureAndArrival(
List<LegBean> legs, TransitLegBuilder builder,
ArrivalAndDepartureInstance from, ArrivalAndDepartureInstance to) {
BlockTripEntry tripFrom = from.getBlockTrip();
BlockTripEntry tripTo = to.getBlockTrip();
if (builder.getBlockTripInstanceFrom() == null) {
builder.setScheduledDepartureTime(from.getScheduledDepartureTime());
builder.setPredictedDepartureTime(from.getPredictedDepartureTime());
builder.setBlockTripInstanceFrom(from.getBlockTripInstance());
builder.setFromStop(from);
}
if (!tripFrom.equals(tripTo)) {
/**
* We switch trips during the course of the block, so we clean up the
* current leg and introduce a new one
*/
/**
* We just split the difference for now
*/
long scheduledTransitionTime = (from.getScheduledDepartureTime() + to.getScheduledArrivalTime()) / 2;
long predictedTransitionTime = 0;
if (from.isPredictedDepartureTimeSet() && to.isPredictedArrivalTimeSet())
predictedTransitionTime = (from.getPredictedDepartureTime() + to.getPredictedArrivalTime()) / 2;
builder.setScheduledArrivalTime(scheduledTransitionTime);
builder.setPredictedArrivalTime(predictedTransitionTime);
builder.setToStop(null);
builder.setNextTrip(tripTo);
getTransitLegBuilderAsLeg(builder, legs);
builder = new TransitLegBuilder();
builder.setScheduledDepartureTime(scheduledTransitionTime);
builder.setPredictedDepartureTime(predictedTransitionTime);
builder.setBlockTripInstanceTo(to.getBlockTripInstance());
}
builder.setToStop(to);
builder.setScheduledArrivalTime(to.getScheduledArrivalTime());
builder.setPredictedArrivalTime(to.getPredictedArrivalTime());
return builder;
}
private TransitLegBuilder extendTransitLegWithArrival(List<LegBean> legs,
TransitLegBuilder builder, BlockArrivalVertex arrival, Vertex vTo,
State state, OBATraverseOptions options) {
/**
* Did we finish up a transit leg?
*/
if (vTo instanceof ArrivalVertex) {
/**
* We've finished up our transit leg, so publish the leg
*/
builder = getTransitLegBuilderAsLeg(builder, legs);
}
/**
* Did we have a transfer to another stop?
*/
else if (vTo instanceof DepartureVertex) {
/**
* We've finished up our transit leg either way, so publish the leg
*/
builder = getTransitLegBuilderAsLeg(builder, legs);
/**
* We've possibly transfered to another stop, so we need to insert the
* walk leg
*/
ArrivalAndDepartureInstance fromStopTimeInstance = arrival.getInstance();
StopEntry fromStop = fromStopTimeInstance.getStop();
DepartureVertex toStopVertex = (DepartureVertex) vTo;
StopEntry toStop = toStopVertex.getStop();
addTransferLegIfNeeded(state, fromStop, toStop, options, legs);
}
return builder;
}
private TransitLegBuilder getTransitLegBuilderAsLeg(
TransitLegBuilder builder, List<LegBean> legs) {
BlockTripInstance blockTripInstanceFrom = builder.getBlockTripInstanceFrom();
if (blockTripInstanceFrom == null)
return new TransitLegBuilder();
LegBean leg = createTransitLegFromBuilder(builder);
legs.add(leg);
return new TransitLegBuilder();
}
private LegBean createTransitLegFromBuilder(TransitLegBuilder builder) {
BlockTripInstance blockTripInstanceFrom = builder.getBlockTripInstanceFrom();
LegBean leg = new LegBean();
leg.setStartTime(builder.getBestDepartureTime());
leg.setEndTime(builder.getBestArrivalTime());
double distance = getTransitLegBuilderAsDistance(builder);
leg.setDistance(distance);
leg.setMode(MODE_TRANSIT);
TripEntry trip = blockTripInstanceFrom.getBlockTrip().getTrip();
TransitLegBean transitLeg = new TransitLegBean();
leg.setTransitLeg(transitLeg);
transitLeg.setServiceDate(blockTripInstanceFrom.getServiceDate());
FrequencyEntry frequencyLabel = blockTripInstanceFrom.getFrequencyLabel();
if (frequencyLabel != null) {
FrequencyBean frequency = FrequencyBeanLibrary.getBeanForFrequency(
blockTripInstanceFrom.getServiceDate(), frequencyLabel);
transitLeg.setFrequency(frequency);
}
TripBean tripBean = _tripBeanService.getTripForId(trip.getId());
transitLeg.setTrip(tripBean);
transitLeg.setScheduledDepartureTime(builder.getScheduledDepartureTime());
transitLeg.setScheduledArrivalTime(builder.getScheduledArrivalTime());
transitLeg.setPredictedDepartureTime(builder.getPredictedDepartureTime());
transitLeg.setPredictedArrivalTime(builder.getPredictedArrivalTime());
String path = getTransitLegBuilderAsPath(builder);
transitLeg.setPath(path);
applyFromStopDetailsForTransitLeg(builder, transitLeg, leg);
applyToStopDetailsForTransitLeg(builder, transitLeg, leg);
if (leg.getFrom() == null || leg.getTo() == null && path != null) {
List<CoordinatePoint> points = PolylineEncoder.decode(path);
if (leg.getFrom() == null)
leg.setFrom(points.get(0));
if (leg.getTo() == null)
leg.setTo(points.get(points.size() - 1));
}
return leg;
}
private double getTransitLegBuilderAsDistance(TransitLegBuilder builder) {
BlockTripInstance blockTripInstanceFrom = builder.getBlockTripInstanceFrom();
BlockTripEntry trip = blockTripInstanceFrom.getBlockTrip();
BlockStopTimeEntry fromStop = null;
BlockStopTimeEntry toStop = null;
if (builder.getFromStop() != null)
fromStop = builder.getFromStop().getBlockStopTime();
if (builder.getToStop() != null)
toStop = builder.getToStop().getBlockStopTime();
if (fromStop == null && toStop == null)
return trip.getTrip().getTotalTripDistance();
if (fromStop == null && toStop != null)
return toStop.getDistanceAlongBlock() - trip.getDistanceAlongBlock();
if (fromStop != null && toStop == null)
return trip.getDistanceAlongBlock()
+ trip.getTrip().getTotalTripDistance()
- fromStop.getDistanceAlongBlock();
return toStop.getDistanceAlongBlock() - fromStop.getDistanceAlongBlock();
}
private String getTransitLegBuilderAsPath(TransitLegBuilder builder) {
BlockTripInstance blockTripInstanceFrom = builder.getBlockTripInstanceFrom();
BlockTripEntry blockTrip = blockTripInstanceFrom.getBlockTrip();
TripEntry trip = blockTrip.getTrip();
AgencyAndId shapeId = trip.getShapeId();
if (shapeId == null)
return null;
ShapePoints shapePoints = _shapePointService.getShapePointsForShapeId(shapeId);
BlockStopTimeEntry fromStop = null;
BlockStopTimeEntry toStop = null;
if (builder.getFromStop() != null)
fromStop = builder.getFromStop().getBlockStopTime();
if (builder.getToStop() != null)
toStop = builder.getToStop().getBlockStopTime();
CoordinatePoint nextPoint = null;
BlockTripEntry nextBlockTrip = builder.getNextTrip();
if (nextBlockTrip != null) {
TripEntry nextTrip = nextBlockTrip.getTrip();
AgencyAndId nextShapeId = nextTrip.getShapeId();
if (nextShapeId != null) {
ShapePoints nextShapePoints = _shapePointService.getShapePointsForShapeId(nextShapeId);
nextPoint = nextShapePoints.getPointForIndex(0);
}
}
if (fromStop == null && toStop == null) {
return ShapeSupport.getFullPath(shapePoints, nextPoint);
}
if (fromStop == null && toStop != null) {
return ShapeSupport.getPartialPathToStop(shapePoints,
toStop.getStopTime());
}
if (fromStop != null && toStop == null) {
return ShapeSupport.getPartialPathFromStop(shapePoints,
fromStop.getStopTime(), nextPoint);
}
return ShapeSupport.getPartialPathBetweenStops(shapePoints,
fromStop.getStopTime(), toStop.getStopTime());
}
private void applyFromStopDetailsForTransitLeg(TransitLegBuilder builder,
TransitLegBean transitLeg, LegBean leg) {
ArrivalAndDepartureInstance fromStopTimeInstance = builder.getFromStop();
if (fromStopTimeInstance == null)
return;
BlockStopTimeEntry bstFrom = fromStopTimeInstance.getBlockStopTime();
StopTimeEntry fromStopTime = bstFrom.getStopTime();
StopTimeNarrative stopTimeNarrative = _narrativeService.getStopTimeForEntry(fromStopTime);
transitLeg.setRouteShortName(stopTimeNarrative.getRouteShortName());
transitLeg.setTripHeadsign(stopTimeNarrative.getStopHeadsign());
StopEntry fromStop = fromStopTimeInstance.getStop();
StopBean fromStopBean = _stopBeanService.getStopForId(fromStop.getId());
transitLeg.setFromStop(fromStopBean);
transitLeg.setFromStopSequence(fromStopTime.getSequence());
leg.setFrom(fromStop.getStopLocation());
BlockLocation blockLocation = fromStopTimeInstance.getBlockLocation();
if (blockLocation != null) {
AgencyAndId vehicleId = blockLocation.getVehicleId();
transitLeg.setVehicleId(AgencyAndIdLibrary.convertToString(vehicleId));
}
}
private void applyToStopDetailsForTransitLeg(TransitLegBuilder builder,
TransitLegBean transitLeg, LegBean leg) {
ArrivalAndDepartureInstance toStopTimeInstance = builder.getToStop();
if (toStopTimeInstance == null)
return;
StopEntry toStop = toStopTimeInstance.getStop();
StopBean toStopBean = _stopBeanService.getStopForId(toStop.getId());
transitLeg.setToStop(toStopBean);
BlockStopTimeEntry blockStopTime = toStopTimeInstance.getBlockStopTime();
StopTimeEntry stopTime = blockStopTime.getStopTime();
transitLeg.setToStopSequence(stopTime.getSequence());
leg.setTo(toStop.getStopLocation());
BlockLocation blockLocation = toStopTimeInstance.getBlockLocation();
if (blockLocation != null) {
AgencyAndId vehicleId = blockLocation.getVehicleId();
transitLeg.setVehicleId(AgencyAndIdLibrary.convertToString(vehicleId));
}
}
private int extendStreetLeg(List<State> states, int currentIndex,
TraverseMode mode, List<LegBean> legs) {
List<State> streetStates = new ArrayList<State>();
while (currentIndex < states.size()) {
State state = states.get(currentIndex);
EdgeNarrative narrative = state.getBackEdgeNarrative();
TraverseMode edgeMode = narrative.getMode();
if (mode != edgeMode)
break;
streetStates.add(state);
currentIndex++;
}
if (!streetStates.isEmpty()) {
getStreetLegBuilderAsLeg(streetStates, mode, legs);
}
return currentIndex;
}
private void getStreetLegBuilderAsLeg(List<State> streetStates,
TraverseMode mode, List<LegBean> legs) {
List<StreetLegBean> streetLegs = new ArrayList<StreetLegBean>();
StreetLegBean streetLeg = null;
List<CoordinatePoint> path = new ArrayList<CoordinatePoint>();
double distance = 0.0;
double totalDistance = 0.0;
long startTime = 0;
long endTime = 0;
CoordinatePoint from = null;
CoordinatePoint to = null;
for (State state : streetStates) {
EdgeNarrative edgeResult = state.getBackEdgeNarrative();
Geometry geom = edgeResult.getGeometry();
if (geom == null) {
continue;
}
String streetName = edgeResult.getName();
if (streetLeg == null
|| !ObjectUtils.equals(streetLeg.getStreetName(), streetName)) {
addPathToStreetLegIfApplicable(streetLeg, path, distance);
streetLeg = createStreetLeg(state);
streetLegs.add(streetLeg);
path = new ArrayList<CoordinatePoint>();
appendGeometryToPath(geom, path, true);
distance = edgeResult.getDistance();
} else {
appendGeometryToPath(geom, path, false);
distance += edgeResult.getDistance();
}
totalDistance += edgeResult.getDistance();
if (startTime == 0)
startTime = state.getBackState().getTime();
endTime = state.getTime();
if (!path.isEmpty()) {
if (from == null)
from = path.get(0);
to = path.get(path.size() - 1);
}
}
addPathToStreetLegIfApplicable(streetLeg, path, distance);
LegBean leg = new LegBean();
legs.add(leg);
leg.setStartTime(startTime);
leg.setEndTime(endTime);
leg.setMode(getStreetModeAsString(mode));
leg.setFrom(from);
leg.setTo(to);
leg.setDistance(totalDistance);
leg.setStreetLegs(streetLegs);
}
private void addPathToStreetLegIfApplicable(StreetLegBean streetLeg,
List<CoordinatePoint> path, double distance) {
if (streetLeg != null) {
EncodedPolylineBean polyline = PolylineEncoder.createEncodings(path);
streetLeg.setPath(polyline.getPoints());
streetLeg.setDistance(distance);
}
}
private void appendGeometryToPath(Geometry geom, List<CoordinatePoint> path,
boolean includeFirstPoint) {
if (geom instanceof LineString) {
LineString ls = (LineString) geom;
for (int i = 0; i < ls.getNumPoints(); i++) {
if (i == 0 && !includeFirstPoint)
continue;
Coordinate c = ls.getCoordinateN(i);
CoordinatePoint p = new CoordinatePoint(c.y, c.x);
path.add(p);
}
} else {
throw new IllegalStateException("unknown geometry: " + geom);
}
}
private StreetLegBean createStreetLeg(State state) {
StreetLegBean bean = new StreetLegBean();
bean.setStreetName(state.getBackEdgeNarrative().getName());
return bean;
}
private String getStreetModeAsString(TraverseMode mode) {
switch (mode) {
case BICYCLE:
return MODE_BICYCLE;
case WALK:
return MODE_WALK;
}
throw new IllegalStateException("unknown street mode: " + mode);
}
private void scaleItinerary(ItineraryBean bean, long timeFrom, long timeTo) {
long tStart = bean.getStartTime();
long tEnd = bean.getEndTime();
double ratio = (timeTo - timeFrom) / (tEnd - tStart);
bean.setStartTime(scaleTime(tStart, timeFrom, ratio, tStart));
bean.setEndTime(scaleTime(tStart, timeFrom, ratio, tEnd));
for (LegBean leg : bean.getLegs()) {
leg.setStartTime(scaleTime(tStart, timeFrom, ratio, leg.getStartTime()));
leg.setEndTime(scaleTime(tStart, timeFrom, ratio, leg.getEndTime()));
}
}
private long scaleTime(long tStartOrig, long tStartNew, double ratio, long t) {
return (long) ((t - tStartOrig) * ratio + tStartNew);
}
private void ensureSelectedItineraryIsIncluded(TransitLocationBean from,
TransitLocationBean to, long targetTime, ItinerariesBean itineraries,
ItineraryBean selected, OBATraverseOptions options) {
if (selected == null)
return;
if (!isItinerarySufficientlySpecified(selected))
return;
for (ItineraryBean itinerary : itineraries.getItineraries()) {
if (isItineraryMatch(itinerary, selected)) {
itinerary.setSelected(true);
return;
}
}
updateItinerary(selected, from, to, targetTime, options);
selected.setSelected(true);
itineraries.getItineraries().add(selected);
}
private boolean isItinerarySufficientlySpecified(ItineraryBean itinerary) {
for (LegBean leg : itinerary.getLegs()) {
if (!isLegSufficientlySpecified(leg))
return false;
}
return true;
}
private boolean isLegSufficientlySpecified(LegBean leg) {
if (leg == null)
return false;
if (leg.getFrom() == null || leg.getTo() == null)
return false;
String mode = leg.getMode();
if (MODE_TRANSIT.equals(mode)) {
if (!isTransitLegSufficientlySpecified(leg.getTransitLeg()))
return false;
} else if (MODE_WALK.equals(mode) || MODE_BICYCLE.equals(mode)) {
} else {
return false;
}
return true;
}
private boolean isTransitLegSufficientlySpecified(TransitLegBean leg) {
if (leg == null)
return false;
if (leg.getTrip() == null || leg.getTrip().getId() == null)
return false;
if (leg.getServiceDate() <= 0)
return false;
return true;
}
private boolean isItineraryMatch(ItineraryBean a, ItineraryBean b) {
List<String> instancesA = getTransitInstancesForItinerary(a);
List<String> instancesB = getTransitInstancesForItinerary(b);
return instancesA.equals(instancesB);
}
private List<String> getTransitInstancesForItinerary(ItineraryBean itinerary) {
List<String> instances = new ArrayList<String>();
for (LegBean leg : itinerary.getLegs()) {
TransitLegBean transitLeg = leg.getTransitLeg();
if (transitLeg != null) {
String instance = transitLeg.getTrip().getId() + " "
+ transitLeg.getServiceDate();
instances.add(instance);
}
}
return instances;
}
private void updateItinerary(ItineraryBean itinerary,
TransitLocationBean from, TransitLocationBean to, long targetTime,
OBATraverseOptions options) {
List<LegBean> legs = itinerary.getLegs();
int firstTransitLegIndex = -1;
/**
* Update the legs
*/
for (int i = 0; i < legs.size(); i++) {
LegBean leg = legs.get(i);
TransitLegBean transitLeg = leg.getTransitLeg();
if (transitLeg != null) {
LegBean updatedLeg = updateTransitLeg(transitLeg, options);
legs.set(i, updatedLeg);
if (firstTransitLegIndex == -1)
firstTransitLegIndex = i;
} else if (isStreetLeg(leg)) {
Date time = new Date(targetTime);
CoordinatePoint walkFrom = leg.getFrom();
CoordinatePoint walkTo = leg.getTo();
/**
* Adjust the start and end locations for walk-legs to match the query
* points. This is a hack, since it allows the client to pass in the
* original unmodified itinerary from a previous to call, but also
* slightly change their start or end point (aka user walks some
* distance) without having to understand how to update the itinerary
* object itself.
*/
if (i == 0)
walkFrom = from.getLocation();
if (i + 1 == legs.size())
walkTo = to.getLocation();
GraphPath path = _itinerariesService.getWalkingItineraryBetweenPoints(
walkFrom, walkTo, time, options);
if (path == null) {
throw new IllegalStateException("expected walking path to exist");
}
ItineraryBean walkItinerary = getPathAsItinerary(path, options);
legs.set(i, walkItinerary.getLegs().get(0));
}
}
/**
* Update the times
*/
if (firstTransitLegIndex == -1) {
} else {
long nextTime = legs.get(firstTransitLegIndex).getStartTime();
for (int i = firstTransitLegIndex - 1; i >= 0; i--) {
LegBean leg = legs.get(i);
if (isStreetLeg(leg)) {
long duration = leg.getEndTime() - leg.getStartTime();
leg.setEndTime(nextTime);
leg.setStartTime(nextTime - duration);
}
nextTime = leg.getStartTime();
}
long prevTime = legs.get(firstTransitLegIndex).getEndTime();
for (int i = firstTransitLegIndex + 1; i < legs.size(); i++) {
LegBean leg = legs.get(i);
if (isStreetLeg(leg)) {
long duration = leg.getEndTime() - leg.getStartTime();
leg.setStartTime(prevTime);
leg.setEndTime(prevTime + duration);
}
prevTime = leg.getEndTime();
}
itinerary.setStartTime(nextTime);
itinerary.setEndTime(prevTime);
}
}
private boolean isStreetLeg(LegBean leg) {
return MODE_WALK.equals(leg.getMode())
|| MODE_BICYCLE.equals(leg.getMode());
}
private LegBean updateTransitLeg(TransitLegBean transitLeg,
OBATraverseOptions options) {
TransitLegBuilder b = new TransitLegBuilder();
AgencyAndId tripId = AgencyAndIdLibrary.convertFromString(transitLeg.getTrip().getId());
TripEntry trip = _transitGraphDao.getTripEntryForId(tripId);
if (trip == null)
throw new NoSuchTripServiceException(transitLeg.getTrip().getId());
long serviceDate = transitLeg.getServiceDate();
AgencyAndId vehicleId = null;
if (transitLeg.getVehicleId() != null)
vehicleId = AgencyAndIdLibrary.convertFromString(transitLeg.getVehicleId());
if (transitLeg.getFromStop() != null
&& transitLeg.getFromStop().getId() != null) {
AgencyAndId fromStopId = AgencyAndIdLibrary.convertFromString(transitLeg.getFromStop().getId());
StopEntry fromStop = _transitGraphDao.getStopEntryForId(fromStopId, true);
int fromStopSequence = transitLeg.getFromStopSequence();
ArrivalAndDepartureQuery query = new ArrivalAndDepartureQuery();
query.setStop(fromStop);
query.setStopSequence(fromStopSequence);
query.setTrip(trip);
query.setServiceDate(serviceDate);
query.setVehicleId(vehicleId);
query.setTime(options.currentTime);
ArrivalAndDepartureInstance instance = _arrivalAndDepartureService.getArrivalAndDepartureForStop(query);
b.setFromStop(instance);
b.setBlockTripInstanceFrom(instance.getBlockTripInstance());
b.setScheduledDepartureTime(instance.getScheduledDepartureTime());
b.setPredictedDepartureTime(instance.getPredictedDepartureTime());
}
if (transitLeg.getToStop() != null
&& transitLeg.getToStop().getId() != null) {
AgencyAndId toStopId = AgencyAndIdLibrary.convertFromString(transitLeg.getToStop().getId());
StopEntry toStop = _transitGraphDao.getStopEntryForId(toStopId, true);
int toStopSequence = transitLeg.getToStopSequence();
ArrivalAndDepartureQuery query = new ArrivalAndDepartureQuery();
query.setStop(toStop);
query.setStopSequence(toStopSequence);
query.setTrip(trip);
query.setServiceDate(serviceDate);
query.setVehicleId(vehicleId);
query.setTime(options.currentTime);
ArrivalAndDepartureInstance instance = _arrivalAndDepartureService.getArrivalAndDepartureForStop(query);
b.setToStop(instance);
b.setBlockTripInstanceTo(instance.getBlockTripInstance());
b.setScheduledArrivalTime(instance.getScheduledArrivalTime());
b.setPredictedArrivalTime(instance.getPredictedArrivalTime());
}
if (b.getBlockTripInstanceFrom() == null) {
BlockEntry block = trip.getBlock();
BlockInstance blockInstance = _blockCalendarService.getBlockInstance(
block.getId(), serviceDate);
BlockTripInstance tripInstance = BlockTripInstanceLibrary.getBlockTripInstance(
blockInstance, trip.getId());
b.setBlockTripInstanceFrom(tripInstance);
}
return createTransitLegFromBuilder(b);
}
private VertexBean getVertexAsBean(Map<Vertex, VertexBean> beansByVertex,
Vertex vertex) {
VertexBean bean = beansByVertex.get(vertex);
if (bean == null) {
bean = new VertexBean();
bean.setId(vertex.getLabel());
bean.setLocation(new CoordinatePoint(vertex.getY(), vertex.getX()));
Map<String, Object> tags = new HashMap<String, Object>();
tags.put("class", vertex.getClass().getName());
if (vertex instanceof StreetVertex) {
StreetVertex sv = (StreetVertex) vertex;
StreetTraversalPermission perms = sv.getPermission();
if (perms != null)
tags.put("access", perms.toString().toLowerCase());
} else if (vertex instanceof AbstractStopVertex) {
AbstractStopVertex stopVertex = (AbstractStopVertex) vertex;
StopEntry stop = stopVertex.getStop();
StopBean stopBean = _stopBeanService.getStopForId(stop.getId());
tags.put("stop", stopBean);
}
bean.setTags(tags);
beansByVertex.put(vertex, bean);
}
return bean;
}
private MinTravelTimeToStopsBean getStopTravelTimesAsResultsBean(
Map<StopEntry, Long> results, double walkingVelocity) {
int n = results.size();
String[] stopIds = new String[n];
double[] lats = new double[n];
double[] lons = new double[n];
long[] times = new long[n];
int index = 0;
String agencyId = null;
for (Map.Entry<StopEntry, Long> entry : results.entrySet()) {
StopEntry stop = entry.getKey();
agencyId = stop.getId().getAgencyId();
Long time = entry.getValue();
stopIds[index] = ApplicationBeanLibrary.getId(stop.getId());
lats[index] = stop.getStopLat();
lons[index] = stop.getStopLon();
times[index] = time;
index++;
}
return new MinTravelTimeToStopsBean(agencyId, stopIds, lats, lons, times,
walkingVelocity);
}
private static class SortByDeparture implements Comparator<ItineraryBean> {
@Override
public int compare(ItineraryBean o1, ItineraryBean o2) {
long t1 = o1.getStartTime();
long t2 = o2.getStartTime();
return t1 == t2 ? 0 : (t1 < t2 ? -1 : 1);
}
}
private static class SortByArrival implements Comparator<ItineraryBean> {
@Override
public int compare(ItineraryBean o1, ItineraryBean o2) {
long t1 = o1.getEndTime();
long t2 = o2.getEndTime();
return t1 == t2 ? 0 : (t1 > t2 ? -1 : 1);
}
}
private static class TripToStop implements Comparable<TripToStop> {
private AgencyAndId _stopId;
private long _transitTimeToStop;
private double _minTransitTimeToPlace;
private int _index;
public TripToStop(AgencyAndId stopId, long transitTimeToStop,
double minTransitTimeToPlace, int index) {
_stopId = stopId;
_transitTimeToStop = transitTimeToStop;
_minTransitTimeToPlace = minTransitTimeToPlace;
_index = index;
}
public AgencyAndId getStopId() {
return _stopId;
}
public long getTransitTimeToStop() {
return _transitTimeToStop;
}
public double getMinTansitTimeToPlace() {
return _minTransitTimeToPlace;
}
public int getIndex() {
return _index;
}
public int compareTo(TripToStop o) {
return _minTransitTimeToPlace == o._minTransitTimeToPlace ? 0
: (_minTransitTimeToPlace < o._minTransitTimeToPlace ? -1 : 1);
}
}
}