com.graphhopper.matching.LocationIndexMatch Maven / Gradle / Ivy
/*
* Licensed to GraphHopper and Peter Karich under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*
* GraphHopper licenses this file to you 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 com.graphhopper.matching;
import com.graphhopper.coll.GHBitSet;
import com.graphhopper.coll.GHTBitSet;
import com.graphhopper.routing.util.EdgeFilter;
import com.graphhopper.storage.Graph;
import com.graphhopper.storage.GraphStorage;
import com.graphhopper.storage.index.LocationIndexTree;
import com.graphhopper.storage.index.QueryResult;
import com.graphhopper.util.EdgeExplorer;
import com.graphhopper.util.EdgeIteratorState;
import com.graphhopper.util.Helper;
import gnu.trove.procedure.TIntProcedure;
import gnu.trove.set.hash.TIntHashSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
*
* @author Peter Karich
*/
public class LocationIndexMatch extends LocationIndexTree {
private static final Comparator QR_COMPARATOR = new Comparator() {
@Override
public int compare(QueryResult o1, QueryResult o2) {
return Double.compare(o1.getQueryDistance(), o2.getQueryDistance());
}
};
private final double returnAllResultsWithin;
private final LocationIndexTree index;
public LocationIndexMatch(GraphStorage graph, LocationIndexTree index) {
this(graph, index, 15);
}
public LocationIndexMatch(GraphStorage graph, LocationIndexTree index, int gpxAccuracyInMetern) {
super(graph, graph.getDirectory());
this.index = index;
// Return ALL results which are very close and e.g. within the GPS signal accuracy.
// Also important to get all edges if GPS point is close to a junction.
returnAllResultsWithin = distCalc.calcNormalizedDist(gpxAccuracyInMetern);
}
public List findNClosest(final double queryLat, final double queryLon, final EdgeFilter edgeFilter) {
// implement a cheap priority queue via List, sublist and Collections.sort
final List queryResults = new ArrayList();
TIntHashSet set = index.findNetworkEntries(queryLat, queryLon, 2);
final GHBitSet exploredNodes = new GHTBitSet(new TIntHashSet(set));
final EdgeExplorer explorer = graph.createEdgeExplorer(edgeFilter);
set.forEach(new TIntProcedure() {
@Override
public boolean execute(int node) {
new XFirstSearchCheck(queryLat, queryLon, exploredNodes, edgeFilter) {
@Override
protected double getQueryDistance() {
// do not skip search if distance is 0 or near zero (equalNormedDelta)
return Double.MAX_VALUE;
}
@Override
protected boolean check(int node, double normedDist, int wayIndex, EdgeIteratorState edge, QueryResult.Position pos) {
if (normedDist < returnAllResultsWithin
|| queryResults.isEmpty()
|| queryResults.get(0).getQueryDistance() > normedDist) {
int index = -1;
for (int qrIndex = 0; qrIndex < queryResults.size(); qrIndex++) {
QueryResult qr = queryResults.get(qrIndex);
// overwrite older queryResults which are potentially more far away than returnAllResultsWithin
if (qr.getQueryDistance() > returnAllResultsWithin) {
index = qrIndex;
break;
}
// avoid duplicate edges
if (qr.getClosestEdge().getEdge() == edge.getEdge()) {
if (qr.getQueryDistance() < normedDist) {
// do not add current edge
return true;
} else {
// overwrite old edge with current
index = qrIndex;
break;
}
}
}
QueryResult qr = new QueryResult(queryLat, queryLon);
qr.setQueryDistance(normedDist);
qr.setClosestNode(node);
qr.setClosestEdge(edge.detach(false));
qr.setWayIndex(wayIndex);
qr.setSnappedPosition(pos);
if (index < 0) {
queryResults.add(qr);
} else {
queryResults.set(index, qr);
}
}
return true;
}
}.start(explorer, node);
return true;
}
});
Collections.sort(queryResults, QR_COMPARATOR);
for (QueryResult qr : queryResults) {
if (qr.isValid()) {
// denormalize distance
qr.setQueryDistance(distCalc.calcDenormalizedDist(qr.getQueryDistance()));
qr.calcSnappedPoint(distCalc);
} else {
throw new IllegalStateException("invalid query result should not happen here: " + qr);
}
}
return queryResults;
}
}