Skip to content

Commit

Permalink
Real-time stop-skipping. (Includes the ability to cancel lines.) #923
Browse files Browse the repository at this point in the history
  • Loading branch information
michaz committed Apr 10, 2017
1 parent a83f297 commit fbaa9e8
Show file tree
Hide file tree
Showing 11 changed files with 299 additions and 64 deletions.
File renamed without changes.
@@ -1,5 +1,7 @@
package com.graphhopper.reader.gtfs;

import com.graphhopper.routing.util.DefaultEdgeFilter;
import com.graphhopper.storage.Graph;
import com.graphhopper.util.EdgeExplorer;
import com.graphhopper.util.EdgeIterator;
import com.graphhopper.util.EdgeIteratorState;
Expand All @@ -11,12 +13,14 @@ final class GraphExplorer {
private final EdgeExplorer edgeExplorer;
private final PtFlagEncoder flagEncoder;
private final GtfsStorage gtfsStorage;
private final RealtimeFeed realtimeFeed;
private final boolean reverse;

GraphExplorer(EdgeExplorer edgeExplorer, PtFlagEncoder flagEncoder, GtfsStorage gtfsStorage, boolean reverse) {
this.edgeExplorer = edgeExplorer;
GraphExplorer(Graph graph, PtFlagEncoder flagEncoder, GtfsStorage gtfsStorage, RealtimeFeed realtimeFeed, boolean reverse) {
this.edgeExplorer = graph.createEdgeExplorer(new DefaultEdgeFilter(flagEncoder, reverse, !reverse));
this.flagEncoder = flagEncoder;
this.gtfsStorage = gtfsStorage;
this.realtimeFeed = realtimeFeed;
this.reverse = reverse;
}

Expand All @@ -32,11 +36,14 @@ public Iterator<EdgeIteratorState> iterator() {
@Override
public boolean hasNext() {
while(edgeIterator.next()) {
GtfsStorage.EdgeType edgeType = flagEncoder.getEdgeType(edgeIterator.getFlags());
int trafficDay = (int) (label.currentTime / (24 * 60 * 60));
final GtfsStorage.EdgeType edgeType = flagEncoder.getEdgeType(edgeIterator.getFlags());
final int trafficDay = (int) (label.currentTime / (24 * 60 * 60));
if (!isValidOn(edgeIterator, trafficDay)) {
continue;
}
if (realtimeFeed.isBlocked(edgeIterator.getEdge())) {
continue;
}
if (edgeType == GtfsStorage.EdgeType.ENTER_TIME_EXPANDED_NETWORK && !reverse) {
if ((int) (label.currentTime) % (24 * 60 * 60) > flagEncoder.getTime(edgeIterator.getFlags())) {
continue;
Expand Down Expand Up @@ -67,7 +74,7 @@ public EdgeIteratorState next() {
}

private boolean isValidOn(EdgeIteratorState edge, int trafficDay) {
return gtfsStorage.getValidities().get((int) flagEncoder.getValidityId(edge.getFlags())).get(trafficDay);
return gtfsStorage.getValidities().get(flagEncoder.getValidityId(edge.getFlags())).get(trafficDay);
}

}
Expand Up @@ -3,13 +3,13 @@
import com.conveyal.gtfs.GTFSFeed;
import com.conveyal.gtfs.model.Stop;
import com.conveyal.gtfs.model.StopTime;
import com.google.transit.realtime.GtfsRealtime;
import com.graphhopper.*;
import com.graphhopper.Trip;
import com.graphhopper.gtfs.fare.*;
import com.graphhopper.reader.osm.OSMReader;
import com.graphhopper.routing.InstructionsFromEdges;
import com.graphhopper.routing.QueryGraph;
import com.graphhopper.routing.util.DefaultEdgeFilter;
import com.graphhopper.routing.util.EdgeFilter;
import com.graphhopper.routing.util.EncodingManager;
import com.graphhopper.routing.weighting.Weighting;
Expand Down Expand Up @@ -40,6 +40,34 @@

public final class GraphHopperGtfs implements GraphHopperAPI {

public static class Factory {
private final TranslationMap translationMap;
private final EncodingManager encodingManager;
private final GraphHopperStorage graphHopperStorage;
private final LocationIndex locationIndex;
private final GtfsStorage gtfsStorage;

private Factory(EncodingManager encodingManager, TranslationMap translationMap, GraphHopperStorage graphHopperStorage, LocationIndex locationIndex, GtfsStorage gtfsStorage) {
this.encodingManager = encodingManager;
this.translationMap = translationMap;
this.graphHopperStorage = graphHopperStorage;
this.locationIndex = locationIndex;
this.gtfsStorage = gtfsStorage;
}

public GraphHopperGtfs createWith(GtfsRealtime.FeedMessage realtimeFeed) {
return new GraphHopperGtfs(encodingManager, translationMap, graphHopperStorage, locationIndex, gtfsStorage, RealtimeFeed.fromProtobuf(gtfsStorage, realtimeFeed));
}

public GraphHopperGtfs createWithoutRealtimeFeed() {
return new GraphHopperGtfs(encodingManager, translationMap, graphHopperStorage, locationIndex, gtfsStorage, RealtimeFeed.empty());
}
}

public static Factory createFactory(EncodingManager encodingManager, TranslationMap translationMap, GraphHopperStorage graphHopperStorage, LocationIndex locationIndex, GtfsStorage gtfsStorage) {
return new Factory(encodingManager, translationMap, graphHopperStorage, locationIndex, gtfsStorage);
}

public static final String EARLIEST_DEPARTURE_TIME_HINT = "earliestDepartureTime";
public static final String RANGE_QUERY_END_TIME = "rangeQueryEndTime";
public static final String ARRIVE_BY = "arriveBy";
Expand All @@ -50,51 +78,28 @@ public final class GraphHopperGtfs implements GraphHopperAPI {

private final TranslationMap translationMap;
private final EncodingManager encodingManager;
private final GraphHopperStorage graphHopperStorage;
private final LocationIndex locationIndex;
private final GtfsStorage gtfsStorage;
private final RealtimeFeed realtimeFeed;

private GraphHopperStorage graphHopperStorage;
private LocationIndex locationIndex;
private GtfsStorage gtfsStorage;

public GraphHopperGtfs(EncodingManager encodingManager, TranslationMap translationMap, GraphHopperStorage graphHopperStorage, LocationIndex locationIndex, GtfsStorage gtfsStorage) {
public GraphHopperGtfs(EncodingManager encodingManager, TranslationMap translationMap, GraphHopperStorage graphHopperStorage, LocationIndex locationIndex, GtfsStorage gtfsStorage, RealtimeFeed realtimeFeed) {
this.encodingManager = encodingManager;
this.translationMap = translationMap;
this.graphHopperStorage = graphHopperStorage;
this.locationIndex = locationIndex;
this.gtfsStorage = gtfsStorage;
this.realtimeFeed = realtimeFeed;
}

public static GraphHopperGtfs createGraphHopperGtfs(String graphHopperFolder, String gtfsFile, boolean createWalkNetwork) {
public static GraphHopperGtfs create(String graphHopperFolder, String gtfsFile, boolean createWalkNetwork) {
EncodingManager encodingManager = createEncodingManager();

if (Helper.isEmpty(graphHopperFolder))
throw new IllegalStateException("GraphHopperLocation is not specified. Call setGraphHopperLocation or init before");

if (graphHopperFolder.endsWith("-gh")) {
// do nothing
} else if (graphHopperFolder.endsWith(".osm") || graphHopperFolder.endsWith(".xml")) {
throw new IllegalArgumentException("GraphHopperLocation cannot be the OSM file. Instead you need to use importOrLoad");
} else if (!graphHopperFolder.contains(".")) {
if (new File(graphHopperFolder + "-gh").exists())
graphHopperFolder += "-gh";
} else {
File compressed = new File(graphHopperFolder + ".ghz");
if (compressed.exists() && !compressed.isDirectory()) {
try {
new Unzipper().unzip(compressed.getAbsolutePath(), graphHopperFolder, false);
} catch (IOException ex) {
throw new RuntimeException("Couldn't extract file " + compressed.getAbsolutePath()
+ " to " + graphHopperFolder, ex);
}
}
}

GtfsStorage gtfsStorage = createGtfsStorage();

GHDirectory directory = createGHDirectory(graphHopperFolder);
GraphHopperStorage graphHopperStorage = createOrLoad(directory, encodingManager, gtfsStorage, createWalkNetwork, Collections.singleton(gtfsFile), Collections.emptyList());
LocationIndex locationIndex = createOrLoadIndex(directory, graphHopperStorage);

return new GraphHopperGtfs(encodingManager, createTranslationMap(), graphHopperStorage, locationIndex, gtfsStorage);
return createFactory(encodingManager, createTranslationMap(), graphHopperStorage, locationIndex, gtfsStorage)
.createWithoutRealtimeFeed();
}

public static GtfsStorage createGtfsStorage() {
Expand All @@ -119,7 +124,6 @@ public static GraphHopperStorage createOrLoad(GHDirectory directory, EncodingMan
graphHopperStorage.create(1000);
for (String osmFile : osmFiles) {
OSMReader osmReader = new OSMReader(graphHopperStorage);
// osmReader.setEncodingManager(encodingManager);
osmReader.setFile(new File(osmFile));
osmReader.setDontCreateStorage(true);
try {
Expand Down Expand Up @@ -244,19 +248,9 @@ public GHResponse route(GHRequest request) {

PtTravelTimeWeighting weighting = createPtTravelTimeWeighting(encoder, arriveBy, ignoreTransfers, walkSpeedKmH);

GraphExplorer explorer;
if (arriveBy) {
explorer = new GraphExplorer(queryGraph.createEdgeExplorer(new DefaultEdgeFilter(encoder, true, false)), encoder, gtfsStorage, true);
} else {
explorer = new GraphExplorer(queryGraph.createEdgeExplorer(new DefaultEdgeFilter(encoder, false, true)), encoder, gtfsStorage, false);
}
GraphExplorer graphExplorer = new GraphExplorer(queryGraph, encoder, gtfsStorage, realtimeFeed, arriveBy);

MultiCriteriaLabelSetting router;
if (arriveBy) {
router = new MultiCriteriaLabelSetting(queryGraph, weighting, maxVisitedNodesForRequest, explorer, true, maxWalkDistancePerLeg, maxTransferDistancePerLeg, !ignoreTransfers);
} else {
router = new MultiCriteriaLabelSetting(queryGraph, weighting, maxVisitedNodesForRequest, explorer, false, maxWalkDistancePerLeg, maxTransferDistancePerLeg, !ignoreTransfers);
}
MultiCriteriaLabelSetting router = new MultiCriteriaLabelSetting(graphExplorer, weighting, arriveBy, maxWalkDistancePerLeg, maxTransferDistancePerLeg, !ignoreTransfers, maxVisitedNodesForRequest);

String debug = ", algoInit:" + stopWatch.stop().getSeconds() + "s";

Expand Down
@@ -1,9 +1,11 @@
package com.graphhopper.reader.gtfs;

import com.carrotsearch.hppc.IntArrayList;
import com.conveyal.gtfs.GTFSFeed;
import com.conveyal.gtfs.model.*;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.SetMultimap;
import com.google.transit.realtime.GtfsRealtime;
import com.graphhopper.routing.util.EdgeFilter;
import com.graphhopper.storage.GraphHopperStorage;
import com.graphhopper.storage.NodeAccess;
Expand Down Expand Up @@ -70,7 +72,6 @@ private TimelineNodeIdWithTripId(int timelineNodeId, String tripId) {
private final SetMultimap<String, TimelineNodeIdWithTripId> departureTimelineNodes = HashMultimap.create();
private final SetMultimap<String, TimelineNodeIdWithTripId> arrivalTimelineNodes = HashMultimap.create();
private Collection<EnterAndExitNodeIdWithStopId> stopEnterAndExitNodes = new ArrayList<>();
private final SetMultimap<String, Integer> arrivals = HashMultimap.create();
private final PtFlagEncoder encoder;

GtfsReader(String id, GraphHopperStorage ghStorage, LocationIndex walkNetworkIndex) {
Expand Down Expand Up @@ -142,6 +143,8 @@ private void buildPtNetwork() {
for (Frequency frequency : (frequencies.isEmpty() ? Collections.singletonList(SINGLE_FREQUENCY) : frequencies)) {
for (int time = frequency.start_time; time < frequency.end_time; time += frequency.headway_secs) {
List<Integer> arrivalNodes = new ArrayList<>();
IntArrayList boardEdges = new IntArrayList();
IntArrayList alightEdges = new IntArrayList();
for (Trip trip : trips) {
Service service = feed.services.get(trip.service_id);
BitSet validOnDay = new BitSet((int) DAYS.between(startDate, endDate));
Expand All @@ -159,7 +162,6 @@ private void buildPtNetwork() {
nodeAccess.setNode(arrivalNode, stop.stop_lat, stop.stop_lon);
nodeAccess.setAdditionalNodeField(arrivalNode, NodeType.INTERNAL_PT.ordinal());
times.put(arrivalNode, stopTime.arrival_time + time);
arrivals.put(stopTime.stop_id, arrivalNode);
if (prev != null) {
Stop fromStop = feed.stops.get(prev.stop_id);
double distance = distCalc.calcDist(
Expand Down Expand Up @@ -208,6 +210,7 @@ private void buildPtNetwork() {
false);
boardEdge.setName(getRouteName(feed, trip));
setEdgeType(boardEdge, GtfsStorage.EdgeType.BOARD);
boardEdges.add(boardEdge.getEdge());
gtfsStorage.getStopSequences().put(boardEdge.getEdge(), stopTime.stop_sequence);
gtfsStorage.getExtraStrings().put(boardEdge.getEdge(), trip.trip_id);
boardEdge.setFlags(encoder.setValidityId(boardEdge.getFlags(), validityId));
Expand All @@ -220,6 +223,7 @@ private void buildPtNetwork() {
false);
alightEdge.setName(getRouteName(feed, trip));
setEdgeType(alightEdge, GtfsStorage.EdgeType.ALIGHT);
alightEdges.add(alightEdge.getEdge());
gtfsStorage.getStopSequences().put(alightEdge.getEdge(), stopTime.stop_sequence);
gtfsStorage.getExtraStrings().put(alightEdge.getEdge(), trip.trip_id);
alightEdge.setFlags(encoder.setValidityId(alightEdge.getFlags(), validityId));
Expand All @@ -239,6 +243,9 @@ private void buildPtNetwork() {
}
prev = stopTime;
}
final GtfsRealtime.TripDescriptor tripDescriptor = GtfsRealtime.TripDescriptor.newBuilder().setTripId(trip.trip_id).setStartTime(Entity.Writer.convertToGtfsTime(time)).build();
gtfsStorage.getBoardEdgesForTrip().put(tripDescriptor, boardEdges.toArray());
gtfsStorage.getAlightEdgesForTrip().put(tripDescriptor, alightEdges.toArray());
arrivalNodes.add(arrivalNode);
}
}
Expand Down Expand Up @@ -360,12 +367,12 @@ private Iterable<StopTime> getInterpolatedStopTimesForTrip(String trip_id) {
}

private void insertInboundTransfers(String fromStopId, int minimumTransferTime, SortedSet<Fun.Tuple2<Integer, Integer>> toStopTimelineNode) {
for (Integer arrivalNodeId : arrivals.get(fromStopId)) {
int arrivalTime = times.get(arrivalNodeId);
for (TimelineNodeIdWithTripId entry : arrivalTimelineNodes.get(fromStopId)) {
int arrivalTime = times.get(entry.timelineNodeId);
SortedSet<Fun.Tuple2<Integer, Integer>> tailSet = toStopTimelineNode.tailSet(new Fun.Tuple2<>(arrivalTime + minimumTransferTime, -1));
if (!tailSet.isEmpty()) {
Fun.Tuple2<Integer, Integer> e = tailSet.first();
EdgeIteratorState edge = graph.edge(arrivalNodeId, e.b, 0.0, false);
EdgeIteratorState edge = graph.edge(entry.timelineNodeId, e.b, 0.0, false);
setEdgeType(edge, GtfsStorage.EdgeType.TRANSFER);
edge.setFlags(encoder.setTime(edge.getFlags(), e.a-arrivalTime));
}
Expand Down
Expand Up @@ -2,12 +2,14 @@

import com.conveyal.gtfs.GTFSFeed;
import com.conveyal.gtfs.model.Fare;
import com.google.transit.realtime.GtfsRealtime;
import com.graphhopper.storage.Directory;
import com.graphhopper.storage.Graph;
import com.graphhopper.storage.GraphExtension;
import org.mapdb.*;

import java.io.*;
import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.ExecutionException;
Expand Down Expand Up @@ -42,6 +44,8 @@ static long traveltimeReverse(int edgeTimeValue, long latestExitTime) {
private Map<Integer, String> extra;
private Map<Integer, Integer> stopSequences;
private Map<String, Fare> fares;
private Map<GtfsRealtime.TripDescriptor, int[]> boardEdgesForTrip;
private Map<GtfsRealtime.TripDescriptor, int[]> leaveEdgesForTrip;

enum EdgeType {
HIGHWAY, ENTER_TIME_EXPANDED_NETWORK, LEAVE_TIME_EXPANDED_NETWORK, ENTER_PT, EXIT_PT, HOP, DWELL, BOARD, ALIGHT, OVERNIGHT, TRANSFER, WAIT
Expand Down Expand Up @@ -120,6 +124,8 @@ private void init() {
this.extra = data.getTreeMap("extra");
this.stopSequences = data.getTreeMap("stopSequences");
this.fares = data.getTreeMap("fares");
this.boardEdgesForTrip = data.getHashMap("boardEdgesForTrip");
this.leaveEdgesForTrip = data.getHashMap("leaveEdgesForTrip");
}

void loadGtfsFromFile(String id, ZipFile zip) {
Expand Down Expand Up @@ -180,6 +186,14 @@ Map<Integer, Integer> getStopSequences() {
return stopSequences;
}

Map<GtfsRealtime.TripDescriptor, int[]> getBoardEdgesForTrip() {
return boardEdgesForTrip;
}

Map<GtfsRealtime.TripDescriptor, int[]> getAlightEdgesForTrip() {
return leaveEdgesForTrip;
}

Map<String, Fare> getFares() {
return fares;
}
Expand Down
Expand Up @@ -49,7 +49,7 @@ class MultiCriteriaLabelSetting {
private int visitedNodes;
private final GraphExplorer explorer;

MultiCriteriaLabelSetting(Graph graph, Weighting weighting, int maxVisitedNodes, GraphExplorer explorer, boolean reverse, double maxWalkDistancePerLeg, double maxTransferDistancePerLeg, boolean mindTransfers) {
MultiCriteriaLabelSetting(GraphExplorer explorer, Weighting weighting, boolean reverse, double maxWalkDistancePerLeg, double maxTransferDistancePerLeg, boolean mindTransfers, int maxVisitedNodes) {
this.weighting = (PtTravelTimeWeighting) weighting;
this.flagEncoder = (PtFlagEncoder) weighting.getFlagEncoder();
this.maxVisitedNodes = maxVisitedNodes;
Expand All @@ -58,8 +58,7 @@ class MultiCriteriaLabelSetting {
this.maxWalkDistancePerLeg = maxWalkDistancePerLeg;
this.maxTransferDistancePerLeg = maxTransferDistancePerLeg;
this.mindTransfers = mindTransfers;
int size = Math.min(Math.max(200, graph.getNodes() / 10), 2000);
fromHeap = new PriorityQueue<>(size, new Comparator<Label>() {
fromHeap = new PriorityQueue<>(new Comparator<Label>() {
@Override
public int compare(Label o1, Label o) {
return Long.compare(queueCriterion(o1), queueCriterion(o));
Expand Down

0 comments on commit fbaa9e8

Please sign in to comment.