Skip to content

Commit

Permalink
Merge branch 'main' into feat/open_up_snapping_endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
takb authored Jun 4, 2024
2 parents d89fff9 + 31d8c8a commit bffdfa5
Show file tree
Hide file tree
Showing 12 changed files with 208 additions and 49 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ RELEASING:
### Changed
- determine way surface based only on the value of OSM tag `surface`; if the tag is not present, the surface is reported as "Unknown" and no longer inferred from the way type ([#1794](https://github.com/GIScience/openrouteservice/pull/1794))
- revise snap endpoint error codes ([#1796](https://github.com/GIScience/openrouteservice/pull/1796))
- way surface is now being determined based only on the value of OSM `surface` tag; when the tag is not present the surface is reported as "Unknown" and is no longer being inferred from the way type ([#1794](https://github.com/GIScience/openrouteservice/pull/1794))
- improved performance of RPHAST matrix queries in the case when the number of sources is higher than the number of destinations
### Deprecated
- soon-to-be removed features.
### Removed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,31 @@ void testDefinedSourcesAndDestinations() {
.statusCode(200);
}

@Test
void testSwap() {
JSONObject body = new JSONObject();
body.put("locations", getParameter("locations5"));
body.put("sources", new JSONArray(new int[]{0, 1, 4}));
body.put("destinations", new JSONArray(new int[]{2, 3}));
body.put("metrics", getParameter("metricsDuration"));

given()
.config(JSON_CONFIG_DOUBLE_NUMBERS)
.headers(jsonContent)
.pathParam("profile", getParameter("carProfile"))
.body(body.toString())
.when().log().ifValidationFails()
.post(getEndPointPath() + "/{profile}/json")
.then().log().ifValidationFails()
.assertThat()
.body("any { it.key == 'durations' }", is(true))
.body("durations[0][0]", is(closeTo(726.88, 2)))
.body("durations[0][1]", is(closeTo(672.3, 2)))
.body("durations[1][0]", is(closeTo(625.37, 2)))
.body("durations[1][1]", is(closeTo(570.8, 2)))
.statusCode(200);
}

@Test
void expectTurnRestrictionDurations() {
JSONObject body = new JSONObject();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.carrotsearch.hppc.IntHashSet;
import com.graphhopper.routing.querygraph.QueryRoutingCHGraph;
import com.graphhopper.routing.util.FlagEncoder;
import com.graphhopper.storage.RoutingCHEdgeExplorer;
import com.graphhopper.storage.RoutingCHEdgeIterator;
import com.graphhopper.storage.RoutingCHGraph;
Expand All @@ -24,9 +23,9 @@ public class TargetGraphBuilder {
*
* @param targets the targets that form the seed for target graph building
*/
public TargetGraphResults prepareTargetGraph(int[] targets, RoutingCHGraph chGraph, FlagEncoder encoder, boolean swap, int coreNodeLevel) {
public TargetGraphResults prepareTargetGraph(int[] targets, RoutingCHGraph chGraph, boolean swap, int coreNodeLevel) {
PriorityQueue<Integer> localPrioQueue = new PriorityQueue<>(100);
ExclusiveDownwardSearchEdgeFilter downwardEdgeFilter = new ExclusiveDownwardSearchEdgeFilter(chGraph, encoder, swap);
ExclusiveDownwardSearchEdgeFilter downwardEdgeFilter = new ExclusiveDownwardSearchEdgeFilter(chGraph, swap);
RoutingCHEdgeExplorer edgeExplorer = swap ? chGraph.createOutEdgeExplorer()
: chGraph.createInEdgeExplorer();
SubGraph targetGraph = new SubGraph(chGraph);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public MatrixResult compute(MatrixLocations srcData, MatrixLocations dstData, in
}
this.treeEntrySize = srcData.size();

TargetGraphBuilder.TargetGraphResults targetGraphResults = new TargetGraphBuilder().prepareTargetGraph(dstData.getNodeIds(), chGraph, encoder, swap, coreNodeLevel);
TargetGraphBuilder.TargetGraphResults targetGraphResults = new TargetGraphBuilder().prepareTargetGraph(dstData.getNodeIds(), chGraph, swap, coreNodeLevel);
targetGraph = targetGraphResults.getTargetGraph();
coreExitPoints.addAll(targetGraphResults.getCoreExitPoints());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,13 @@ public void init(MatrixRequest req, GraphHopper gh, RoutingCHGraph chGraph, Flag

@Override
public MatrixResult compute(MatrixLocations srcData, MatrixLocations dstData, int metrics) throws Exception {
MatrixResult mtxResult = new MatrixResult(srcData.getLocations(), dstData.getLocations());
// Search is more efficient for dstData.size > srcData.size, so check if they should be swapped
boolean swap = checkSwapSrcDst(srcData, dstData);
if (swap) {
MatrixLocations tmp = srcData;
srcData = dstData;
dstData = tmp;
}

float[] times = null;
float[] distances = null;
Expand All @@ -60,15 +66,12 @@ public MatrixResult compute(MatrixLocations srcData, MatrixLocations dstData, in
for (int srcIndex = 0; srcIndex < srcData.size(); srcIndex++)
pathMetricsExtractor.setEmptyValues(srcIndex, dstData, times, distances, weights);
} else {
RPHASTAlgorithm algorithm = new RPHASTAlgorithm(chGraph, chGraph.getWeighting(), TraversalMode.NODE_BASED);
RPHASTAlgorithm algorithm = new RPHASTAlgorithm(chGraph, chGraph.getWeighting(), TraversalMode.NODE_BASED, swap);
algorithm.setMaxVisitedNodes(this.maxVisitedNodes);

int[] srcIds = getValidNodeIds(srcData.getNodeIds());
int[] destIds = getValidNodeIds(dstData.getNodeIds());

if (graphHopper != null)
mtxResult.setGraphDate(graphHopper.getGraphHopperStorage().getProperties().get("datareader.import.date"));

algorithm.prepare(srcIds, destIds);

MultiTreeSPEntry[] destTrees = algorithm.calcPaths(srcIds, destIds);
Expand All @@ -84,10 +87,26 @@ public MatrixResult compute(MatrixLocations srcData, MatrixLocations dstData, in
originalDestTrees[i] = null;
}
}

pathMetricsExtractor.setSwap(swap);
pathMetricsExtractor.calcValues(originalDestTrees, srcData, dstData, times, distances, weights);
}

// Need to remap sources and destinations if we swapped before
if (swap) {
MatrixLocations tmp = srcData;
srcData = dstData;
dstData = tmp;
float[][] results = swapResults(srcData, dstData, times, distances, weights);
times = results[0];
distances = results[1];
weights = results[2];
}

MatrixResult mtxResult = new MatrixResult(srcData.getLocations(), dstData.getLocations());

if (graphHopper != null)
mtxResult.setGraphDate(graphHopper.getGraphHopperStorage().getProperties().get("datareader.import.date"));

if (MatrixMetricsType.isSet(metrics, MatrixMetricsType.DURATION))
mtxResult.setTable(MatrixMetricsType.DURATION, times);
if (MatrixMetricsType.isSet(metrics, MatrixMetricsType.DISTANCE))
Expand All @@ -112,4 +131,53 @@ private int[] getValidNodeIds(int[] nodeIds) {

return res;
}

/**
* Search is more efficient for low source count and high destination count than the other way around.
* If there are more sources than destinations, they get swapped and all calculations are done backwards.
* The final result gets unswapped to return correct results.
*
* @param srcData original Source data
* @param dstData original Destination data
* @return boolean whether more sources than destinations
*/
private boolean checkSwapSrcDst(MatrixLocations srcData, MatrixLocations dstData) {
return (srcData.size() > dstData.size());
}

/**
* Invert the results matrix (represented by flattened array) in case src and dst were swapped
*
* @param srcData the original unswapped source data
* @param dstData the original unswapped destination data
* @param times the swapped array of results
* @param distances the swapped array of results
* @param weights the swapped array of results
* @return array of unswapped result arrays [times, distances, weights]
*/
private float[][] swapResults(MatrixLocations srcData, MatrixLocations dstData, float[] times, float[] distances, float[] weights) {
boolean hasTimes = times != null;
boolean hasDistances = distances != null;
boolean hasWeights = weights != null;
float[] newTimes = new float[hasTimes ? times.length : 0];
float[] newDistances = new float[hasDistances ? distances.length : 0];
float[] newWeights = new float[hasWeights ? weights.length : 0];

int i = 0;
int srcSize = srcData.size();
int dstSize = dstData.size();
for (int dst = 0; dst < dstSize; dst++) {
for (int src = 0; src < srcSize; src++) {
int index = dst + src * dstSize;
if (hasTimes)
newTimes[index] = times[i];
if (hasDistances)
newDistances[index] = distances[i];
if (hasWeights)
newWeights[index] = weights[i];
i++;
}
}
return new float[][]{newTimes, newDistances, newWeights};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

import com.carrotsearch.hppc.IntObjectMap;
import com.graphhopper.coll.GHIntObjectHashMap;
import com.graphhopper.routing.util.FlagEncoder;
import com.graphhopper.routing.util.TraversalMode;
import com.graphhopper.routing.weighting.Weighting;
import com.graphhopper.storage.RoutingCHEdgeExplorer;
Expand All @@ -36,6 +35,7 @@ public class RPHASTAlgorithm extends AbstractManyToManyRoutingAlgorithm {
private MultiTreeSPEntry currFrom;
private PriorityQueue<MultiTreeSPEntry> prioQueue;
private SubGraph targetGraph;
private boolean swap;
private boolean finishedFrom;
private boolean finishedTo;
private int visitedCountFrom;
Expand All @@ -49,15 +49,17 @@ public class RPHASTAlgorithm extends AbstractManyToManyRoutingAlgorithm {
private double tmpWeight;

public RPHASTAlgorithm(RoutingCHGraph graph, Weighting weighting, TraversalMode traversalMode) {
this(graph, weighting, traversalMode, false);
}
public RPHASTAlgorithm(RoutingCHGraph graph, Weighting weighting, TraversalMode traversalMode, boolean swap) {
super(graph, weighting, traversalMode);

this.swap = swap;
int size = Math.min(Math.max(200, graph.getNodes() / 10), 2000);

initCollections(size);
FlagEncoder encoder = weighting.getFlagEncoder();

upwardEdgeFilter = new UpwardSearchEdgeFilter(graph, encoder);
downwardEdgeFilter = new DownwardSearchEdgeFilter(graph, encoder);
upwardEdgeFilter = new UpwardSearchEdgeFilter(graph);
downwardEdgeFilter = new DownwardSearchEdgeFilter(graph);
}

protected void initCollections(int size) {
Expand All @@ -84,13 +86,16 @@ public void prepare(int[] sources, int[] targets) {

addNodes(targetGraph, localPrioQueue, targets);

RoutingCHEdgeExplorer edgeExplorer = swap ? graph.createOutEdgeExplorer()
: graph.createInEdgeExplorer();

while (!localPrioQueue.isEmpty()) {
int node = localPrioQueue.poll();
RoutingCHEdgeIterator iter = inEdgeExplorer.setBaseNode(node);
RoutingCHEdgeIterator iter = edgeExplorer.setBaseNode(node);
downwardEdgeFilter.setBaseNode(node);

while (iter.next()) {
if (!downwardEdgeFilter.accept(iter))
if (!downwardEdgeFilter.accept(iter, swap))
continue;

if (targetGraph.addEdge(node, iter, true))
Expand Down Expand Up @@ -175,7 +180,8 @@ public MultiTreeSPEntry[] calcPaths(int[] from, int[] to) {
throw new IllegalStateException("Edge-based behavior not supported");
}

outEdgeExplorer = graph.createOutEdgeExplorer();
outEdgeExplorer = swap ? graph.createInEdgeExplorer()
: graph.createOutEdgeExplorer();
runUpwardSearch();
if (!upwardEdgeFilter.isHighestNodeFound())
throw new IllegalStateException("First RPHAST phase was not successful.");
Expand Down Expand Up @@ -214,11 +220,10 @@ private void fillEdgesUpward(MultiTreeSPEntry currEdge, PriorityQueue<MultiTreeS
upwardEdgeFilter.setBaseNode(currEdge.getAdjNode());

while (iter.next()) {
if (!upwardEdgeFilter.accept(iter))
if (!upwardEdgeFilter.accept(iter, swap))
continue;

edgeWeight = iter.getWeight(false);
// edgeWeight = weighting.calcEdgeWeight(iter, false, 0);
edgeWeight = iter.getWeight(swap);

if (!Double.isInfinite(edgeWeight)) {
MultiTreeSPEntry ee = shortestWeightMap.get(iter.getAdjNode());
Expand Down Expand Up @@ -272,8 +277,7 @@ private void fillEdgesDownward(MultiTreeSPEntry currEdge, PriorityQueue<MultiTre
return;

while (iter.next()) {
// edgeWeight = weighting.calcEdgeWeight(iter, false, 0);
edgeWeight = iter.getWeight(false);
edgeWeight = iter.getWeight(swap);
if (!Double.isInfinite(edgeWeight)) {
MultiTreeSPEntry ee = bestWeightMap.get(iter.getAdjNode());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,22 @@
*/
package org.heigit.ors.routing.graphhopper.extensions.edgefilters.ch;

import com.graphhopper.routing.util.FlagEncoder;
import com.graphhopper.storage.CHEdgeFilter;
import com.graphhopper.storage.RoutingCHEdgeIteratorState;
import com.graphhopper.storage.RoutingCHGraph;
import org.heigit.ors.routing.graphhopper.extensions.util.GraphUtils;

public abstract class CHLevelEdgeFilter implements CHEdgeFilter {
protected final FlagEncoder encoder;
protected final RoutingCHGraph graph;
protected final int maxNodes;
protected int highestNode = -1;
protected int highestNodeLevel = -1;
protected int baseNode;
protected int baseNodeLevel = -1;

protected CHLevelEdgeFilter(RoutingCHGraph g, FlagEncoder encoder) {
protected CHLevelEdgeFilter(RoutingCHGraph g) {
graph = g;
maxNodes = GraphUtils.getBaseGraph(g).getNodes();
this.encoder = encoder;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,25 @@
*/
package org.heigit.ors.routing.graphhopper.extensions.edgefilters.ch;

import com.graphhopper.routing.ev.BooleanEncodedValue;
import com.graphhopper.routing.util.FlagEncoder;
import com.graphhopper.storage.RoutingCHEdgeIteratorState;
import com.graphhopper.storage.RoutingCHGraph;

public class DownwardSearchEdgeFilter extends CHLevelEdgeFilter {
protected final BooleanEncodedValue accessEnc;


public DownwardSearchEdgeFilter(RoutingCHGraph g, FlagEncoder encoder) {
super(g, encoder);
accessEnc = encoder.getAccessEnc();
public DownwardSearchEdgeFilter(RoutingCHGraph g) {
super(g);
}

@Override
public boolean accept(RoutingCHEdgeIteratorState edgeIterState) {
return accept(edgeIterState, false);
}

public boolean accept(RoutingCHEdgeIteratorState edgeIterState, boolean swap) {
int adj = edgeIterState.getAdjNode();

if (baseNode >= maxNodes || adj >= maxNodes || baseNodeLevel <= graph.getLevel(adj))
return isAccessible(edgeIterState, true);
// return edgeIterState.getReverse(accessEnc);
return isAccessible(edgeIterState, !swap);
else
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,25 @@
*/
package org.heigit.ors.routing.graphhopper.extensions.edgefilters.ch;

import com.graphhopper.routing.ev.BooleanEncodedValue;
import com.graphhopper.routing.util.FlagEncoder;
import com.graphhopper.storage.RoutingCHEdgeIteratorState;
import com.graphhopper.storage.RoutingCHGraph;

public class UpwardSearchEdgeFilter extends CHLevelEdgeFilter {
BooleanEncodedValue accessEnc;

public UpwardSearchEdgeFilter(RoutingCHGraph g, FlagEncoder encoder) {
super(g, encoder);
this.accessEnc = encoder.getAccessEnc();
public UpwardSearchEdgeFilter(RoutingCHGraph g) {
super(g);
}

@Override
public boolean accept(RoutingCHEdgeIteratorState edgeIterState) {
return accept(edgeIterState, false);
}

public boolean accept(RoutingCHEdgeIteratorState edgeIterState, boolean swap) {
int adj = edgeIterState.getAdjNode();

if (baseNode >= maxNodes || adj >= maxNodes || baseNodeLevel <= graph.getLevel(adj))
return isAccessible(edgeIterState, false);
// return edgeIterState.get(accessEnc);
return isAccessible(edgeIterState, swap);
else
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,19 @@
*/
package org.heigit.ors.routing.graphhopper.extensions.edgefilters.core;

import com.graphhopper.routing.util.FlagEncoder;
import com.graphhopper.storage.RoutingCHEdgeIteratorState;
import com.graphhopper.storage.RoutingCHGraph;
import org.heigit.ors.routing.graphhopper.extensions.edgefilters.ch.DownwardSearchEdgeFilter;

public class ExclusiveDownwardSearchEdgeFilter extends DownwardSearchEdgeFilter {
private boolean swap = false;

public ExclusiveDownwardSearchEdgeFilter(RoutingCHGraph g, FlagEncoder encoder) {
super(g, encoder);
public ExclusiveDownwardSearchEdgeFilter(RoutingCHGraph g) {
super(g);
}

public ExclusiveDownwardSearchEdgeFilter(RoutingCHGraph g, FlagEncoder encoder, boolean swap) {
this(g, encoder);
public ExclusiveDownwardSearchEdgeFilter(RoutingCHGraph g, boolean swap) {
this(g);
this.swap = swap;
}

Expand Down
Loading

0 comments on commit bffdfa5

Please sign in to comment.