Repository: graphhopper/graphhopper
Pull request #2241 (2021-02-17 16:58:29)

CustomModelParser can handle multiple areas
Solving issue #2240 
CustomModelParser used to create a Java file with a JsonFeature called feature. In case of multiple areas the JsonFeature was created twice.

The following is the code created by CustomModelParser.
  @Override
   public void init(EncodedValueLookup lookup, com.graphhopper.routing.ev.DecimalEncodedValue avgSpeedEnc, Map<String, com.graphhopper.util.JsonFeature> areas) {
this.avg_speed_enc = avgSpeedEnc;
JsonFeature feature = (JsonFeature) areas.get("milano");
this.in_area_milano = new Polygon(new PreparedGeometryFactory().create(feature.getGeometry()));
JsonFeature feature = (JsonFeature) areas.get("modena");
this.in_area_modena = new Polygon(new PreparedGeometryFactory().create(feature.getGeometry()));
if (lookup.hasEncodedValue("road_class")) this.road_class_enc = (EnumEncodedValue) lookup.getEncodedValue("road_class", EncodedValue.class);
   }

I added in CustomModelParser.createClassTemplate an id to the JsonFeature feature.
Now CustomModelParser create the following code:
 @Override
   public void init(EncodedValueLookup lookup, com.graphhopper.routing.ev.DecimalEncodedValue avgSpeedEnc, Map<String, com.graphhopper.util.JsonFeature> areas) {
this.avg_speed_enc = avgSpeedEnc;
JsonFeature feature_milano = (JsonFeature) areas.get("milano");
this.in_area_milano = new Polygon(new PreparedGeometryFactory().create(feature_milano.getGeometry()));
JsonFeature feature_modena = (JsonFeature) areas.get("modena");
this.in_area_modena = new Polygon(new PreparedGeometryFactory().create(feature_modena.getGeometry()));
if (lookup.hasEncodedValue("road_class")) this.road_class_enc = (EnumEncodedValue) lookup.getEncodedValue("road_class", EncodedValue.class);
   }

karussell: Thanks a lot for this!

Can you add yourself also to the [contributing doc](https://github.com/graphhopper/graphhopper/blob/master/CONTRIBUTORS.md)?
GProbo: > Thanks a lot for this!
> 
> Can you add yourself also to the [contributing doc](https://github.com/graphhopper/graphhopper/blob/master/CONTRIBUTORS.md)?

Done!
karussell: Thanks! And sorry that I did not mention it but the list is sorted :) ... can you move it to the correct place? (this way a human lookup is faster ;) )
GProbo: Sorry! My bad! 


Patch:
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index f68c5d144..cba77cb13 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -32,7 +32,8 @@ Here is an overview:
  * elibar, fix for alternative route calculation
  * fbonzon, several UI improvements like #615
  * florent-morel, improvements regarding fords, #320
- * fredao, translations 
+ * fredao, translations
+ * GProbo, fixes like #2241
  * HarelM, improvements regarding elevation
  * HelgeKrueger, modularization of javascript, #590
  * henningvs, doc improvements
diff --git a/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomModelParser.java b/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomModelParser.java
index ebf630cf5..2e08d67cd 100644
--- a/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomModelParser.java
+++ b/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomModelParser.java
@@ -259,8 +259,8 @@ public class CustomModelParser {
                 if (!customModel.getAreas().containsKey(id))
                     throw new IllegalArgumentException("Area '" + id + "' wasn't found");
                 classSourceCode.append("protected " + Polygon.class.getSimpleName() + " " + arg + ";\n");
-                initSourceCode.append("JsonFeature feature = (JsonFeature) areas.get(\"" + id + "\");\n");
-                initSourceCode.append("this." + arg + " = new Polygon(new PreparedGeometryFactory().create(feature.getGeometry()));\n");
+                initSourceCode.append("JsonFeature feature_" + id + " = (JsonFeature) areas.get(\"" + id + "\");\n");
+                initSourceCode.append("this." + arg + " = new Polygon(new PreparedGeometryFactory().create(feature_" + id + ".getGeometry()));\n");
             } else {
                 if (!isValidVariableName(arg))
                     throw new IllegalArgumentException("Variable not supported: " + arg);


Test patch:
diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomModelParserTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomModelParserTest.java
index baac82f32..c48619ee3 100644
--- a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomModelParserTest.java
+++ b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomModelParserTest.java
@@ -20,21 +20,31 @@ package com.graphhopper.routing.weighting.custom;
 
 import com.graphhopper.routing.ev.*;
 import com.graphhopper.routing.util.CarFlagEncoder;
+import com.graphhopper.routing.util.DefaultFlagEncoderFactory;
 import com.graphhopper.routing.util.EncodingManager;
+import com.graphhopper.routing.weighting.TurnCostProvider;
 import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.GraphBuilder;
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.util.CustomModel;
 import com.graphhopper.util.EdgeIteratorState;
+import com.graphhopper.util.JsonFeature;
+import com.graphhopper.util.PMap;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Envelope;
+import org.locationtech.jts.geom.GeometryFactory;
+
+import java.util.HashMap;
+import java.util.Map;
 
 import static com.graphhopper.json.Statement.*;
 import static com.graphhopper.json.Statement.Op.LIMIT;
 import static com.graphhopper.json.Statement.Op.MULTIPLY;
 import static com.graphhopper.routing.ev.RoadClass.*;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.*;
 
 class CustomModelParserTest {
 
@@ -173,4 +183,56 @@ class CustomModelParserTest {
         customModel.addToSpeed(Else(LIMIT, 100));
         assertEquals(100, CustomModelParser.findMaxSpeed(customModel, 120));
     }
+
+    @Test
+    public void multipleAreas(){
+        CustomModel customModel = new CustomModel();
+        Map<String, JsonFeature> areas = new HashMap<>();
+        Coordinate[] area_1_coordinates = new Coordinate[]{
+                new Coordinate(48.019324184801185, 11.28021240234375),
+                new Coordinate(48.019324184801185, 11.53564453125),
+                new Coordinate(48.11843396091691, 11.53564453125),
+                new Coordinate(48.11843396091691, 11.28021240234375),
+                new Coordinate(48.019324184801185, 11.28021240234375),
+        };
+        Coordinate[] area_2_coordinates = new Coordinate[]{
+                new Coordinate(48.15509285476017, 11.53289794921875),
+                new Coordinate(48.15509285476017, 11.8212890625),
+                new Coordinate(48.281365151571755, 11.8212890625),
+                new Coordinate(48.281365151571755, 11.53289794921875),
+                new Coordinate(48.15509285476017, 11.53289794921875),
+        };
+        areas.put("area_1", new JsonFeature("area_1",
+                "Feature",
+                new Envelope(),
+                new GeometryFactory().createPolygon(area_1_coordinates),
+                new HashMap<>()));
+        areas.put("area_2", new JsonFeature("area_2",
+                "Feature",
+                new Envelope(),
+                new GeometryFactory().createPolygon(area_2_coordinates),
+                new HashMap<>()));
+        customModel.setAreas(areas);
+
+        customModel.addToSpeed(If("in_area_area_1", LIMIT, 100));
+        customModel.addToSpeed(If("in_area_area_2", LIMIT, 25));
+        customModel.addToSpeed(Else(LIMIT, 15));
+
+        // No exception is thrown during createWeightingParameters
+        assertAll(() ->
+                CustomModelParser.createWeightingParameters(customModel, encodingManager,
+                        encoder.getMaxSpeed(), avgSpeedEnc));
+
+        CustomModel customModel2 = new CustomModel();
+        customModel2.setAreas(areas);
+
+        customModel2.addToSpeed(If("in_area_area_1", LIMIT, 100));
+        customModel2.addToSpeed(If("in_area_area_2", LIMIT, 25));
+        customModel2.addToSpeed(If("in_area_area_3", LIMIT, 150));
+        customModel2.addToSpeed(Else(LIMIT, 15));
+
+        assertThrows(IllegalArgumentException.class, () ->
+                CustomModelParser.createWeightingParameters(customModel2, encodingManager,
+                        encoder.getMaxSpeed(), avgSpeedEnc));
+    }
 }
\ No newline at end of file