Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
### 5.0 [not yet released]

### 4.0 [29 Sep 2021]

- faster node-based CH preparation (~20%), (#2390)
Expand Down
6 changes: 3 additions & 3 deletions client-hc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,21 @@
<modelVersion>4.0.0</modelVersion>

<artifactId>directions-api-client-hc</artifactId>
<version>4.9-SNAPSHOT</version>
<version>5.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>GraphHopper Directions API hand-crafted Java Client.</name>

<parent>
<groupId>com.github.GIScience.graphhopper</groupId>
<artifactId>graphhopper-parent</artifactId>
<version>4.9-SNAPSHOT</version>
<version>5.0-SNAPSHOT</version>
</parent>

<dependencies>
<dependency>
<groupId>com.github.GIScience.graphhopper</groupId>
<artifactId>graphhopper-web-api</artifactId>
<version>4.9-SNAPSHOT</version>
<version>5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
Expand Down
6 changes: 3 additions & 3 deletions config-example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,9 @@ graphhopper:
# custom_areas.directory: path/to/custom_areas

##### Country Rules #####
# GraphHopper applies country-specific routing rules (enabled by default) during import.
# Use this flag to disable these rules (you need to repeat the import for changes to take effect)
# country_rules.enable: false
# GraphHopper applies country-specific routing rules during import (not enabled by default).
# You need to redo the import for changes to take effect.
# country_rules.enabled: true

# Dropwizard server configuration
server:
Expand Down
6 changes: 3 additions & 3 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<artifactId>graphhopper-core</artifactId>
<name>GraphHopper Core</name>
<version>4.12-SNAPSHOT</version>
<version>5.0-SNAPSHOT</version>
<packaging>jar</packaging>
<description>
GraphHopper is a fast and memory efficient Java road routing engine
Expand All @@ -14,7 +14,7 @@
<parent>
<groupId>com.github.GIScience.graphhopper</groupId>
<artifactId>graphhopper-parent</artifactId>
<version>4.12-SNAPSHOT</version>
<version>5.0-SNAPSHOT</version>
</parent>

<properties>
Expand All @@ -38,7 +38,7 @@
<dependency>
<groupId>com.github.GIScience.graphhopper</groupId>
<artifactId>graphhopper-web-api</artifactId>
<version>4.12-SNAPSHOT</version>
<version>5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.carrotsearch</groupId>
Expand Down
5 changes: 1 addition & 4 deletions core/src/main/java/com/graphhopper/GraphHopper.java
Original file line number Diff line number Diff line change
Expand Up @@ -493,10 +493,7 @@ public GraphHopper init(GraphHopperConfig ghConfig) {
graphHopperFolder = pruneFileEnd(osmFile) + "-gh";
}

if (ghConfig.has("country_rules.enabled")) {
boolean countryRulesEnabled = ghConfig.getBool("country_rules.enabled", false);
countryRuleFactory = countryRulesEnabled ? new CountryRuleFactory() : null;
}
countryRuleFactory = ghConfig.getBool("country_rules.enabled", false) ? new CountryRuleFactory() : null;
customAreasDirectory = ghConfig.getString("custom_areas.directory", customAreasDirectory);

// graph
Expand Down
11 changes: 4 additions & 7 deletions core/src/main/java/com/graphhopper/routing/HeadingResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@
package com.graphhopper.routing;

import com.carrotsearch.hppc.IntArrayList;
import com.graphhopper.routing.util.EdgeFilter;
import com.graphhopper.storage.Graph;
import com.graphhopper.util.*;
import com.graphhopper.util.shapes.GHPoint;

public class HeadingResolver {
private final EdgeExplorer edgeExplorer;
private double toleranceRad = deg2Rad(100);
private double toleranceRad = Math.toRadians(100);

public HeadingResolver(Graph graph) {
this.edgeExplorer = graph.createEdgeExplorer();
Expand Down Expand Up @@ -63,12 +65,7 @@ public IntArrayList getEdgesWithDifferentHeading(int baseNode, double heading) {
* Sets the tolerance for {@link #getEdgesWithDifferentHeading} in degrees.
*/
public HeadingResolver setTolerance(double tolerance) {
this.toleranceRad = deg2Rad(tolerance);
this.toleranceRad = Math.toRadians(tolerance);
return this;
}

private static double deg2Rad(double deg) {
return Math.toRadians(deg);
}

}
25 changes: 17 additions & 8 deletions core/src/main/java/com/graphhopper/routing/Router.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.graphhopper.ResponsePath;
import com.graphhopper.config.Profile;
import com.graphhopper.routing.ch.CHRoutingAlgorithmFactory;
import com.graphhopper.routing.ev.BooleanEncodedValue;
import com.graphhopper.routing.ev.EncodedValueLookup;
import com.graphhopper.routing.ev.Subnetwork;
import com.graphhopper.routing.lm.LMRoutingAlgorithmFactory;
Expand Down Expand Up @@ -176,12 +177,12 @@ private void checkHeadings(GHRequest request) {
}

private void checkPointHints(GHRequest request) {
if (request.getPointHints().size() > 0 && request.getPointHints().size() != request.getPoints().size())
if (!request.getPointHints().isEmpty() && request.getPointHints().size() != request.getPoints().size())
throw new IllegalArgumentException("If you pass " + POINT_HINT + ", you need to pass exactly one hint for every point, empty hints will be ignored");
}

private void checkCurbsides(GHRequest request) {
if (request.getCurbsides().size() > 0 && request.getCurbsides().size() != request.getPoints().size())
if (!request.getCurbsides().isEmpty() && request.getCurbsides().size() != request.getPoints().size())
throw new IllegalArgumentException("If you pass " + CURBSIDE + ", you need to pass exactly one curbside for every point, empty curbsides will be ignored");
}

Expand All @@ -207,7 +208,7 @@ protected GHResponse routeRoundTrip(GHRequest request, FlexSolver solver) {
StopWatch sw = new StopWatch().start();
double startHeading = request.getHeadings().isEmpty() ? Double.NaN : request.getHeadings().get(0);
RoundTripRouting.Params params = new RoundTripRouting.Params(request.getHints(), startHeading, routerConfig.getMaxRoundTripRetries());
List<Snap> snaps = RoundTripRouting.lookup(request.getPoints(), solver.getSnapFilter(), locationIndex, params);
List<Snap> snaps = RoundTripRouting.lookup(request.getPoints(), solver.createSnapFilter(), locationIndex, params);
ghRsp.addDebugInfo("idLookup:" + sw.stop().getSeconds() + "s");
// ORS-GH MOD START - additional code
checkMaxSearchDistances(request, ghRsp, snaps);
Expand Down Expand Up @@ -259,7 +260,8 @@ protected GHResponse routeAlt(GHRequest request, Solver solver) {
throw new IllegalArgumentException("Currently alternative routes work only with start and end point. You tried to use: " + request.getPoints().size() + " points");
GHResponse ghRsp = new GHResponse();
StopWatch sw = new StopWatch().start();
List<Snap> snaps = ViaRouting.lookup(encodingManager, request.getPoints(), solver.getSnapFilter(), locationIndex, request.getSnapPreventions(), request.getPointHints());
List<Snap> snaps = ViaRouting.lookup(encodingManager, request.getPoints(), solver.createSnapFilter(), locationIndex,
request.getSnapPreventions(), request.getPointHints(), solver.createDirectedSnapFilter(), request.getHeadings());
ghRsp.addDebugInfo("idLookup:" + sw.stop().getSeconds() + "s");
// ORS-GH MOD START - additional code
checkMaxSearchDistances(request, ghRsp, snaps);
Expand Down Expand Up @@ -308,7 +310,8 @@ protected GHResponse routeAlt(GHRequest request, Solver solver) {
protected GHResponse routeVia(GHRequest request, Solver solver) {
GHResponse ghRsp = new GHResponse();
StopWatch sw = new StopWatch().start();
List<Snap> snaps = ViaRouting.lookup(encodingManager, request.getPoints(), solver.getSnapFilter(), locationIndex, request.getSnapPreventions(), request.getPointHints());
List<Snap> snaps = ViaRouting.lookup(encodingManager, request.getPoints(), solver.createSnapFilter(), locationIndex,
request.getSnapPreventions(), request.getPointHints(), solver.createDirectedSnapFilter(), request.getHeadings());
ghRsp.addDebugInfo("idLookup:" + sw.stop().getSeconds() + "s");
// ORS-GH MOD START - additional code
checkMaxSearchDistances(request, ghRsp, snaps);
Expand All @@ -321,7 +324,8 @@ protected GHResponse routeVia(GHRequest request, Solver solver) {
boolean forceCurbsides = getForceCurbsides(request.getHints());
// ORS-GH MOD START: enable TD routing
long time = getTime(request.getHints());
ViaRouting.Result result = ViaRouting.calcPaths(request.getPoints(), queryGraph, snaps, solver.weighting, pathCalculator, request.getCurbsides(), forceCurbsides, request.getHeadings(), passThrough, time);
ViaRouting.Result result = ViaRouting.calcPaths(request.getPoints(), queryGraph, snaps, solver.weighting,
pathCalculator, request.getCurbsides(), forceCurbsides, request.getHeadings(), passThrough, time);
// ORS-GH MOD END

if (request.getPoints().size() != result.paths.size() + 1)
Expand Down Expand Up @@ -490,10 +494,15 @@ protected void checkProfileCompatibility() {

protected abstract Weighting createWeighting();

protected EdgeFilter getSnapFilter() {
protected EdgeFilter createSnapFilter() {
return new DefaultSnapFilter(weighting, lookup.getBooleanEncodedValue(Subnetwork.key(profile.getName())));
}

protected EdgeFilter createDirectedSnapFilter() {
BooleanEncodedValue inSubnetworkEnc = lookup.getBooleanEncodedValue(Subnetwork.key(profile.getName()));
return edgeState -> !edgeState.get(inSubnetworkEnc) && Double.isFinite(weighting.calcEdgeWeightWithAccess(edgeState, false));
}

protected abstract PathCalculator createPathCalculator(QueryGraph queryGraph);

private List<String> getTurnCostProfiles() {
Expand Down Expand Up @@ -611,7 +620,7 @@ protected FlexiblePathCalculator createPathCalculator(QueryGraph queryGraph) {

// ORS-GH MOD START: pass edgeFilter
@Override
protected EdgeFilter getSnapFilter() {
protected EdgeFilter createSnapFilter() {
EdgeFilter defaultSnapFilter = new DefaultSnapFilter(weighting, lookup.getBooleanEncodedValue(Subnetwork.key(profile.getName())));
if (edgeFilterFactory != null)
return edgeFilterFactory.createEdgeFilter(request.getAdditionalHints(), weighting.getFlagEncoder(), ghStorage, defaultSnapFilter);
Expand Down
23 changes: 16 additions & 7 deletions core/src/main/java/com/graphhopper/routing/ViaRouting.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import com.graphhopper.routing.ev.RoadEnvironment;
import com.graphhopper.routing.querygraph.QueryGraph;
import com.graphhopper.routing.util.EdgeFilter;
import com.graphhopper.routing.util.FiniteWeightFilter;
import com.graphhopper.routing.util.HeadingEdgeFilter;
import com.graphhopper.routing.util.NameSimilarityEdgeFilter;
import com.graphhopper.routing.util.SnapPreventionEdgeFilter;
import com.graphhopper.routing.weighting.Weighting;
Expand Down Expand Up @@ -54,27 +54,36 @@ public class ViaRouting {
/**
* @throws MultiplePointsNotFoundException in case one or more points could not be resolved
*/
public static List<Snap> lookup(EncodedValueLookup lookup, List<GHPoint> points, EdgeFilter edgeFilter, LocationIndex locationIndex, List<String> snapPreventions, List<String> pointHints) {
public static List<Snap> lookup(EncodedValueLookup lookup, List<GHPoint> points, EdgeFilter snapFilter,
LocationIndex locationIndex, List<String> snapPreventions, List<String> pointHints,
EdgeFilter directedSnapFilter, List<Double> headings) {
if (points.size() < 2)
throw new IllegalArgumentException("At least 2 points have to be specified, but was:" + points.size());

final EnumEncodedValue<RoadClass> roadClassEnc = lookup.getEnumEncodedValue(RoadClass.KEY, RoadClass.class);
final EnumEncodedValue<RoadEnvironment> roadEnvEnc = lookup.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class);
EdgeFilter strictEdgeFilter = snapPreventions.isEmpty()
? edgeFilter
: new SnapPreventionEdgeFilter(edgeFilter, roadClassEnc, roadEnvEnc, snapPreventions);
? snapFilter
: new SnapPreventionEdgeFilter(snapFilter, roadClassEnc, roadEnvEnc, snapPreventions);
List<Snap> snaps = new ArrayList<>(points.size());
IntArrayList pointsNotFound = new IntArrayList();
for (int placeIndex = 0; placeIndex < points.size(); placeIndex++) {
GHPoint point = points.get(placeIndex);
Snap snap = null;
if (!pointHints.isEmpty())
if (placeIndex < headings.size() && !Double.isNaN(headings.get(placeIndex))) {
if (!pointHints.isEmpty() && !Helper.isEmpty(pointHints.get(placeIndex)))
throw new IllegalArgumentException("Cannot specify heading and point_hint at the same time. " +
"Make sure you specify either an empty point_hint (String) or a NaN heading (double) for point " + placeIndex);
snap = locationIndex.findClosest(point.lat, point.lon, new HeadingEdgeFilter(directedSnapFilter, headings.get(placeIndex), point));
} else if (!pointHints.isEmpty()) {
snap = locationIndex.findClosest(point.lat, point.lon, new NameSimilarityEdgeFilter(strictEdgeFilter,
pointHints.get(placeIndex), point, 100));
else if (!snapPreventions.isEmpty())
} else if (!snapPreventions.isEmpty()) {
snap = locationIndex.findClosest(point.lat, point.lon, strictEdgeFilter);
}

if (snap == null || !snap.isValid())
snap = locationIndex.findClosest(point.lat, point.lon, edgeFilter);
snap = locationIndex.findClosest(point.lat, point.lon, snapFilter);
if (!snap.isValid())
pointsNotFound.add(placeIndex);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.graphhopper.routing.util;

import com.graphhopper.util.*;
import com.graphhopper.util.shapes.GHPoint;

public class HeadingEdgeFilter implements EdgeFilter {

private final double heading;
private final EdgeFilter directedEdgeFilter;
private final GHPoint pointNearHeading;

public HeadingEdgeFilter(EdgeFilter directedEdgeFilter, double heading, GHPoint pointNearHeading) {
this.directedEdgeFilter = directedEdgeFilter;
this.heading = heading;
this.pointNearHeading = pointNearHeading;
}

@Override
public boolean accept(EdgeIteratorState edgeState) {
final double tolerance = 30;
// we only accept edges that are not too far away. It might happen that only far away edges match the heading
// in which case we rather rely on the fallback snapping than return a match here.
final double maxDistance = 20;
double headingOfEdge = getHeadingOfGeometryNearPoint(edgeState, pointNearHeading, maxDistance);
if (Double.isNaN(headingOfEdge))
// this edge is too far away. we do not accept it.
return false;
// we accept the edge if either of the two directions roughly has the right heading
return Math.abs(headingOfEdge - heading) < tolerance && directedEdgeFilter.accept(edgeState) ||
Math.abs((headingOfEdge + 180) % 360 - heading) < tolerance && directedEdgeFilter.accept(edgeState.detach(true));
}

/**
* Calculates the heading (in degrees) of the given edge in fwd direction near the given point. If the point is
* too far away from the edge (according to the maxDistance parameter) it returns Double.NaN.
*/
static double getHeadingOfGeometryNearPoint(EdgeIteratorState edgeState, GHPoint point, double maxDistance) {
final DistanceCalc calcDist = DistanceCalcEarth.DIST_EARTH;
double closestDistance = Double.POSITIVE_INFINITY;
PointList points = edgeState.fetchWayGeometry(FetchMode.ALL);
int closestPoint = -1;
for (int i = 1; i < points.size(); i++) {
double fromLat = points.getLat(i - 1), fromLon = points.getLon(i - 1);
double toLat = points.getLat(i), toLon = points.getLon(i);
// the 'distance' between the point and an edge segment is either the vertical distance to the segment or
// the distance to the closer one of the two endpoints. here we save one call to calcDist per segment,
// because each endpoint appears in two segments (except the first and last).
double distance = calcDist.validEdgeDistance(point.lat, point.lon, fromLat, fromLon, toLat, toLon)
? calcDist.calcDenormalizedDist(calcDist.calcNormalizedEdgeDistance(point.lat, point.lon, fromLat, fromLon, toLat, toLon))
: calcDist.calcDist(fromLat, fromLon, point.lat, point.lon);
if (i == points.size() - 1)
distance = Math.min(distance, calcDist.calcDist(toLat, toLon, point.lat, point.lon));
if (distance > maxDistance)
continue;
if (distance < closestDistance) {
closestDistance = distance;
closestPoint = i;
}
}
if (closestPoint < 0)
return Double.NaN;

double fromLat = points.getLat(closestPoint - 1), fromLon = points.getLon(closestPoint - 1);
double toLat = points.getLat(closestPoint), toLon = points.getLon(closestPoint);
// calcOrientation returns value relative to East, but heading is relative to North
return (Math.toDegrees(AngleCalc.ANGLE_CALC.calcOrientation(fromLat, fromLon, toLat, toLon)) + 90) % 360;
}
}
Loading
Loading