Repository: graphhopper/graphhopper
Pull request #2317 (2021-05-12 21:26:26)

Add lanes encoded value

easbar: Maybe we can resurrect https://github.com/graphhopper/graphhopper/pull/1269 as a follow-up to this :)

Patch:
diff --git a/config-example.yml b/config-example.yml
index 91be0e735..bf3afd9c6 100644
--- a/config-example.yml
+++ b/config-example.yml
@@ -18,7 +18,7 @@ graphhopper:
   # Add additional information to every edge. Used for path details (#1548), better instructions (#1844) and tunnel/bridge interpolation (#798).
   # Default values are: road_class,road_class_link,road_environment,max_speed,road_access (since #1805)
   # More are: surface,max_width,max_height,max_weight,max_axle_load,max_length,hazmat,hazmat_tunnel,hazmat_water,toll,track_type,
-  #           mtb_rating, hiking_rating, horse_rating
+  #           mtb_rating,hiking_rating,horse_rating,lanes
   # graph.encoded_values: surface,toll,track_type
 
   ##### Routing Profiles ####
diff --git a/core/src/main/java/com/graphhopper/routing/ev/Lanes.java b/core/src/main/java/com/graphhopper/routing/ev/Lanes.java
new file mode 100644
index 000000000..f3e3c7510
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/routing/ev/Lanes.java
@@ -0,0 +1,27 @@
+/*
+ *  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.routing.ev;
+
+public class Lanes {
+    public static final String KEY = "lanes";
+
+    public static IntEncodedValue create() {
+        return new UnsignedIntEncodedValue(KEY, 3, false);
+    }
+}
diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/DefaultTagParserFactory.java b/core/src/main/java/com/graphhopper/routing/util/parsers/DefaultTagParserFactory.java
index 7d705a2f0..32f589a25 100644
--- a/core/src/main/java/com/graphhopper/routing/util/parsers/DefaultTagParserFactory.java
+++ b/core/src/main/java/com/graphhopper/routing/util/parsers/DefaultTagParserFactory.java
@@ -64,6 +64,8 @@ public class DefaultTagParserFactory implements TagParserFactory {
             return new OSMHazmatTunnelParser();
         else if (name.equals(HazmatWater.KEY))
             return new OSMHazmatWaterParser();
+        else if (name.equals(Lanes.KEY))
+            return new OSMLanesParser();
         else if (name.equals(MtbRating.KEY))
             return new OSMMtbRatingParser();
         else if (name.equals(HikeRating.KEY))
diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMLanesParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMLanesParser.java
new file mode 100644
index 000000000..750a4ac62
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMLanesParser.java
@@ -0,0 +1,69 @@
+/*
+ *  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.routing.util.parsers;
+
+import com.graphhopper.reader.ReaderWay;
+import com.graphhopper.routing.ev.EncodedValue;
+import com.graphhopper.routing.ev.EncodedValueLookup;
+import com.graphhopper.routing.ev.IntEncodedValue;
+import com.graphhopper.routing.ev.Lanes;
+import com.graphhopper.storage.IntsRef;
+
+import java.util.List;
+
+/**
+ * https://wiki.openstreetmap.org/wiki/Key:lanes
+ */
+public class OSMLanesParser implements TagParser {
+    private final IntEncodedValue lanesEnc;
+
+    public OSMLanesParser() {
+        this.lanesEnc = Lanes.create();
+    }
+
+    @Override
+    public void createEncodedValues(EncodedValueLookup lookup, List<EncodedValue> registerNewEncodedValue) {
+        registerNewEncodedValue.add(lanesEnc);
+    }
+
+    @Override
+    public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, boolean ferry, IntsRef relationFlags) {
+        int laneCount = 1;
+        if (way.hasTag("lanes")) {
+            String noLanes = way.getTag("lanes");
+            String[] noLanesTok = noLanes.split(";|\\.");
+            if (noLanesTok.length > 0) {
+                try {
+                    int noLanesInt = Integer.parseInt(noLanesTok[0]);
+                    // there was a proposal with negative lanes but I cannot find it
+                    if (noLanesInt < 0)
+                        laneCount = 1;
+                    else if (noLanesInt > 6)
+                        laneCount = 6;
+                    else
+                        laneCount = noLanesInt;
+                } catch (NumberFormatException ex) {
+                    // ignore if no number
+                }
+            }
+        }
+        lanesEnc.setInt(false, edgeFlags, laneCount);
+        return edgeFlags;
+    }
+}
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 8880aac82..7392a8eaf 100644
--- a/core/src/main/java/com/graphhopper/util/details/PathDetailsBuilderFactory.java
+++ b/core/src/main/java/com/graphhopper/util/details/PathDetailsBuilderFactory.java
@@ -81,7 +81,7 @@ public class PathDetailsBuilderFactory {
                 builders.add(new EnumDetails<>(key, evl.getEnumEncodedValue(key, Enum.class)));
         }
 
-        for (String key : Arrays.asList(MtbRating.KEY, HikeRating.KEY, HorseRating.KEY)) {
+        for (String key : Arrays.asList(MtbRating.KEY, HikeRating.KEY, HorseRating.KEY, Lanes.KEY)) {
             if (requestedPathDetails.contains(key) && evl.hasEncodedValue(key))
                 builders.add(new IntDetails(key, evl.getIntEncodedValue(key)));
         }


Test patch:
diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMLanesParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMLanesParserTest.java
new file mode 100644
index 000000000..f58710da4
--- /dev/null
+++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMLanesParserTest.java
@@ -0,0 +1,57 @@
+/*
+ *  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.routing.util.parsers;
+
+import com.graphhopper.reader.ReaderWay;
+import com.graphhopper.routing.ev.Lanes;
+import com.graphhopper.routing.util.EncodingManager;
+import com.graphhopper.storage.IntsRef;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class OSMLanesParserTest {
+
+    private OSMLanesParser parser;
+    private EncodingManager em;
+
+    @BeforeEach
+    void setup() {
+        parser = new OSMLanesParser();
+        em = new EncodingManager.Builder().add(parser).build();
+    }
+
+    @Test
+    void basic() {
+        ReaderWay readerWay = new ReaderWay(1);
+        IntsRef intsRef = em.createEdgeFlags();
+        readerWay.setTag("lanes", "4");
+        parser.handleWayTags(intsRef, readerWay, false, em.createRelationFlags());
+        Assertions.assertEquals(4, em.getIntEncodedValue(Lanes.KEY).getInt(false, intsRef));
+    }
+
+    @Test
+    void notTagged() {
+        ReaderWay readerWay = new ReaderWay(1);
+        IntsRef intsRef = em.createEdgeFlags();
+        parser.handleWayTags(intsRef, readerWay, false, em.createRelationFlags());
+        Assertions.assertEquals(1, em.getIntEncodedValue(Lanes.KEY).getInt(false, intsRef));
+    }
+
+}
\ No newline at end of file