Skip to content
This repository was archived by the owner on Mar 1, 2021. It is now read-only.

Commit 10117c7

Browse files
committed
Dedupe QueryResults by closest node (#91)
1 parent 746b1e7 commit 10117c7

File tree

4 files changed

+55
-19
lines changed

4 files changed

+55
-19
lines changed

CONTRIBUTORS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* michaz, very important hidden markov improvement via hmm-lib, see #49
66
* rory, support milisecond gpx timestamps, see #4
77
* stefanholder, Stefan Holder, BMW AG, creating and integrating the hmm-lib (#49, #66, #69) and
8-
penalizing inner-link U-turns (#70)
9-
* kodonnell, adding support for CH and other algorithms (#60) and penalizing inner-link U-turns (#70)
8+
penalizing inner-link U-turns (#88, #91)
9+
* kodonnell, adding support for CH and other algorithms (#60) and penalizing inner-link U-turns (#88)
1010

1111
For GraphHopper contributors see [here](https://github.com/graphhopper/graphhopper/blob/master/CONTRIBUTORS.md).

matching-core/src/main/java/com/graphhopper/matching/LocationIndexMatch.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,24 @@ public LocationIndexMatch(GraphHopperStorage graph, LocationIndexTree index) {
5252
this.index = index;
5353
}
5454

55-
public List<QueryResult> findNClosest(final double queryLat, final double queryLon, final EdgeFilter edgeFilter,
56-
double gpxAccuracyInMetern) {
55+
/**
56+
* Returns all edges that are within the specified radius around the queried position.
57+
* Searches at most 9 cells to avoid performance problems. Hence, if the radius is larger than
58+
* the cell width then not all edges might be returned.
59+
*
60+
* @param radius in meters
61+
*/
62+
public List<QueryResult> findNClosest(final double queryLat, final double queryLon,
63+
final EdgeFilter edgeFilter, double radius) {
5764
// Return ALL results which are very close and e.g. within the GPS signal accuracy.
5865
// Also important to get all edges if GPS point is close to a junction.
59-
final double returnAllResultsWithin = distCalc.calcNormalizedDist(gpxAccuracyInMetern);
66+
final double returnAllResultsWithin = distCalc.calcNormalizedDist(radius);
6067

6168
// implement a cheap priority queue via List, sublist and Collections.sort
6269
final List<QueryResult> queryResults = new ArrayList<QueryResult>();
6370
GHIntHashSet set = new GHIntHashSet();
6471

72+
// Doing 2 iterations means searching 9 tiles.
6573
for (int iteration = 0; iteration < 2; iteration++) {
6674
// should we use the return value of earlyFinish?
6775
index.findNetworkEntries(queryLat, queryLon, set, iteration);

matching-core/src/main/java/com/graphhopper/matching/MapMatching.java

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -183,18 +183,29 @@ public MatchResult doWork(List<GPXEntry> gpxList) {
183183
// now find each of the entries in the graph:
184184
final EdgeFilter edgeFilter = new DefaultEdgeFilter(algoOptions.getWeighting().getFlagEncoder());
185185

186-
List<List<QueryResult>> queriesPerEntry = lookupGPXEntries(filteredGPXEntries, edgeFilter);
186+
List<Collection<QueryResult>> queriesPerEntry =
187+
lookupGPXEntries(filteredGPXEntries, edgeFilter);
187188

188-
// now look up the entries up in the graph:
189+
// Add virtual nodes and edges to the graph so that candidates on edges can be represented
190+
// by virtual nodes.
189191
final QueryGraph queryGraph = new QueryGraph(routingGraph).setUseEdgeExplorerCache(true);
190192
List<QueryResult> allQueryResults = new ArrayList<>();
191-
for (List<QueryResult> qrs: queriesPerEntry)
193+
for (Collection<QueryResult> qrs: queriesPerEntry)
192194
allQueryResults.addAll(qrs);
193195
queryGraph.lookup(allQueryResults);
194196

197+
// Different QueryResults can have the same tower node as their closest node.
198+
// Hence, we now dedupe the query results of each GPX entry by their closest node (#91).
199+
// This must be done after calling queryGraph.lookup() since this replaces some of the
200+
// QueryResult nodes with virtual nodes. Virtual nodes are not deduped since there is at
201+
// most one QueryResult per edge and virtual nodes are inserted into the middle of an edge.
202+
// Reducing the number of QueryResults improves performance since less shortest/fastest
203+
// routes need to be computed.
204+
queriesPerEntry = dedupeQueryResultsByClosestNode(queriesPerEntry);
205+
195206
logger.debug("================= Query results =================");
196207
int i = 1;
197-
for (List<QueryResult> entries : queriesPerEntry) {
208+
for (Collection<QueryResult> entries : queriesPerEntry) {
198209
logger.debug("Query results for GPX entry {}", i++);
199210
for (QueryResult qr : entries) {
200211
logger.debug("Node id: {}, virtual: {}, snapped on: {}, pos: {},{}, "
@@ -269,26 +280,41 @@ private List<GPXEntry> filterGPXEntries(List<GPXEntry> gpxList) {
269280
}
270281

271282
/**
272-
* Find the possible locations of each qpxEntry in the graph.
283+
* Find the possible locations (edges) of each GPXEntry in the graph.
273284
*/
274-
private List<List<QueryResult>> lookupGPXEntries(List<GPXEntry> gpxList,
285+
private List<Collection<QueryResult>> lookupGPXEntries(List<GPXEntry> gpxList,
275286
EdgeFilter edgeFilter) {
276287

277-
List<List<QueryResult>> gpxEntryLocations = new ArrayList<>();
288+
final List<Collection<QueryResult>> gpxEntryLocations = new ArrayList<>();
278289
for (GPXEntry gpxEntry : gpxList) {
279-
gpxEntryLocations.add(locationIndex.findNClosest(gpxEntry.lat, gpxEntry.lon, edgeFilter,
280-
measurementErrorSigma));
290+
final List<QueryResult> queryResults = locationIndex.findNClosest(
291+
gpxEntry.lat, gpxEntry.lon, edgeFilter, measurementErrorSigma);
292+
gpxEntryLocations.add(queryResults);
281293
}
282294
return gpxEntryLocations;
283295
}
284296

297+
private List<Collection<QueryResult>> dedupeQueryResultsByClosestNode(
298+
List<Collection<QueryResult>> queriesPerEntry) {
299+
final List<Collection<QueryResult>> result = new ArrayList<>(queriesPerEntry.size());
300+
301+
for (Collection<QueryResult> queryResults : queriesPerEntry) {
302+
final Map<Integer, QueryResult> dedupedQueryResults = new HashMap<>();
303+
for (QueryResult qr : queryResults) {
304+
dedupedQueryResults.put(qr.getClosestNode(), qr);
305+
}
306+
result.add(dedupedQueryResults.values());
307+
}
308+
return result;
309+
}
310+
285311
/**
286312
* Creates TimeSteps with candidates for the GPX entries but does not create emission or
287313
* transition probabilities. Creates directed candidates for virtual nodes and undirected
288314
* candidates for real nodes.
289315
*/
290316
private List<TimeStep<GPXExtension, GPXEntry, Path>> createTimeSteps(
291-
List<GPXEntry> filteredGPXEntries, List<List<QueryResult>> queriesPerEntry,
317+
List<GPXEntry> filteredGPXEntries, List<Collection<QueryResult>> queriesPerEntry,
292318
QueryGraph queryGraph) {
293319
final int n = filteredGPXEntries.size();
294320
if (queriesPerEntry.size() != n) {
@@ -300,7 +326,7 @@ private List<TimeStep<GPXExtension, GPXEntry, Path>> createTimeSteps(
300326
for (int i = 0; i < n; i++) {
301327

302328
GPXEntry gpxEntry = filteredGPXEntries.get(i);
303-
List<QueryResult> queryResults = queriesPerEntry.get(i);
329+
final Collection<QueryResult> queryResults = queriesPerEntry.get(i);
304330

305331
List<GPXExtension> candidates = new ArrayList<>();
306332
for (QueryResult qr: queryResults) {
@@ -512,7 +538,7 @@ private double penalizedPathDistance(Path path,
512538

513539
private MatchResult computeMatchResult(List<SequenceState<GPXExtension, GPXEntry, Path>> seq,
514540
List<GPXEntry> gpxList,
515-
List<List<QueryResult>> queriesPerEntry,
541+
List<Collection<QueryResult>> queriesPerEntry,
516542
EdgeExplorer explorer) {
517543
final Map<String, EdgeIteratorState> virtualEdgesMap = createVirtualEdgesMap(
518544
queriesPerEntry, explorer);
@@ -611,10 +637,10 @@ private boolean isVirtualNode(int node) {
611637
* Returns a map where every virtual edge maps to its real edge with correct orientation.
612638
*/
613639
private Map<String, EdgeIteratorState> createVirtualEdgesMap(
614-
List<List<QueryResult>> queriesPerEntry, EdgeExplorer explorer) {
640+
List<Collection<QueryResult>> queriesPerEntry, EdgeExplorer explorer) {
615641
// TODO For map key, use the traversal key instead of string!
616642
Map<String, EdgeIteratorState> virtualEdgesMap = new HashMap<>();
617-
for (List<QueryResult> queryResults: queriesPerEntry) {
643+
for (Collection<QueryResult> queryResults: queriesPerEntry) {
618644
for (QueryResult qr: queryResults) {
619645
if (isVirtualNode(qr.getClosestNode())) {
620646
EdgeIterator iter = explorer.setBaseNode(qr.getClosestNode());

matching-core/src/main/java/com/graphhopper/matching/MapMatchingMain.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ private void start(CmdArgs args) {
8686
algorithm(Parameters.Algorithms.DIJKSTRA_BI).traversalMode(hopper.getTraversalMode()).
8787
weighting(new FastestWeighting(firstEncoder)).
8888
maxVisitedNodes(args.getInt("max_visited_nodes", 1000)).
89+
// Penalizing inner-link U-turns only works with fastest weighting, since
90+
// shortest weighting does not apply penalties to unfavored virtual edges.
8991
hints(new HintsMap().put("weighting", "fastest").put("vehicle", firstEncoder.toString())).
9092
build();
9193
MapMatching mapMatching = new MapMatching(hopper, opts);

0 commit comments

Comments
 (0)