This repository has been archived by the owner on Mar 1, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 273
Closed
Changes from 2 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
31e1d6d
WIP: playing round with directed candidates for #70
kodonnell 070230a
crlf === dum
kodonnell 68e68ec
d'oh virtual edges are directed ...
9354680
Merge branch 'master' into issue70
9d07f33
what about directed virtual edge quadruples?
kodonnell 0f3fafc
tab to space
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ | |
*/ | ||
package com.graphhopper.matching; | ||
|
||
import com.graphhopper.routing.VirtualEdgeIteratorState; | ||
import com.graphhopper.GraphHopper; | ||
import com.graphhopper.matching.util.HmmProbabilities; | ||
import com.graphhopper.matching.util.TimeStep; | ||
|
@@ -161,70 +162,133 @@ public MatchResult doWork(List<GPXEntry> gpxList) { | |
+ gpxList.size() + "). Correct format?"); | ||
} | ||
|
||
final EdgeFilter edgeFilter = new DefaultEdgeFilter(algoOptions.getWeighting().getFlagEncoder()); | ||
|
||
// Compute all candidates first. | ||
// TODO: Generate candidates on-the-fly within computeViterbiSequence() if this does not | ||
// degrade performance. | ||
final List<QueryResult> allCandidates = new ArrayList<>(); | ||
List<TimeStep<GPXExtension, GPXEntry, Path>> timeSteps = createTimeSteps(gpxList, | ||
edgeFilter, allCandidates); | ||
|
||
if (allCandidates.size() < 2) { | ||
throw new IllegalArgumentException("Too few matching coordinates (" | ||
+ allCandidates.size() + "). Wrong region imported?"); | ||
// filter the entries: | ||
List<GPXEntry> filteredGPXEntries = filterGPXEntries(gpxList); | ||
if (filteredGPXEntries.size() < 2) { | ||
throw new IllegalStateException("Only " + filteredGPXEntries.size() + " filtered GPX entries (from " + gpxList.size() + "), but two or more are needed"); | ||
} | ||
if (timeSteps.size() < 2) { | ||
throw new IllegalStateException("Coordinates produced too few time steps " | ||
+ timeSteps.size() + ", gpxList:" + gpxList.size()); | ||
} | ||
|
||
|
||
// now find each of the entries in the graph: | ||
final EdgeFilter edgeFilter = new DefaultEdgeFilter(algoOptions.getWeighting().getFlagEncoder()); | ||
List<List<QueryResult>> queriesPerEntry = findGPXEntriesInGraph(filteredGPXEntries, edgeFilter); | ||
|
||
// now look up the entries up in the graph: | ||
final QueryGraph queryGraph = new QueryGraph(routingGraph).setUseEdgeExplorerCache(true); | ||
queryGraph.lookup(allCandidates); | ||
List<QueryResult> allQueryResults = new ArrayList<QueryResult>(); | ||
for (List<QueryResult> qrs: queriesPerEntry) | ||
allQueryResults.addAll(qrs); | ||
queryGraph.lookup(allQueryResults); | ||
|
||
// create candidates from the entries in the graph (a candidate is basically an entry + direction): | ||
List<TimeStep<GPXExtension, GPXEntry, Path>> timeSteps = createTimeSteps(filteredGPXEntries, queriesPerEntry, queryGraph); | ||
|
||
List<SequenceState<GPXExtension, GPXEntry, Path>> seq = computeViterbiSequence(timeSteps, | ||
gpxList, queryGraph); | ||
// viterbify: | ||
List<SequenceState<GPXExtension, GPXEntry, Path>> seq = computeViterbiSequence(timeSteps, gpxList, queryGraph); | ||
|
||
// finally, extract the result: | ||
final EdgeExplorer explorer = queryGraph.createEdgeExplorer(edgeFilter); | ||
MatchResult matchResult = computeMatchResult(seq, gpxList, allCandidates, explorer); | ||
MatchResult matchResult = computeMatchResult(seq, filteredGPXEntries, queriesPerEntry, explorer); | ||
|
||
return matchResult; | ||
} | ||
|
||
|
||
/** | ||
* Filters GPX entries to only those which will be used for map matching (i.e. those which | ||
* are separated by at least 2 * measurementErrorSigman | ||
*/ | ||
private List<GPXEntry> filterGPXEntries(List<GPXEntry> gpxList) { | ||
List<GPXEntry> filtered = new ArrayList<GPXEntry>(); | ||
GPXEntry prevEntry = null; | ||
int last = gpxList.size() - 1; | ||
for (int i = 0; i <= last; i++) { | ||
GPXEntry gpxEntry = gpxList.get(i); | ||
if (i == 0 || i == last || distanceCalc.calcDist( | ||
prevEntry.getLat(), prevEntry.getLon(), | ||
gpxEntry.getLat(), gpxEntry.getLon()) > 2 * measurementErrorSigma) { | ||
filtered.add(gpxEntry); | ||
prevEntry = gpxEntry; | ||
} | ||
} | ||
return filtered; | ||
} | ||
/** | ||
* Find the possible locations of each qpxEntry in the graph. | ||
*/ | ||
private List<List<QueryResult>> findGPXEntriesInGraph(List<GPXEntry> gpxList, EdgeFilter edgeFilter) { | ||
|
||
List<List<QueryResult>> gpxEntryLocations = new ArrayList<List<QueryResult>>(); | ||
for (GPXEntry gpxEntry : gpxList) { | ||
gpxEntryLocations.add(locationIndex.findNClosest(gpxEntry.lat, gpxEntry.lon, edgeFilter, measurementErrorSigma)); | ||
} | ||
return gpxEntryLocations; | ||
} | ||
|
||
|
||
/** | ||
* Creates TimeSteps for the GPX entries but does not create emission or | ||
* transition probabilities. | ||
* | ||
* @param outAllCandidates output parameter for all candidates, must be an | ||
* empty list. | ||
*/ | ||
private List<TimeStep<GPXExtension, GPXEntry, Path>> createTimeSteps(List<GPXEntry> gpxList, | ||
EdgeFilter edgeFilter, List<QueryResult> outAllCandidates) { | ||
int indexGPX = 0; | ||
TimeStep<GPXExtension, GPXEntry, Path> prevTimeStep = null; | ||
private List<TimeStep<GPXExtension, GPXEntry, Path>> createTimeSteps(List<GPXEntry> filteredGPXEntries, | ||
List<List<QueryResult>> queriesPerEntry, QueryGraph queryGraph) { | ||
|
||
final List<TimeStep<GPXExtension, GPXEntry, Path>> timeSteps = new ArrayList<>(); | ||
|
||
for (GPXEntry gpxEntry : gpxList) { | ||
if (prevTimeStep == null | ||
|| distanceCalc.calcDist( | ||
prevTimeStep.observation.getLat(), prevTimeStep.observation.getLon(), | ||
gpxEntry.getLat(), gpxEntry.getLon()) > 2 * measurementErrorSigma | ||
// always include last point | ||
|| indexGPX == gpxList.size() - 1) { | ||
final List<QueryResult> queryResults = locationIndex.findNClosest( | ||
gpxEntry.lat, gpxEntry.lon, | ||
edgeFilter, measurementErrorSigma); | ||
outAllCandidates.addAll(queryResults); | ||
final List<GPXExtension> candidates = new ArrayList<>(); | ||
for (QueryResult candidate : queryResults) { | ||
candidates.add(new GPXExtension(gpxEntry, candidate, indexGPX)); | ||
} | ||
final TimeStep<GPXExtension, GPXEntry, Path> timeStep | ||
= new TimeStep<>(gpxEntry, candidates); | ||
timeSteps.add(timeStep); | ||
prevTimeStep = timeStep; | ||
} | ||
indexGPX++; | ||
int n = filteredGPXEntries.size(); | ||
assert queriesPerEntry.size() == n; | ||
for (int i = 0; i < n; i++) { | ||
|
||
GPXEntry gpxEntry = filteredGPXEntries.get(i); | ||
List<QueryResult> queryResults = queriesPerEntry.get(i); | ||
|
||
// as discussed in #51, if the closest node is virtual (i.e. inner-link) then we need to create two candidates: | ||
// one for each direction of each virtual edge. For example, in A---X---B, we'd add the edges A->X and B->X. Note | ||
// that we add the edges with an incoming direction (i.e. A->X not X->A). We can choose to enforce the incoming/outgoing | ||
// direction with the third argument of queryGraph.enforceHeading | ||
List<GPXExtension> candidates = new ArrayList<GPXExtension>(); | ||
for (QueryResult qr: queryResults) { | ||
int closestNode = qr.getClosestNode(); | ||
if (queryGraph.isVirtualNode(closestNode)) { | ||
// get virtual edges: | ||
List<VirtualEdgeIteratorState> virtualEdges = new ArrayList<VirtualEdgeIteratorState>(); | ||
EdgeIterator iter = queryGraph.createEdgeExplorer().setBaseNode(closestNode); | ||
while (iter.next()) { | ||
if (queryGraph.isVirtualEdge(iter.getEdge())) { | ||
virtualEdges.add((VirtualEdgeIteratorState) queryGraph.getEdgeIteratorState(iter.getEdge(), iter.getAdjNode())); | ||
} | ||
} | ||
assert virtualEdges.size() == 2; | ||
|
||
// create a candidate for each: the candidate being the querypoint plus the virtual edge to favour. Note | ||
// that we favour the virtual edge by *unfavoring* the rest, so we need to record these. | ||
VirtualEdgeIteratorState e1 = virtualEdges.get(0); | ||
VirtualEdgeIteratorState e2 = virtualEdges.get(1); | ||
for (int j = 0; j < 2; j++) { | ||
// get favored/unfavored edges: | ||
VirtualEdgeIteratorState incomingVirtualEdge = j == 0 ? e1 : e2; | ||
VirtualEdgeIteratorState outgoingVirtualEdge = j == 0 ? e2 : e1; | ||
// create candidate | ||
QueryResult vqr = new QueryResult(qr.getQueryPoint().lat, qr.getQueryPoint().lon); | ||
vqr.setQueryDistance(qr.getQueryDistance()); | ||
vqr.setClosestNode(qr.getClosestNode()); | ||
vqr.setWayIndex(qr.getWayIndex()); | ||
vqr.setSnappedPosition(qr.getSnappedPosition()); | ||
vqr.setClosestEdge(qr.getClosestEdge()); | ||
vqr.calcSnappedPoint(distanceCalc); | ||
GPXExtension candidate = new GPXExtension(gpxEntry, vqr, incomingVirtualEdge, outgoingVirtualEdge); | ||
candidates.add(candidate); | ||
} | ||
} else { | ||
// just add the real edge, undirected | ||
GPXExtension candidate = new GPXExtension(gpxEntry, qr); | ||
candidates.add(candidate); | ||
} | ||
} | ||
|
||
final TimeStep<GPXExtension, GPXEntry, Path> timeStep = new TimeStep<>(gpxEntry, candidates); | ||
timeSteps.add(timeStep); | ||
} | ||
return timeSteps; | ||
} | ||
|
@@ -301,9 +365,16 @@ private void computeTransitionProbabilities(TimeStep<GPXExtension, GPXEntry, Pat | |
for (GPXExtension from : prevTimeStep.candidates) { | ||
for (GPXExtension to : timeStep.candidates) { | ||
RoutingAlgorithm algo = algoFactory.createAlgo(queryGraph, algoOptions); | ||
// System.out.println("algo " + algo.getName()); | ||
final Path path = algo.calcPath(from.getQueryResult().getClosestNode(), | ||
to.getQueryResult().getClosestNode()); | ||
// enforce heading if required: | ||
if (from.isDirected()) { | ||
from.incomingVirtualEdge.setUnfavored(true); | ||
} | ||
if (to.isDirected()) { | ||
// unfavor the favour virtual edge | ||
to.outgoingVirtualEdge.setUnfavored(true); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above. We need to make sure that the router does not take the wrong ingoing virtual edge to the virtual "to" node. |
||
} | ||
final Path path = algo.calcPath(from.getQueryResult().getClosestNode(), to.getQueryResult().getClosestNode()); | ||
queryGraph.clearUnfavoredStatus(); | ||
if (path.isFound()) { | ||
timeStep.addRoadPath(from, to, path); | ||
final double transitionLogProbability = probabilities | ||
|
@@ -315,13 +386,15 @@ private void computeTransitionProbabilities(TimeStep<GPXExtension, GPXEntry, Pat | |
} | ||
|
||
private MatchResult computeMatchResult(List<SequenceState<GPXExtension, GPXEntry, Path>> seq, | ||
List<GPXEntry> gpxList, List<QueryResult> allCandidates, | ||
List<GPXEntry> gpxList, List<List<QueryResult>> queriesPerEntry, | ||
EdgeExplorer explorer) { | ||
// every virtual edge maps to its real edge where the orientation is already correct! | ||
// TODO use traversal key instead of string! | ||
final Map<String, EdgeIteratorState> virtualEdgesMap = new HashMap<>(); | ||
for (QueryResult candidate : allCandidates) { | ||
fillVirtualEdges(virtualEdgesMap, explorer, candidate); | ||
for (List<QueryResult> queryResults: queriesPerEntry) { | ||
for (QueryResult qr: queryResults) { | ||
fillVirtualEdges(virtualEdgesMap, explorer, qr); | ||
} | ||
} | ||
|
||
MatchResult matchResult = computeMatchedEdges(seq, virtualEdgesMap); | ||
|
@@ -538,4 +611,4 @@ public Path calcPath(MatchResult mr) { | |
return p; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?> | ||
<gpx xmlns="http://www.topografix.com/GPX/1/1" creator="MapSource 6.16.3" version="1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"> | ||
<trk> | ||
<name>converted track</name> | ||
<trkseg> | ||
<trkpt lat="45.24443688057394" lon="19.70705632120371"><ele>382</ele><time>2010-01-01T01:00:53Z</time></trkpt> | ||
<trkpt lat="45.244557744418756" lon="19.70795754343271"><ele>382</ele><time>2010-01-01T01:01:53Z</time></trkpt> | ||
<trkpt lat="45.24481457923517" lon="19.709523953497406"><ele>382</ele><time>2010-01-01T01:02:53Z</time></trkpt> | ||
<trkpt lat="45.24516205978563" lon="19.71006039530039"><ele>382</ele><time>2010-01-01T01:03:53Z</time></trkpt> | ||
<trkpt lat="45.24513184416995" lon="19.710961617529392"><ele>382</ele><time>2010-01-01T01:04:53Z</time></trkpt> | ||
<trkpt lat="45.24540378413238" lon="19.71250656992197"><ele>382</ele><time>2010-01-01T01:05:53Z</time></trkpt> | ||
<trkpt lat="45.245690830458216" lon="19.713021554052826"><ele>382</ele><time>2010-01-01T01:06:53Z</time></trkpt> | ||
<trkpt lat="45.24563039977332" lon="19.713944233953953"><ele>382</ele><time>2010-01-01T01:07:53Z</time></trkpt> | ||
<trkpt lat="45.24517716758746" lon="19.71370819956064"><ele>382</ele><time>2010-01-01T01:08:53Z</time></trkpt> | ||
<trkpt lat="45.24482968712937" lon="19.71398714929819"><ele>382</ele><time>2010-01-01T01:09:53Z</time></trkpt> | ||
<trkpt lat="45.24458796033977" lon="19.713622368872166"><ele>382</ele><time>2010-01-01T01:10:53Z</time></trkpt> | ||
<trkpt lat="45.244210260171826" lon="19.713515080511566"><ele>382</ele><time>2010-01-01T01:11:53Z</time></trkpt> | ||
<trkpt lat="45.244557744418756" lon="19.71428755670786"><ele>382</ele><time>2010-01-01T01:12:53Z</time></trkpt> | ||
<trkpt lat="45.2443764485551" lon="19.714502133429047"><ele>382</ele><time>2010-01-01T01:13:53Z</time></trkpt> | ||
<trkpt lat="45.244708823863164" lon="19.71527460962534"><ele>382</ele><time>2010-01-01T01:14:53Z</time></trkpt> | ||
<trkpt lat="45.24460306829425" lon="19.71548918634653"><ele>382</ele><time>2010-01-01T01:15:53Z</time></trkpt> | ||
<trkpt lat="45.244905226540226" lon="19.716197289526463"><ele>382</ele><time>2010-01-01T01:16:53Z</time></trkpt> | ||
</trkseg> | ||
</trk> | ||
</gpx> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, we need to unfavor the reverse edge to make sure that the router does not take the wrong outgoing virtual edge from the virtual "from" node.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes - forgot both directions on the edge. As per new commit?