/** * Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org> * * 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.bundle.tasks.transfer_pattern; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import org.onebusaway.collections.Counter; import org.onebusaway.collections.FactoryMap; import org.onebusaway.collections.Range; import org.onebusaway.collections.tuple.Pair; import org.onebusaway.collections.tuple.Tuples; import org.onebusaway.geospatial.model.CoordinateBounds; import org.onebusaway.geospatial.services.SphericalGeometryLibrary; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.calendar.ServiceDate; import org.onebusaway.transit_data_federation.bundle.tasks.transfer_pattern.graph.HasStopTimeInstanceTransitVertex; import org.onebusaway.transit_data_federation.bundle.tasks.transfer_pattern.graph.TPOfflineBlockArrivalVertex; import org.onebusaway.transit_data_federation.bundle.tasks.transfer_pattern.graph.TPOfflineNearbyStopsVertex; import org.onebusaway.transit_data_federation.bundle.tasks.transfer_pattern.graph.TPOfflineOriginVertex; import org.onebusaway.transit_data_federation.bundle.tasks.transfer_pattern.graph.TPOfflineTransferEdge; import org.onebusaway.transit_data_federation.bundle.tasks.transfer_pattern.graph.TPOfflineTransferVertex; import org.onebusaway.transit_data_federation.impl.otp.GraphContext; 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.HasStopTransitVertex; import org.onebusaway.transit_data_federation.model.ServiceDateSummary; import org.onebusaway.transit_data_federation.services.AgencyAndIdLibrary; import org.onebusaway.transit_data_federation.services.FederatedTransitDataBundle; import org.onebusaway.transit_data_federation.services.StopScheduleService; import org.onebusaway.transit_data_federation.services.StopTimeService; import org.onebusaway.transit_data_federation.services.StopTimeService.EFrequencyStopTimeBehavior; import org.onebusaway.transit_data_federation.services.otp.OTPConfigurationService; import org.onebusaway.transit_data_federation.services.transit_graph.StopEntry; import org.onebusaway.transit_data_federation.services.transit_graph.TransitGraphDao; import org.onebusaway.transit_data_federation.services.tripplanner.ItinerariesService; import org.onebusaway.transit_data_federation.services.tripplanner.StopTimeInstance; import org.onebusaway.utility.IOLibrary; import org.opentripplanner.routing.algorithm.GenericDijkstra; import org.opentripplanner.routing.algorithm.strategies.SkipTraverseResultStrategy; import org.opentripplanner.routing.core.Edge; import org.opentripplanner.routing.core.EdgeNarrative; import org.opentripplanner.routing.core.Graph; import org.opentripplanner.routing.core.State; import org.opentripplanner.routing.core.TraverseOptions; import org.opentripplanner.routing.core.Vertex; import org.opentripplanner.routing.pqueue.PriorityQueueImpl; import org.opentripplanner.routing.services.GraphService; import org.opentripplanner.routing.spt.GraphPath; import org.opentripplanner.routing.spt.MultiShortestPathTree; import org.opentripplanner.routing.spt.ShortestPathTree; import org.springframework.beans.factory.annotation.Autowired; public class TransferPatternsTask implements Runnable { private static StateDurationComparator _sptVertexDurationComparator = new StateDurationComparator(); private FederatedTransitDataBundle _bundle; private TransitGraphDao _transitGraphDao; private GraphService _graphService; private OTPConfigurationService _otpConfigurationService; private StopScheduleService _stopScheduleService; private StopTimeService _stopTimeService; private ItinerariesService _itinerariesService; private int _serviceDateCount = 4; private double _transferPatternFrequencyCutoff = 0.1; private double _transferPatternFrequencySlack = 0.5; private double _transferPatternWeightImprovement = 0.66; private int _maxPathCountForLocalStop = 3; private int _maxPathCountForHubStop = 5; private double _nearbyStopsRadius = 100; private boolean _useHubStopsAsSourceStops = false; private boolean _useAllStopsAsSourceStops = false; @Autowired public void setBundle(FederatedTransitDataBundle bundle) { _bundle = bundle; } @Autowired public void setTransitGraphDao(TransitGraphDao transitGraphDao) { _transitGraphDao = transitGraphDao; } @Autowired public void setGraphProvider(GraphService graphService) { _graphService = graphService; } @Autowired public void setOtpConfigurationService( OTPConfigurationService otpConfigurationService) { _otpConfigurationService = otpConfigurationService; } @Autowired public void setStopScheduleService(StopScheduleService stopScheduleService) { _stopScheduleService = stopScheduleService; } @Autowired public void setStopTimeService(StopTimeService stopTimeService) { _stopTimeService = stopTimeService; } @Autowired public void setItinerariesService(ItinerariesService itinerariesService) { _itinerariesService = itinerariesService; } public void setServiceDateCount(int serviceDateCount) { _serviceDateCount = serviceDateCount; } public void setTransferPatternFrequencyCutoff( double transferPatternFrequencyCutoff) { _transferPatternFrequencyCutoff = transferPatternFrequencyCutoff; } public void setTransferPatternFrequencySlack( double transferPatternFrequencySlack) { _transferPatternFrequencySlack = transferPatternFrequencySlack; } public void setMaxPathCountForLocalStop(int maxPathCountForLocalStop) { _maxPathCountForLocalStop = maxPathCountForLocalStop; } public void setMaxPathCountForHubStop(int maxPathCountForHubStop) { _maxPathCountForHubStop = maxPathCountForHubStop; } public void setUseHubStopsAsSourceStops(boolean useHubStopsAsSourceStops) { _useHubStopsAsSourceStops = useHubStopsAsSourceStops; } public void setUseAllStopsAsSourceStops(boolean useAllStopsAsSourceStops) { _useAllStopsAsSourceStops = useAllStopsAsSourceStops; } @Override public void run() { long tIn = System.currentTimeMillis(); List<StopEntry> hubStops = loadHubStops(); List<StopEntry> stops = loadSourceStops(hubStops); Set<StopEntry> hubStopsAsSet = new HashSet<StopEntry>(hubStops); Graph graph = _graphService.getGraph(); GraphContext context = _otpConfigurationService.createGraphContext(); Map<AgencyAndId, MutableTransferPattern> patternsByStopId = new HashMap<AgencyAndId, MutableTransferPattern>(); for (StopEntry stop : stops) { Map<StopEntry, Integer> nearbyStopsAndWalkTimes = getNearbyStopsAndWalkTimes(stop); boolean isHubStop = hubStopsAsSet.contains(stop); System.out.println("stop=" + stop.getId() + " hub=" + isHubStop); List<ServiceDate> serviceDates = computeServiceDates(stop); Map<StopEntry, Counter<List<Pair<StopEntry>>>> pathCountsByStop = new FactoryMap<StopEntry, Counter<List<Pair<StopEntry>>>>( new Counter<List<Pair<StopEntry>>>()); for (ServiceDate serviceDate : serviceDates) { System.out.println(" serviceDate=" + serviceDate); List<StopTimeInstance> instances = getStopTimeInstancesForStopAndServiceDate( stop, serviceDate); System.out.println(" instances=" + instances.size()); if (instances.isEmpty()) continue; StopTimeInstance first = instances.get(0); long tFrom = first.getDepartureTime(); Map<StopEntry, List<StopTimeInstance>> nearbyStopTimeInstances = getNearbyStopTimeInstances( nearbyStopsAndWalkTimes.keySet(), serviceDate); OBATraverseOptions options = _otpConfigurationService.createTraverseOptions(); options.maxComputationTime = -1; options.waitAtBeginningFactor = 1.0; options.extraSpecialMode = true; if (isHubStop) options.maxTransfers = Integer.MAX_VALUE; else options.maxTransfers = 2; GenericDijkstra dijkstra = new GenericDijkstra(graph, options); dijkstra.setSkipTraverseResultStrategy(new SkipVertexImpl(stop, tFrom)); dijkstra.setShortestPathTreeFactory(MultiShortestPathTree.FACTORY); dijkstra.setPriorityQueueFactory(PriorityQueueImpl.FACTORY); TPOfflineOriginVertex origin = new TPOfflineOriginVertex(context, stop, instances, nearbyStopsAndWalkTimes, nearbyStopTimeInstances); State state = new OBAState(tFrom, origin, options); MultiShortestPathTree spt = (MultiShortestPathTree) dijkstra.getShortestPathTree(state); processTree(spt, stop, pathCountsByStop); } MutableTransferPattern pattern = new MutableTransferPattern(stop); System.out.println("arrivalStops=" + pathCountsByStop.size()); for (Map.Entry<StopEntry, Counter<List<Pair<StopEntry>>>> entry : pathCountsByStop.entrySet()) { boolean verbose = false;// entry.getKey().getId().toString().equals("1_29430"); Counter<List<Pair<StopEntry>>> pathCounts = entry.getValue(); List<List<Pair<StopEntry>>> keys = pathCounts.getSortedKeys(); int maxCount = isHubStop ? _maxPathCountForHubStop : _maxPathCountForLocalStop; if (verbose) { for (List<Pair<StopEntry>> path : keys) System.out.println(pathCounts.getCount(path) + "\t" + path); } while (keys.size() > maxCount) keys.remove(0); for (List<Pair<StopEntry>> path : keys) pattern.addPath(path); } avgPaths(pattern, stop); patternsByStopId.put(stop.getId(), pattern); } writeData(patternsByStopId); long tOut = System.currentTimeMillis(); int duration = (int) ((tOut - tIn) / 1000); System.out.println("duration=" + duration); } private void writeData( Map<AgencyAndId, MutableTransferPattern> patternsByStopId) { File path = _bundle.getTransferPatternsPath(); try { PrintWriter out = new PrintWriter(IOLibrary.getFileAsWriter(path)); long index = 0; for (MutableTransferPattern pattern : patternsByStopId.values()) { index = pattern.writeTransferPatternsToPrintWriter(out, index); } out.close(); } catch (Throwable ex) { throw new IllegalStateException("error writing output to " + path, ex); } } private void avgPaths(MutableTransferPattern pattern, StopEntry origin) { /* * DoubleArrayList values = new DoubleArrayList(); for (StopEntry stop : * pattern.getStops()) { TransferParent root = new TransferParent(); * pattern.getTransfersForStop(stop, root); values.add(root.size()); } * * if (values.isEmpty()) return; * * values.sort(); * * System.out.println(" mu=" + Descriptive.mean(values)); * System.out.println("median=" + Descriptive.median(values)); * System.out.println(" max=" + Descriptive.max(values)); */ } private List<StopEntry> loadSourceStops(List<StopEntry> hubStops) { List<StopEntry> stops = new ArrayList<StopEntry>(); String key = _bundle.getKey(); String totalTaskCountValue = System.getProperty("totalTaskCount"); if (key != null && totalTaskCountValue != null) { int taskIndex = Integer.parseInt(key); int totalTaskCount = Integer.parseInt(totalTaskCountValue); List<StopEntry> allStops = _transitGraphDao.getAllStops(); if (_useHubStopsAsSourceStops) allStops = hubStops; double stopsPerTask = (double) allStops.size() / totalTaskCount; int from = (int) (taskIndex * stopsPerTask); int to = (int) ((taskIndex + 1) * stopsPerTask); for (int i = from; i < to; i++) { stops.add(allStops.get(i)); } return stops; } if (_useAllStopsAsSourceStops) return _transitGraphDao.getAllStops(); if (_useHubStopsAsSourceStops) return hubStops; File path = _bundle.getTransferPatternsSourceStopsPath(); try { BufferedReader reader = new BufferedReader(new FileReader(path)); String line = null; while ((line = reader.readLine()) != null) { AgencyAndId stopId = AgencyAndIdLibrary.convertFromString(line); StopEntry stop = _transitGraphDao.getStopEntryForId(stopId, true); stops.add(stop); } } catch (IOException ex) { throw new IllegalStateException("error reading hub stops", ex); } return stops; } private List<StopEntry> loadHubStops() { List<StopEntry> hubStops = new ArrayList<StopEntry>(); File path = _bundle.getHubStopsPath(false); if (path.exists()) { try { BufferedReader reader = new BufferedReader(new FileReader(path)); String line = null; while ((line = reader.readLine()) != null) { int index = line.indexOf('\t'); if (index != -1) line = line.substring(index + 1); AgencyAndId stopId = AgencyAndIdLibrary.convertFromString(line); StopEntry stop = _transitGraphDao.getStopEntryForId(stopId, true); hubStops.add(stop); } } catch (IOException ex) { throw new IllegalStateException("error loading HubStops file: " + path, ex); } } return hubStops; } private Map<StopEntry, Integer> getNearbyStopsAndWalkTimes(StopEntry stop) { CoordinateBounds bounds = SphericalGeometryLibrary.bounds( stop.getStopLocation(), _nearbyStopsRadius); Map<StopEntry, Integer> nearbyStopsAndWalkTimes = new HashMap<StopEntry, Integer>(); OBATraverseOptions opts = _otpConfigurationService.createTraverseOptions(); Date now = new Date(); List<StopEntry> stops = _transitGraphDao.getStopsByLocation(bounds); for (StopEntry nearbyStop : stops) { if (nearbyStop == stop) continue; GraphPath path = _itinerariesService.getWalkingItineraryBetweenStops( stop, nearbyStop, now, opts); if (path == null) continue; int duration = (int) (path.getDuration() / 1000); nearbyStopsAndWalkTimes.put(nearbyStop, duration); } return nearbyStopsAndWalkTimes; } private Map<StopEntry, List<StopTimeInstance>> getNearbyStopTimeInstances( Iterable<StopEntry> nearbyStops, ServiceDate serviceDate) { Map<StopEntry, List<StopTimeInstance>> nearbyStopTimeInstances = new HashMap<StopEntry, List<StopTimeInstance>>(); for (StopEntry nearbyStop : nearbyStops) { List<StopTimeInstance> nearbyInstances = getStopTimeInstancesForStopAndServiceDate( nearbyStop, serviceDate); nearbyStopTimeInstances.put(nearbyStop, nearbyInstances); } return nearbyStopTimeInstances; } private List<ServiceDate> computeServiceDates(StopEntry stop) { List<ServiceDateSummary> summaries = _stopScheduleService.getServiceDateSummariesForStop( stop.getId(), false); Collections.sort(summaries); List<ServiceDate> serviceDates = new ArrayList<ServiceDate>(); int floor = Math.max(0, summaries.size() - _serviceDateCount); for (int i = summaries.size() - 1; i >= floor; i--) { ServiceDateSummary summary = summaries.get(i); List<ServiceDate> dates = summary.getDates(); serviceDates.add(dates.get(dates.size() / 2)); } return serviceDates; } private List<StopTimeInstance> getStopTimeInstancesForStopAndServiceDate( StopEntry stop, ServiceDate serviceDate) { Range interval = _stopTimeService.getDepartureForStopAndServiceDate( stop.getId(), serviceDate); if (interval.isEmpty()) return Collections.emptyList(); long tFrom = (long) interval.getMin(); long tTo = (long) interval.getMax(); return _stopTimeService.getStopTimeInstancesInTimeRange(stop, new Date( tFrom), new Date(tTo), EFrequencyStopTimeBehavior.INCLUDE_INTERPOLATED); } private void processTree(MultiShortestPathTree spt, StopEntry originStop, Map<StopEntry, Counter<List<Pair<StopEntry>>>> pathCountsByStop) { Map<State, List<StopEntry>> parentsByState = new HashMap<State, List<StopEntry>>(); Map<State, StopEntry> actualOriginStops = new HashMap<State, StopEntry>(); Map<StopEntry, List<TPOfflineBlockArrivalVertex>> arrivalsByStop = new FactoryMap<StopEntry, List<TPOfflineBlockArrivalVertex>>( new ArrayList<TPOfflineBlockArrivalVertex>()); for (Vertex v : spt.getVeritces()) { if (!(v instanceof TPOfflineBlockArrivalVertex)) continue; TPOfflineBlockArrivalVertex bav = (TPOfflineBlockArrivalVertex) v; StopEntry stop = bav.getStop(); arrivalsByStop.get(stop).add(bav); } for (Map.Entry<StopEntry, List<TPOfflineBlockArrivalVertex>> entry : arrivalsByStop.entrySet()) { processArrivalsForStop(entry.getKey(), entry.getValue(), originStop, spt, actualOriginStops, parentsByState, pathCountsByStop); } } private void processArrivalsForStop(StopEntry arrivalStop, List<TPOfflineBlockArrivalVertex> arrivals, StopEntry originStop, MultiShortestPathTree spt, Map<State, StopEntry> actualOriginStops, Map<State, List<StopEntry>> parentsByState, Map<StopEntry, Counter<List<Pair<StopEntry>>>> pathCountsByStop) { Collections.sort(arrivals); Counter<List<Pair<StopEntry>>> pathCounts = new Counter<List<Pair<StopEntry>>>(); Map<List<Pair<StopEntry>>, List<State>> paths = new FactoryMap<List<Pair<StopEntry>>, List<State>>( new ArrayList<State>()); SortedMap<Long, List<State>> m = new TreeMap<Long, List<State>>(); m = FactoryMap.createSorted(m, new ArrayList<State>()); Map<State, List<Pair<StopEntry>>> pathsByVertex = new HashMap<State, List<Pair<StopEntry>>>(); boolean verbose = false;// arrivalStop.getId().toString().equals("1_29430"); if (verbose) System.out.println("here!"); long startTime = 0; int totalPathCount = 0; boolean atLeastOneArrivalWithProperOrigins = false; for (TPOfflineBlockArrivalVertex arrival : arrivals) { if (verbose) System.out.println(arrival); Collection<State> states = pruneSptVertices(spt.getStates(arrival)); for (State state : states) { if (verbose) System.out.println(" " + state); StopEntry actualOriginStop = getActualOriginStop(state, actualOriginStops); boolean properOrigins = originStop == actualOriginStop; if (verbose) System.out.println(" origins=" + properOrigins); boolean cut = state.getStartTime() <= startTime; if (!cut) { if (properOrigins) atLeastOneArrivalWithProperOrigins = true; startTime = state.getStartTime(); totalPathCount++; List<Pair<StopEntry>> path = constructTransferPattern(arrival, state, parentsByState); pathCounts.increment(path); paths.get(path).add(state); m.get(state.getTime()).add(state); pathsByVertex.put(state, path); if (verbose) System.out.println(" path=" + path); } } } if (!atLeastOneArrivalWithProperOrigins) return; List<Pair<StopEntry>> max = pathCounts.getMax(); int maxCount = pathCounts.getCount(max); List<List<Pair<StopEntry>>> toKeep = new ArrayList<List<Pair<StopEntry>>>(); for (List<Pair<StopEntry>> path : pathCounts.getSortedKeys()) { int count = pathCounts.getCount(path); if (count <= _transferPatternFrequencyCutoff * maxCount) { List<State> states = paths.get(path); boolean keep = false; for (State state : states) { long t = state.getTime(); State nextState = getNextVertex(m, pathsByVertex, t, path); if (nextState == null) { keep = true; break; } int duration = (int) (Math.abs(state.getTime() - state.getStartTime()) / 1000); double additional = ((nextState.getTime() - state.getTime()) / 1000); if (additional / duration > _transferPatternFrequencySlack) { keep = true; break; } } if (!keep) continue; } if (path.get(0).getFirst() == originStop) toKeep.add(path); } Counter<List<Pair<StopEntry>>> counts = pathCountsByStop.get(arrivalStop); for (List<Pair<StopEntry>> path : toKeep) { int count = pathCounts.getCount(path); counts.increment(path, count); if (verbose) System.out.println(count + "\t" + path); } } private Collection<State> pruneSptVertices(Collection<State> sptVerticesOrig) { if (sptVerticesOrig.size() == 1) return sptVerticesOrig; List<State> sptVertices = new ArrayList<State>(sptVerticesOrig); /** * This will put the spt vertices in order of increasing trip duration */ Collections.sort(sptVertices, _sptVertexDurationComparator); double bestRatio = Double.MAX_VALUE; for (int i = 0; i < sptVertices.size(); i++) { State state = sptVertices.get(i); double duration = getStateDuration(state); double weight = state.getWeight(); double ratio = weight / duration; if (ratio / bestRatio > _transferPatternWeightImprovement) { return sptVertices.subList(0, i); } else { bestRatio = ratio; } } return sptVertices; } private State getNextVertex(SortedMap<Long, List<State>> m, Map<State, List<Pair<StopEntry>>> pathsByVertex, long t, List<Pair<StopEntry>> currentPath) { SortedMap<Long, List<State>> after = m.tailMap(t + 1); for (List<State> sptVertices : after.values()) { for (State sptVertex : sptVertices) { List<Pair<StopEntry>> path = pathsByVertex.get(sptVertex); if (!path.equals(currentPath)) return sptVertex; } } return null; } private StopEntry getActualOriginStop(State state, Map<State, StopEntry> actualOriginStops) { StopEntry actual = actualOriginStops.get(state); if (actual == null) { Vertex v = state.getVertex(); if (v instanceof TPOfflineNearbyStopsVertex) { TPOfflineNearbyStopsVertex nsv = (TPOfflineNearbyStopsVertex) v; actual = nsv.getStop(); } else if (v instanceof TPOfflineOriginVertex) { TPOfflineOriginVertex originVertex = (TPOfflineOriginVertex) v; actual = originVertex.getStop(); } else { actual = getActualOriginStop(state.getBackState(), actualOriginStops); } actualOriginStops.put(state, actual); } return actual; } private List<Pair<StopEntry>> constructTransferPattern( TPOfflineBlockArrivalVertex arrival, State sptVertex, Map<State, List<StopEntry>> parentsByState) { List<StopEntry> path = computeParentsForState(sptVertex, parentsByState); path = new ArrayList<StopEntry>(path); path.add(arrival.getStop()); if (path.size() % 2 != 0) throw new IllegalArgumentException(); List<Pair<StopEntry>> pairs = new ArrayList<Pair<StopEntry>>(); for (int i = 0; i < path.size(); i += 2) pairs.add(Tuples.pair(path.get(i), path.get(i + 1))); return pairs; } private List<StopEntry> computeParentsForState(State state, Map<State, List<StopEntry>> parentsByState2) { List<StopEntry> parent = parentsByState2.get(state); if (parent != null) return parent; Vertex v = state.getVertex(); if (v instanceof TPOfflineNearbyStopsVertex) { TPOfflineNearbyStopsVertex nsv = (TPOfflineNearbyStopsVertex) v; return Arrays.asList(nsv.getStop()); } else if (v instanceof TPOfflineOriginVertex) { TPOfflineOriginVertex originVertex = (TPOfflineOriginVertex) v; return Arrays.asList(originVertex.getStop()); } Edge payload = state.getBackEdge(); EdgeNarrative narrative = state.getBackEdgeNarrative(); if (payload instanceof TPOfflineTransferEdge) { TPOfflineBlockArrivalVertex fromV = (TPOfflineBlockArrivalVertex) narrative.getFromVertex(); TPOfflineTransferVertex toV = (TPOfflineTransferVertex) narrative.getToVertex(); List<StopEntry> incomingPattern = computeParentsForState( state.getBackState(), parentsByState2); ArrayList<StopEntry> extendedPattern = new ArrayList<StopEntry>( incomingPattern); extendedPattern.add(fromV.getStop()); extendedPattern.add(toV.getStop()); parent = extendedPattern; } else { parent = computeParentsForState(state.getBackState(), parentsByState2); } parentsByState2.put(state, parent); return parent; } private static int getStateDuration(State state) { return (int) (Math.abs(state.getTime() - state.getStartTime()) / 1000); } private static class SkipVertexImpl implements SkipTraverseResultStrategy { private final Set<StopEntry> _stops = new HashSet<StopEntry>(); private final StopEntry _originStop; private final long _serviceDate; public SkipVertexImpl(StopEntry originStop, long serviceDate) { _originStop = originStop; _serviceDate = serviceDate; } @Override public boolean shouldSkipTraversalResult(Vertex origin, Vertex target, State parent, State current, ShortestPathTree spt, TraverseOptions traverseOptions) { EdgeNarrative narrative = current.getBackEdgeNarrative(); Vertex vertex = narrative.getToVertex(); /** * We prune any arrivals that loop back to the origin stop */ if (vertex instanceof TPOfflineBlockArrivalVertex) { TPOfflineBlockArrivalVertex bav = (TPOfflineBlockArrivalVertex) vertex; StopTimeInstance instance = bav.getInstance(); if (instance.getStop() == _originStop) return true; } /** * Skip a vertex that has moved on to the next service date */ if (vertex instanceof HasStopTimeInstanceTransitVertex) { HasStopTimeInstanceTransitVertex v = (HasStopTimeInstanceTransitVertex) vertex; StopTimeInstance instance = v.getInstance(); if (instance.getServiceDate() > _serviceDate + 12 * 60 * 60 * 1000) return true; } /** * Print the visited stop count as a show of progress */ if (vertex instanceof HasStopTransitVertex) { HasStopTransitVertex v = (HasStopTransitVertex) vertex; StopEntry stop = v.getStop(); if (_stops.add(stop) && _stops.size() % 100 == 0) { System.out.println("stops=" + _stops.size()); if (_stops.size() == 13900) System.out.println("here"); } } return false; } } private static class StateDurationComparator implements Comparator<State> { @Override public int compare(State o1, State o2) { int t1 = getStateDuration(o1); int t2 = getStateDuration(o2); if (t1 != t2) return (t1 < t2 ? -1 : 1); double w1 = o1.getWeight(); double w2 = o2.getWeight(); return Double.compare(w1, w2); } } }