Repository: graphhopper/graphhopper
Pull request #2073 (2020-06-28 12:04:06)

Provide edge key PathDetails
Because edge key is at least as interesting as edge id.

michaz: > the only downside here but also for edge_id is that we still have no ID stability

True! But my customer has control over the import, and deliberately creates edges in a certain order. So we know which edge is which.

Patch:
diff --git a/api/src/main/java/com/graphhopper/util/Parameters.java b/api/src/main/java/com/graphhopper/util/Parameters.java
index 5726f8b08..99c01c715 100644
--- a/api/src/main/java/com/graphhopper/util/Parameters.java
+++ b/api/src/main/java/com/graphhopper/util/Parameters.java
@@ -207,6 +207,7 @@ public class Parameters {
         public static final String AVERAGE_SPEED = "average_speed";
         public static final String STREET_NAME = "street_name";
         public static final String EDGE_ID = "edge_id";
+        public static final String EDGE_KEY = "edge_key";
         public static final String TIME = "time";
         public static final String WEIGHT = "weight";
         public static final String DISTANCE = "distance";
diff --git a/core/src/main/java/com/graphhopper/util/details/EdgeKeyDetails.java b/core/src/main/java/com/graphhopper/util/details/EdgeKeyDetails.java
new file mode 100644
index 000000000..83cfc3a8a
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/util/details/EdgeKeyDetails.java
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to GraphHopper GmbH under one or more contributor
+ *  license agreements. See the NOTICE file distributed with this work for
+ *  additional information regarding copyright ownership.
+ *
+ *  GraphHopper GmbH 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.util.details;
+
+import com.graphhopper.routing.querygraph.VirtualEdgeIteratorState;
+import com.graphhopper.util.EdgeIteratorState;
+import com.graphhopper.util.GHUtility;
+
+import static com.graphhopper.util.Parameters.Details.EDGE_KEY;
+
+public class EdgeKeyDetails extends AbstractPathDetailsBuilder {
+
+    private int edgeKey;
+
+    public EdgeKeyDetails() {
+        super(EDGE_KEY);
+        edgeKey = -1;
+    }
+
+    @Override
+    public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) {
+        int newEdgeKey = getEdgeKey(edge);
+        if (newEdgeKey != edgeKey) {
+            edgeKey = newEdgeKey;
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public Object getCurrentValue() {
+        return this.edgeKey;
+    }
+
+    static int getEdgeKey(EdgeIteratorState edge) {
+        if (edge instanceof VirtualEdgeIteratorState) {
+            return ((VirtualEdgeIteratorState) edge).getOriginalEdgeKey();
+        } else {
+            return edge.getEdge() * 2 + (edge.get(EdgeIteratorState.REVERSE_STATE) ? 1 : 0);
+        }
+    }
+
+}
diff --git a/core/src/main/java/com/graphhopper/util/details/PathDetailsBuilderFactory.java b/core/src/main/java/com/graphhopper/util/details/PathDetailsBuilderFactory.java
index dd94dc60c..2c46eaab9 100644
--- a/core/src/main/java/com/graphhopper/util/details/PathDetailsBuilderFactory.java
+++ b/core/src/main/java/com/graphhopper/util/details/PathDetailsBuilderFactory.java
@@ -48,6 +48,9 @@ public class PathDetailsBuilderFactory {
         if (requestedPathDetails.contains(EDGE_ID))
             builders.add(new EdgeIdDetails());
 
+        if (requestedPathDetails.contains(EDGE_KEY))
+            builders.add(new EdgeKeyDetails());
+
         if (requestedPathDetails.contains(TIME))
             builders.add(new TimeDetails(weighting));
 


Test patch:
diff --git a/core/src/test/java/com/graphhopper/routing/PathTest.java b/core/src/test/java/com/graphhopper/routing/PathTest.java
index c7e174194..8ff35b171 100644
--- a/core/src/test/java/com/graphhopper/routing/PathTest.java
+++ b/core/src/test/java/com/graphhopper/routing/PathTest.java
@@ -381,6 +381,40 @@ public class PathTest {
         assertEquals(4, edgeIdDetails.get(3).getLast());
     }
 
+    @Test
+    public void testCalcEdgeKeyDetailsForward() {
+        ShortestWeighting weighting = new ShortestWeighting(encoder);
+        Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(1, 5);
+        assertTrue(p.isFound());
+
+        Map<String, List<PathDetail>> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting,
+                Arrays.asList(EDGE_KEY), new PathDetailsBuilderFactory(), 0);
+        List<PathDetail> edgeKeyDetails = details.get(EDGE_KEY);
+
+        assertEquals(4, edgeKeyDetails.size());
+        assertEquals(0, edgeKeyDetails.get(0).getValue());
+        assertEquals(4, edgeKeyDetails.get(1).getValue());
+        assertEquals(6, edgeKeyDetails.get(2).getValue());
+        assertEquals(2, edgeKeyDetails.get(3).getValue());
+    }
+
+    @Test
+    public void testCalcEdgeKeyDetailsBackward() {
+        ShortestWeighting weighting = new ShortestWeighting(encoder);
+        Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(5, 1);
+        assertTrue(p.isFound());
+
+        Map<String, List<PathDetail>> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting,
+                Arrays.asList(EDGE_KEY), new PathDetailsBuilderFactory(), 0);
+        List<PathDetail> edgeKeyDetails = details.get(EDGE_KEY);
+
+        assertEquals(4, edgeKeyDetails.size());
+        assertEquals(3, edgeKeyDetails.get(0).getValue());
+        assertEquals(7, edgeKeyDetails.get(1).getValue());
+        assertEquals(5, edgeKeyDetails.get(2).getValue());
+        assertEquals(1, edgeKeyDetails.get(3).getValue());
+    }
+
     @Test
     public void testCalcTimeDetails() {
         ShortestWeighting weighting = new ShortestWeighting(encoder);