Skip to content

Commit

Permalink
Add 100m radius restriction to point hint matches (#1935)
Browse files Browse the repository at this point in the history
* Add 100m radius restriction to point hint matches

* Added requested changes
  • Loading branch information
samruston committed Feb 28, 2020
1 parent 6f31ce0 commit 7d1f8c8
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 12 deletions.
4 changes: 2 additions & 2 deletions core/src/main/java/com/graphhopper/GraphHopper.java
Original file line number Diff line number Diff line change
Expand Up @@ -1168,9 +1168,9 @@ protected RoutingTemplate createRoutingTemplate(GHRequest request, GHResponse gh
if (ROUND_TRIP.equalsIgnoreCase(algoStr))
routingTemplate = new RoundTripRoutingTemplate(request, ghRsp, locationIndex, encodingManager, weighting, routingConfig.getMaxRoundTripRetries());
else if (ALT_ROUTE.equalsIgnoreCase(algoStr))
routingTemplate = new AlternativeRoutingTemplate(request, ghRsp, locationIndex, encodingManager, weighting);
routingTemplate = new AlternativeRoutingTemplate(request, ghRsp, locationIndex, ghStorage.getNodeAccess(), encodingManager, weighting);
else
routingTemplate = new ViaRoutingTemplate(request, ghRsp, locationIndex, encodingManager, weighting);
routingTemplate = new ViaRoutingTemplate(request, ghRsp, locationIndex, ghStorage.getNodeAccess(), encodingManager, weighting);
return routingTemplate;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.graphhopper.routing.profiles.EncodedValueLookup;
import com.graphhopper.routing.querygraph.QueryGraph;
import com.graphhopper.routing.weighting.Weighting;
import com.graphhopper.storage.NodeAccess;
import com.graphhopper.storage.index.LocationIndex;
import com.graphhopper.storage.index.QueryResult;
import com.graphhopper.util.Parameters.Routing;
Expand All @@ -46,8 +47,8 @@
*/
final public class AlternativeRoutingTemplate extends ViaRoutingTemplate {
public AlternativeRoutingTemplate(GHRequest ghRequest, GHResponse ghRsp, LocationIndex locationIndex,
EncodedValueLookup lookup, Weighting weighting) {
super(ghRequest, ghRsp, locationIndex, lookup, weighting);
NodeAccess nodeAccess, EncodedValueLookup lookup, Weighting weighting) {
super(ghRequest, ghRsp, locationIndex, nodeAccess, lookup, weighting);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.graphhopper.routing.util.NameSimilarityEdgeFilter;
import com.graphhopper.routing.util.SnapPreventionEdgeFilter;
import com.graphhopper.routing.weighting.Weighting;
import com.graphhopper.storage.NodeAccess;
import com.graphhopper.storage.index.LocationIndex;
import com.graphhopper.storage.index.QueryResult;
import com.graphhopper.util.*;
Expand Down Expand Up @@ -57,13 +58,15 @@ public class ViaRoutingTemplate extends AbstractRoutingTemplate implements Routi
// result from route
protected List<Path> pathList;
protected final PathWrapper altResponse = new PathWrapper();
private final NodeAccess nodeAccess;
private final EnumEncodedValue<RoadClass> roadClassEnc;
private final EnumEncodedValue<RoadEnvironment> roadEnvEnc;

public ViaRoutingTemplate(GHRequest ghRequest, GHResponse ghRsp, LocationIndex locationIndex, EncodedValueLookup lookup, final Weighting weighting) {
public ViaRoutingTemplate(GHRequest ghRequest, GHResponse ghRsp, LocationIndex locationIndex, NodeAccess nodeAccess, EncodedValueLookup lookup, final Weighting weighting) {
super(locationIndex, lookup, weighting);
this.ghRequest = ghRequest;
this.ghResponse = ghRsp;
this.nodeAccess = nodeAccess;
this.roadClassEnc = lookup.getEnumEncodedValue(RoadClass.KEY, RoadClass.class);
this.roadEnvEnc = lookup.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class);
}
Expand All @@ -81,7 +84,7 @@ public List<QueryResult> lookup(List<GHPoint> points) {
GHPoint point = points.get(placeIndex);
QueryResult qr = null;
if (ghRequest.hasPointHints())
qr = locationIndex.findClosest(point.lat, point.lon, new NameSimilarityEdgeFilter(strictEdgeFilter, ghRequest.getPointHints().get(placeIndex)));
qr = locationIndex.findClosest(point.lat, point.lon, new NameSimilarityEdgeFilter(strictEdgeFilter, nodeAccess, point, 100, ghRequest.getPointHints().get(placeIndex)));
else if (ghRequest.hasSnapPreventions())
qr = locationIndex.findClosest(point.lat, point.lon, strictEdgeFilter);
if (qr == null || !qr.isValid())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@

import com.graphhopper.apache.commons.lang3.StringUtils;
import com.graphhopper.debatty.java.stringsimilarity.JaroWinkler;
import com.graphhopper.storage.GraphEdgeIdFinder;
import com.graphhopper.storage.NodeAccess;
import com.graphhopper.util.EdgeIteratorState;
import com.graphhopper.util.shapes.BBox;
import com.graphhopper.util.shapes.Circle;
import com.graphhopper.util.shapes.GHPoint;

import java.util.Arrays;
import java.util.HashMap;
Expand Down Expand Up @@ -84,18 +89,23 @@ public class NameSimilarityEdgeFilter implements EdgeFilter {
private final EdgeFilter edgeFilter;
private final String pointHint;
private final Map<String, String> rewriteMap;
private final Circle pointCircle;
private final NodeAccess nodeAccess;

public NameSimilarityEdgeFilter(EdgeFilter edgeFilter, String pointHint) {
this(edgeFilter, pointHint, DEFAULT_REWRITE_MAP);
public NameSimilarityEdgeFilter(EdgeFilter edgeFilter, NodeAccess nodeAccess, GHPoint point, double radius, String pointHint) {
this(edgeFilter, nodeAccess, pointHint, point, radius, DEFAULT_REWRITE_MAP);
}

/**
* @param radius the searchable region about the point in meters
* @param rewriteMap maps abbreviations to its longer form
*/
public NameSimilarityEdgeFilter(EdgeFilter edgeFilter, String pointHint, Map<String, String> rewriteMap) {
public NameSimilarityEdgeFilter(EdgeFilter edgeFilter, NodeAccess nodeAccess, String pointHint, GHPoint point, double radius, Map<String, String> rewriteMap) {
this.edgeFilter = edgeFilter;
this.rewriteMap = rewriteMap;
this.nodeAccess = nodeAccess;
this.pointHint = prepareName(removeRelation(pointHint == null ? "" : pointHint));
this.pointCircle = new Circle(point.lat, point.lon, radius);
}

String getNormalizedPointHint() {
Expand Down Expand Up @@ -148,11 +158,22 @@ public final boolean accept(EdgeIteratorState iter) {
return false;
}

BBox bbox = createBBox(nodeAccess, iter);
if(!pointCircle.intersects(bbox)) {
return false;
}

name = removeRelation(name);
String edgeName = prepareName(name);

return isJaroWinklerSimilar(pointHint, edgeName);
}

private static BBox createBBox(NodeAccess na, EdgeIteratorState edgeState) {
return BBox.fromPoints(na.getLatitude(edgeState.getBaseNode()), na.getLongitude(edgeState.getBaseNode()),
na.getLatitude(edgeState.getAdjNode()), na.getLongitude(edgeState.getAdjNode()));
}

private boolean isJaroWinklerSimilar(String str1, String str2) {
double jwSimilarity = jaroWinkler.similarity(str1, str2);
// System.out.println(str1 + " vs. edge:" + str2 + ", " + jwSimilarity);
Expand Down
72 changes: 72 additions & 0 deletions core/src/main/java/com/graphhopper/util/GHUtility.java
Original file line number Diff line number Diff line change
Expand Up @@ -896,4 +896,76 @@ public int getMergeStatus(int flags) {
throw new UnsupportedOperationException("Not supported. Edge is empty.");
}
}

/**
* This node access can be used in tests to mock specific iterator behaviour via overloading
* certain methods.
*/
public static class DisabledNodeAccess implements NodeAccess {

@Override
public int getTurnCostIndex(int nodeId) {
throw new UnsupportedOperationException("Not supported.");
}

@Override
public void setTurnCostIndex(int nodeId, int additionalValue) {
throw new UnsupportedOperationException("Not supported.");
}

@Override
public boolean is3D() {
throw new UnsupportedOperationException("Not supported.");
}

@Override
public int getDimension() {
throw new UnsupportedOperationException("Not supported.");
}

@Override
public void ensureNode(int nodeId) {
throw new UnsupportedOperationException("Not supported.");
}

@Override
public void setNode(int nodeId, double lat, double lon) {
throw new UnsupportedOperationException("Not supported.");
}

@Override
public void setNode(int nodeId, double lat, double lon, double ele) {
throw new UnsupportedOperationException("Not supported.");
}

@Override
public double getLatitude(int nodeId) {
throw new UnsupportedOperationException("Not supported.");
}

@Override
public double getLat(int nodeId) {
throw new UnsupportedOperationException("Not supported.");
}

@Override
public double getLongitude(int nodeId) {
throw new UnsupportedOperationException("Not supported.");
}

@Override
public double getLon(int nodeId) {
throw new UnsupportedOperationException("Not supported.");
}

@Override
public double getElevation(int nodeId) {
throw new UnsupportedOperationException("Not supported.");
}

@Override
public double getEle(int nodeId) {
throw new UnsupportedOperationException("Not supported.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,17 @@
*/
package com.graphhopper.routing.util;

import com.graphhopper.routing.querygraph.QueryGraph;
import com.graphhopper.storage.Graph;
import com.graphhopper.storage.GraphBuilder;
import com.graphhopper.storage.NodeAccess;
import com.graphhopper.storage.index.LocationIndex;
import com.graphhopper.util.CHEdgeIterator;
import com.graphhopper.util.EdgeIterator;
import com.graphhopper.util.EdgeIteratorState;
import com.graphhopper.util.GHUtility;
import com.graphhopper.util.shapes.GHPoint;
import org.junit.Before;
import org.junit.Test;

import static junit.framework.TestCase.assertFalse;
Expand All @@ -30,6 +39,8 @@
*/
public class NameSimilarityEdgeFilterTest {

private GHPoint basePoint = new GHPoint(49.4652132,11.1435159);

@Test
public void testAccept() {
EdgeFilter edgeFilter = createNameSimilarityEdgeFilter("Laufamholzstraße 154 Nürnberg");
Expand Down Expand Up @@ -77,6 +88,34 @@ public void testAccept() {
assertTrue(edgeFilter.accept(edge));
}

@Test
public void testDistanceFiltering() {
CarFlagEncoder encoder = new CarFlagEncoder();
Graph g = new GraphBuilder(EncodingManager.create(encoder)).create();
NodeAccess na = g.getNodeAccess();

GHPoint pointFarAway = new GHPoint(49.458629, 11.146124);
GHPoint point25mAway = new GHPoint(49.464871, 11.143575);
GHPoint point200mAway = new GHPoint(49.464598, 11.149039);

int farAwayId = 0;
int nodeId50 = 1;
int nodeID200 = 2;

na.setNode(farAwayId, pointFarAway.lat, pointFarAway.lon);
na.setNode(nodeId50, point25mAway.lat, point25mAway.lon);
na.setNode(nodeID200, point200mAway.lat, point200mAway.lon);

// Check that it matches a street 50m away
assertTrue(createNameSimilarityEdgeFilter(na, "Wentworth Street").
accept(createTestEdgeIterator("Wentworth Street", nodeId50, farAwayId)));

// Check that it doesn't match streets 200m away
assertFalse(createNameSimilarityEdgeFilter(na, "Wentworth Street").
accept(createTestEdgeIterator("Wentworth Street", nodeID200, farAwayId)));

}

/**
* With Nominatim you should not use the "placename" for best results, otherwise the length difference becomes too big
*/
Expand Down Expand Up @@ -192,22 +231,51 @@ public void testAcceptWithTypos() {
// assertTrue(edgeFilter.accept(edge));
}

/**
* Create a NameSimilarityEdgeFilter that uses the same coordinates for all nodes
* so distance is not used when matching
*/
private NameSimilarityEdgeFilter createNameSimilarityEdgeFilter(String pointHint) {
return createNameSimilarityEdgeFilter(new GHUtility.DisabledNodeAccess() {
@Override
public double getLatitude(int nodeId) {
return basePoint.lat;
}
@Override
public double getLongitude(int nodeId) {
return basePoint.lon;
}
}, pointHint);
}

private NameSimilarityEdgeFilter createNameSimilarityEdgeFilter(NodeAccess nodeAccess, String pointHint) {
return new NameSimilarityEdgeFilter(new EdgeFilter() {
@Override
public boolean accept(EdgeIteratorState edgeState) {
return true;
}
}, pointHint);
}, nodeAccess, basePoint, 100, pointHint);
}

private EdgeIteratorState createTestEdgeIterator(final String name) {
private EdgeIteratorState createTestEdgeIterator(final String name, final int baseNodeId, final int adjNodeId) {
return new GHUtility.DisabledEdgeIterator() {

@Override
public String getName() {
return name;
}
@Override
public int getBaseNode() {
return baseNodeId;
}
@Override
public int getAdjNode() {
return adjNodeId;
}
};
}

private EdgeIteratorState createTestEdgeIterator(final String name) {
return createTestEdgeIterator(name, 0, 0);
}

}

0 comments on commit 7d1f8c8

Please sign in to comment.