use of org.opentripplanner.routing.graph.Edge in project OpenTripPlanner by opentripplanner.
the class NearbyStopFinder method stopAtDistanceForState.
/**
* Given a State at a TransitStop, bundle the TransitStop together with information about how far away it is
* and the geometry of the path leading up to the given State.
*
* TODO this should probably be merged with similar classes in Profile routing.
*/
public static StopAtDistance stopAtDistanceForState(State state) {
double distance = 0.0;
GraphPath graphPath = new GraphPath(state, false);
CoordinateArrayListSequence coordinates = new CoordinateArrayListSequence();
List<Edge> edges = new ArrayList<>();
for (Edge edge : graphPath.edges) {
if (edge instanceof StreetEdge) {
LineString geometry = edge.getGeometry();
if (geometry != null) {
if (coordinates.size() == 0) {
coordinates.extend(geometry.getCoordinates());
} else {
coordinates.extend(geometry.getCoordinates(), 1);
}
}
distance += edge.getDistance();
}
edges.add(edge);
}
if (coordinates.size() < 2) {
// Otherwise the walk step generator breaks.
ArrayList<Coordinate> coordinateList = new ArrayList<Coordinate>(2);
coordinateList.add(graphPath.states.get(1).getVertex().getCoordinate());
State lastState = graphPath.states.getLast().getBackState();
coordinateList.add(lastState.getVertex().getCoordinate());
coordinates = new CoordinateArrayListSequence(coordinateList);
}
StopAtDistance sd = new StopAtDistance((TransitStop) state.getVertex(), distance);
sd.geom = geometryFactory.createLineString(new PackedCoordinateSequence.Double(coordinates.toCoordinateArray()));
sd.edges = edges;
return sd;
}
use of org.opentripplanner.routing.graph.Edge in project OpenTripPlanner by opentripplanner.
the class StreetEdge method doTraverse.
/**
* return a StateEditor rather than a State so that we can make parking/mode switch modifications for kiss-and-ride.
*/
private StateEditor doTraverse(State s0, RoutingRequest options, TraverseMode traverseMode) {
boolean walkingBike = options.walkingBike;
boolean backWalkingBike = s0.isBackWalkingBike();
TraverseMode backMode = s0.getBackMode();
Edge backEdge = s0.getBackEdge();
if (backEdge != null) {
// TODO profiling indicates that this is a hot spot.
if (this.isReverseOf(backEdge) || backEdge.isReverseOf(this)) {
return null;
}
}
// Ensure we are actually walking, when walking a bike
backWalkingBike &= TraverseMode.WALK.equals(backMode);
walkingBike &= TraverseMode.WALK.equals(traverseMode);
/* Check whether this street allows the current mode. If not and we are biking, attempt to walk the bike. */
if (!canTraverse(options, traverseMode)) {
if (traverseMode == TraverseMode.BICYCLE) {
return doTraverse(s0, options.bikeWalkingOptions, TraverseMode.WALK);
}
return null;
}
// Automobiles have variable speeds depending on the edge type
double speed = calculateSpeed(options, traverseMode, s0.getTimeInMillis());
double time = getDistance() / speed;
double weight;
// TODO(flamholz): factor out this bike, wheelchair and walking specific logic to somewhere central.
if (options.wheelchairAccessible) {
weight = getSlopeSpeedEffectiveLength() / speed;
} else if (traverseMode.equals(TraverseMode.BICYCLE)) {
time = getSlopeSpeedEffectiveLength() / speed;
switch(options.optimize) {
case SAFE:
weight = bicycleSafetyFactor * getDistance() / speed;
break;
case GREENWAYS:
weight = bicycleSafetyFactor * getDistance() / speed;
if (bicycleSafetyFactor <= GREENWAY_SAFETY_FACTOR) {
// greenways are treated as even safer than they really are
weight *= 0.66;
}
break;
case FLAT:
/* see notes in StreetVertex on speed overhead */
weight = getDistance() / speed + getSlopeWorkCostEffectiveLength();
break;
case QUICK:
weight = getSlopeSpeedEffectiveLength() / speed;
break;
case TRIANGLE:
double quick = getSlopeSpeedEffectiveLength();
double safety = bicycleSafetyFactor * getDistance();
// TODO This computation is not coherent with the one for FLAT
double slope = getSlopeWorkCostEffectiveLength();
weight = quick * options.triangleTimeFactor + slope * options.triangleSlopeFactor + safety * options.triangleSafetyFactor;
weight /= speed;
break;
default:
weight = getDistance() / speed;
}
} else {
if (walkingBike) {
// take slopes into account when walking bikes
time = getSlopeSpeedEffectiveLength() / speed;
}
weight = time;
if (traverseMode.equals(TraverseMode.WALK)) {
// take slopes into account when walking
// FIXME: this causes steep stairs to be avoided. see #1297.
double costs = ElevationUtils.getWalkCostsForSlope(getDistance(), getMaxSlope());
// as the cost walkspeed is assumed to be for 4.8km/h (= 1.333 m/sec) we need to adjust
// for the walkspeed set by the user
double elevationUtilsSpeed = 4.0 / 3.0;
weight = costs * (elevationUtilsSpeed / speed);
// treat cost as time, as in the current model it actually is the same (this can be checked for maxSlope == 0)
time = weight;
/*
// debug code
if(weight > 100){
double timeflat = length / speed;
System.out.format("line length: %.1f m, slope: %.3f ---> slope costs: %.1f , weight: %.1f , time (flat): %.1f %n", length, elevationProfile.getMaxSlope(), costs, weight, timeflat);
}
*/
}
}
if (isStairs()) {
weight *= options.stairsReluctance;
} else {
// TODO: this is being applied even when biking or driving.
weight *= options.walkReluctance;
}
StateEditor s1 = s0.edit(this);
s1.setBackMode(traverseMode);
s1.setBackWalkingBike(walkingBike);
/* Handle no through traffic areas. */
if (this.isNoThruTraffic()) {
// Record transition into no-through-traffic area.
if (backEdge instanceof StreetEdge && !((StreetEdge) backEdge).isNoThruTraffic()) {
s1.setEnteredNoThroughTrafficArea();
}
// If we transitioned into a no-through-traffic area at some point, check if we are exiting it.
if (s1.hasEnteredNoThroughTrafficArea()) {
// on thru _Vertices_. This could certainly be improved somehow.
for (StreetEdge se : Iterables.filter(s1.getVertex().getOutgoing(), StreetEdge.class)) {
if (!se.isNoThruTraffic()) {
// This vertex has at least one through-traffic edge. We can't dominate it with a no-thru state.
return null;
}
}
}
}
/* Compute turn cost. */
StreetEdge backPSE;
if (backEdge != null && backEdge instanceof StreetEdge) {
backPSE = (StreetEdge) backEdge;
RoutingRequest backOptions = backWalkingBike ? s0.getOptions().bikeWalkingOptions : s0.getOptions();
double backSpeed = backPSE.calculateSpeed(backOptions, backMode, s0.getTimeInMillis());
// Units are seconds.
final double realTurnCost;
// Apply turn restrictions
if (options.arriveBy && !canTurnOnto(backPSE, s0, backMode)) {
return null;
} else if (!options.arriveBy && !backPSE.canTurnOnto(this, s0, traverseMode)) {
return null;
}
/*
* This is a subtle piece of code. Turn costs are evaluated differently during
* forward and reverse traversal. During forward traversal of an edge, the turn
* *into* that edge is used, while during reverse traversal, the turn *out of*
* the edge is used.
*
* However, over a set of edges, the turn costs must add up the same (for
* general correctness and specifically for reverse optimization). This means
* that during reverse traversal, we must also use the speed for the mode of
* the backEdge, rather than of the current edge.
*/
if (options.arriveBy && tov instanceof IntersectionVertex) {
// arrive-by search
IntersectionVertex traversedVertex = ((IntersectionVertex) tov);
realTurnCost = backOptions.getIntersectionTraversalCostModel().computeTraversalCost(traversedVertex, this, backPSE, backMode, backOptions, (float) speed, (float) backSpeed);
} else if (!options.arriveBy && fromv instanceof IntersectionVertex) {
// depart-after search
IntersectionVertex traversedVertex = ((IntersectionVertex) fromv);
realTurnCost = options.getIntersectionTraversalCostModel().computeTraversalCost(traversedVertex, backPSE, this, traverseMode, options, (float) backSpeed, (float) speed);
} else {
// In case this is a temporary edge not connected to an IntersectionVertex
LOG.debug("Not computing turn cost for edge {}", this);
realTurnCost = 0;
}
if (!traverseMode.isDriving()) {
// just a tie-breaker
s1.incrementWalkDistance(realTurnCost / 100);
}
long turnTime = (long) Math.ceil(realTurnCost);
time += turnTime;
weight += options.turnReluctance * realTurnCost;
}
if (walkingBike || TraverseMode.BICYCLE.equals(traverseMode)) {
if (!(backWalkingBike || TraverseMode.BICYCLE.equals(backMode))) {
s1.incrementTimeInSeconds(options.bikeSwitchTime);
s1.incrementWeight(options.bikeSwitchCost);
}
}
if (!traverseMode.isDriving()) {
s1.incrementWalkDistance(getDistance());
}
/* On the pre-kiss/pre-park leg, limit both walking and driving, either soft or hard. */
int roundedTime = (int) Math.ceil(time);
if (options.kissAndRide || options.parkAndRide) {
if (options.arriveBy) {
if (!s0.isCarParked())
s1.incrementPreTransitTime(roundedTime);
} else {
if (!s0.isEverBoarded())
s1.incrementPreTransitTime(roundedTime);
}
if (s1.isMaxPreTransitTimeExceeded(options)) {
if (options.softPreTransitLimiting) {
weight += calculateOverageWeight(s0.getPreTransitTime(), s1.getPreTransitTime(), options.maxPreTransitTime, options.preTransitPenalty, options.preTransitOverageRate);
} else
return null;
}
}
/* Apply a strategy for avoiding walking too far, either soft (weight increases) or hard limiting (pruning). */
if (s1.weHaveWalkedTooFar(options)) {
// if we're using a soft walk-limit
if (options.softWalkLimiting) {
// just slap a penalty for the overage onto s1
weight += calculateOverageWeight(s0.getWalkDistance(), s1.getWalkDistance(), options.getMaxWalkDistance(), options.softWalkPenalty, options.softWalkOverageRate);
} else {
// else, it's a hard limit; bail
LOG.debug("Too much walking. Bailing.");
return null;
}
}
s1.incrementTimeInSeconds(roundedTime);
s1.incrementWeight(weight);
return s1;
}
use of org.opentripplanner.routing.graph.Edge in project OpenTripPlanner by opentripplanner.
the class GTFSPatternHopFactory method loadTransfers.
private void loadTransfers(Graph graph) {
Collection<Transfer> transfers = _dao.getAllTransfers();
TransferTable transferTable = graph.getTransferTable();
for (Transfer sourceTransfer : transfers) {
// we thus expand transfers that use parent stations to all the member stops.
for (Transfer t : expandTransfer(sourceTransfer)) {
Stop fromStop = t.getFromStop();
Stop toStop = t.getToStop();
Route fromRoute = t.getFromRoute();
Route toRoute = t.getToRoute();
Trip fromTrip = t.getFromTrip();
Trip toTrip = t.getToTrip();
Vertex fromVertex = context.stopArriveNodes.get(fromStop);
Vertex toVertex = context.stopDepartNodes.get(toStop);
switch(t.getTransferType()) {
case 1:
// timed (synchronized) transfer
// Handle with edges that bypass the street network.
// from and to vertex here are stop_arrive and stop_depart vertices
// only add edge when it doesn't exist already
boolean hasTimedTransferEdge = false;
for (Edge outgoingEdge : fromVertex.getOutgoing()) {
if (outgoingEdge instanceof TimedTransferEdge) {
if (outgoingEdge.getToVertex() == toVertex) {
hasTimedTransferEdge = true;
break;
}
}
}
if (!hasTimedTransferEdge) {
new TimedTransferEdge(fromVertex, toVertex);
}
// add to transfer table to handle specificity
transferTable.addTransferTime(fromStop, toStop, fromRoute, toRoute, fromTrip, toTrip, StopTransfer.TIMED_TRANSFER);
break;
case 2:
// min transfer time
transferTable.addTransferTime(fromStop, toStop, fromRoute, toRoute, fromTrip, toTrip, t.getMinTransferTime());
break;
case 3:
// forbidden transfer
transferTable.addTransferTime(fromStop, toStop, fromRoute, toRoute, fromTrip, toTrip, StopTransfer.FORBIDDEN_TRANSFER);
break;
case 0:
default:
// preferred transfer
transferTable.addTransferTime(fromStop, toStop, fromRoute, toRoute, fromTrip, toTrip, StopTransfer.PREFERRED_TRANSFER);
break;
}
}
}
}
use of org.opentripplanner.routing.graph.Edge in project OpenTripPlanner by opentripplanner.
the class RoundBasedProfileRouter method route.
public void route() {
LOG.info("access modes: {}", request.accessModes);
LOG.info("egress modes: {}", request.egressModes);
LOG.info("direct modes: {}", request.directModes);
// TimeWindow could constructed in the caller, which does have access to the graph index.
this.window = new TimeWindow(request.fromTime, request.toTime, graph.index.servicesRunning(request.date));
// Establish search timeouts
long searchBeginTime = System.currentTimeMillis();
long abortTime = searchBeginTime + TIMEOUT * 1000;
LOG.info("Finding access/egress paths.");
// Look for stops that are within a given time threshold of the origin and destination
// Find the closest stop on each pattern near the origin and destination
// TODO consider that some stops may be closer by one mode than another
// and that some stops may be accessible by one mode but not another
ProfileStateStore store = RETAIN_PATTERNS ? new MultiProfileStateStore() : new SingleProfileStateStore();
for (ProfileState ps : findInitialStops(false)) {
store.put(ps);
}
LOG.info("Found {} initial stops", store.size());
// we don't want to generate trips that are artificially forced to go past a transit stop.
ROUNDS: for (int round = 0; round < MAX_ROUNDS; round++) {
long roundStart = System.currentTimeMillis();
LOG.info("Begin round {}; {} stops to explore", round, store.size());
ProfileStateStore previousStore = store;
store = RETAIN_PATTERNS ? new MultiProfileStateStore((MultiProfileStateStore) store) : new SingleProfileStateStore((SingleProfileStateStore) store);
Set<TripPattern> patternsToExplore = Sets.newHashSet();
// explore all of the patterns at the stops visited on the previous round
for (TransitStop tstop : previousStore.keys()) {
Collection<TripPattern> patterns = graph.index.patternsForStop.get(tstop.getStop());
patternsToExplore.addAll(patterns);
}
LOG.info("Exploring {} patterns", patternsToExplore.size());
// propagate all of the bounds down each pattern
PATTERNS: for (final TripPattern pattern : patternsToExplore) {
STOPS: for (int i = 0; i < pattern.stopVertices.length; i++) {
if (!previousStore.containsKey(pattern.stopVertices[i]))
continue STOPS;
Collection<ProfileState> statesToPropagate;
// only propagate nondominated states
statesToPropagate = previousStore.get(pattern.stopVertices[i]);
// don't propagate states that use the same pattern
statesToPropagate = Collections2.filter(statesToPropagate, new Predicate<ProfileState>() {
@Override
public boolean apply(ProfileState input) {
// don't reboard same pattern, and don't board patterns that are better boarded elsewhere
return !input.containsPattern(pattern) && (input.targetPatterns == null || input.targetPatterns.contains(pattern));
}
});
if (statesToPropagate.isEmpty())
continue STOPS;
int minWaitTime = Integer.MAX_VALUE;
int maxWaitTime = Integer.MIN_VALUE;
// (i.e. the transfer time is different for the initial boarding than transfers)
for (FrequencyEntry freq : pattern.scheduledTimetable.frequencyEntries) {
if (freq.exactTimes) {
throw new IllegalStateException("Exact times not yet supported in profile routing.");
}
int overlap = window.overlap(freq.startTime, freq.endTime, freq.tripTimes.serviceCode);
if (overlap > 0) {
if (freq.headway > maxWaitTime)
maxWaitTime = freq.headway;
// if any frequency-based trips are running a wait of 0 is always possible, because it could come
// just as you show up at the stop.
minWaitTime = 0;
}
}
DESTSTOPS: for (int j = i + 1; j < pattern.stopVertices.length; j++) {
// how long does it take to ride this trip from i to j?
int minRideTime = Integer.MAX_VALUE;
int maxRideTime = Integer.MIN_VALUE;
// how long does it take to get to stop j from stop i?
for (TripTimes tripTimes : pattern.scheduledTimetable.tripTimes) {
int depart = tripTimes.getDepartureTime(i);
int arrive = tripTimes.getArrivalTime(j);
if (window.includes(depart) && window.includes(arrive) && window.servicesRunning.get(tripTimes.serviceCode)) {
int t = arrive - depart;
if (t < minRideTime)
minRideTime = t;
if (t > maxRideTime)
maxRideTime = t;
}
}
/* Do the same thing for any frequency-based trips. */
for (FrequencyEntry freq : pattern.scheduledTimetable.frequencyEntries) {
TripTimes tt = freq.tripTimes;
int overlap = window.overlap(freq.startTime, freq.endTime, tt.serviceCode);
if (overlap == 0)
continue;
int depart = tt.getDepartureTime(i);
int arrive = tt.getArrivalTime(j);
int t = arrive - depart;
if (t < minRideTime)
minRideTime = t;
if (t > maxRideTime)
maxRideTime = t;
}
if (minWaitTime == Integer.MAX_VALUE || maxWaitTime == Integer.MIN_VALUE || minRideTime == Integer.MAX_VALUE || maxRideTime == Integer.MIN_VALUE)
// no trips in window that arrive at stop
continue DESTSTOPS;
if (minRideTime < 0 || maxRideTime < 0) {
LOG.error("Pattern {} travels backwards in time between stop {} and {}", pattern, pattern.stopVertices[i].getStop(), pattern.stopVertices[j].getStop());
continue DESTSTOPS;
}
// we've already checked to ensure we're not reboarding the same pattern
for (ProfileState ps : statesToPropagate) {
ProfileState ps2 = ps.propagate(minWaitTime + minRideTime, maxWaitTime + maxRideTime);
if (ps2.upperBound > CUTOFF_SECONDS)
continue;
ps2.stop = pattern.stopVertices[j];
ps2.accessType = Type.TRANSIT;
if (RETAIN_PATTERNS)
ps2.patterns = new TripPattern[] { pattern };
store.put(ps2);
}
}
}
}
// merge states that came from the same stop.
if (RETAIN_PATTERNS) {
LOG.info("Round completed, merging similar states");
((MultiProfileStateStore) store).mergeStates();
}
for (ProfileState ps : store.getAll()) {
retainedStates.put(ps.stop, ps);
}
if (round == MAX_ROUNDS - 1) {
LOG.info("Finished round {} in {} seconds", round, (System.currentTimeMillis() - roundStart) / 1000);
break ROUNDS;
}
// propagate states to nearby stops (transfers)
LOG.info("Finding transfers . . .");
// avoid concurrent modification
Set<TransitStop> touchedStopKeys = new HashSet<TransitStop>(store.keys());
for (TransitStop tstop : touchedStopKeys) {
List<Tuple2<TransitStop, Integer>> accessTimes = Lists.newArrayList();
// find transfers for the stop
for (Edge e : tstop.getOutgoing()) {
if (e instanceof SimpleTransfer) {
SimpleTransfer t = (SimpleTransfer) e;
int time = (int) (t.getDistance() / request.walkSpeed);
accessTimes.add(new Tuple2((TransitStop) e.getToVertex(), time));
}
}
// only transfer from nondominated states. only transfer to each pattern once
Collection<ProfileState> statesAtStop = store.get(tstop);
TObjectIntHashMap<TripPattern> minBoardTime = new TObjectIntHashMap<TripPattern>(1000, .75f, Integer.MAX_VALUE);
Map<TripPattern, ProfileState> optimalBoardState = Maps.newHashMap();
List<ProfileState> xferStates = Lists.newArrayList();
// make a hashset of the patterns that stop here, because we don't want to transfer to them at another stop
HashSet<TripPattern> patternsAtSource = new HashSet<TripPattern>(graph.index.patternsForStop.get(tstop.getStop()));
for (ProfileState ps : statesAtStop) {
for (Tuple2<TransitStop, Integer> atime : accessTimes) {
ProfileState ps2 = ps.propagate(atime.b);
ps2.accessType = Type.TRANSFER;
ps2.stop = atime.a;
for (TripPattern patt : graph.index.patternsForStop.get(atime.a.getStop())) {
// don't transfer to patterns that we can board at this stop.
if (patternsAtSource.contains(patt))
continue;
if (atime.b < minBoardTime.get(patt)) {
minBoardTime.put(patt, atime.b);
optimalBoardState.put(patt, ps2);
}
}
xferStates.add(ps2);
}
}
for (Entry<TripPattern, ProfileState> e : optimalBoardState.entrySet()) {
ProfileState ps = e.getValue();
if (ps.targetPatterns == null)
ps.targetPatterns = Sets.newHashSet();
ps.targetPatterns.add(e.getKey());
}
for (ProfileState ps : xferStates) {
if (ps.targetPatterns != null && !ps.targetPatterns.isEmpty()) {
store.put(ps);
}
}
}
LOG.info("Finished round {} in {} seconds", round, (System.currentTimeMillis() - roundStart) / 1000);
}
LOG.info("Finished profile routing in {} seconds", (System.currentTimeMillis() - searchBeginTime) / 1000);
makeSurfaces();
LOG.info("Finished analyst request in {} seconds total", (System.currentTimeMillis() - searchBeginTime) / 1000);
}
use of org.opentripplanner.routing.graph.Edge in project OpenTripPlanner by opentripplanner.
the class AlertPatch method apply.
public void apply(Graph graph) {
Agency agency = null;
if (feedId != null) {
Map<String, Agency> agencies = graph.index.agenciesForFeedId.get(feedId);
agency = this.agency != null ? agencies.get(this.agency) : null;
}
Route route = this.route != null ? graph.index.routeForId.get(this.route) : null;
Stop stop = this.stop != null ? graph.index.stopForId.get(this.stop) : null;
Trip trip = this.trip != null ? graph.index.tripForId.get(this.trip) : null;
if (route != null || trip != null || agency != null) {
Collection<TripPattern> tripPatterns = null;
if (trip != null) {
tripPatterns = new LinkedList<>();
TripPattern tripPattern = graph.index.patternForTrip.get(trip);
if (tripPattern != null) {
tripPatterns.add(tripPattern);
}
} else if (route != null) {
tripPatterns = graph.index.patternsForRoute.get(route);
} else {
// Find patterns for the feed.
tripPatterns = graph.index.patternsForFeedId.get(feedId);
}
if (tripPatterns != null) {
for (TripPattern tripPattern : tripPatterns) {
if (direction != null && !direction.equals(tripPattern.getDirection())) {
continue;
}
if (directionId != -1 && directionId == tripPattern.directionId) {
continue;
}
for (int i = 0; i < tripPattern.stopPattern.stops.length; i++) {
if (stop == null || stop.equals(tripPattern.stopPattern.stops[i])) {
graph.addAlertPatch(tripPattern.boardEdges[i], this);
graph.addAlertPatch(tripPattern.alightEdges[i], this);
}
}
}
}
} else if (stop != null) {
TransitStop transitStop = graph.index.stopVertexForStop.get(stop);
for (Edge edge : transitStop.getOutgoing()) {
if (edge instanceof PreBoardEdge) {
graph.addAlertPatch(edge, this);
break;
}
}
for (Edge edge : transitStop.getIncoming()) {
if (edge instanceof PreAlightEdge) {
graph.addAlertPatch(edge, this);
break;
}
}
}
}
Aggregations