Skip to content

Commit f2c5e66

Browse files
authored
make custom areas available in custom models (graphhopper#2725)
* make it possible to use all custom areas in all custom models * minor comment regarding ID
1 parent 4dca77d commit f2c5e66

File tree

6 files changed

+110
-24
lines changed

6 files changed

+110
-24
lines changed

config-example.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ graphhopper:
4545
# weighting: custom
4646
# # the custom model in bike2.json is defined to avoid hills
4747
# custom_model_file: bike2.json
48+
49+
# specify the folder where to find the custom model files
4850
custom_model_folder: custom_models
4951

5052
# Speed mode:
@@ -190,7 +192,8 @@ graphhopper:
190192
#### Custom Areas ####
191193

192194
# GraphHopper reads GeoJSON polygon files including their properties from this directory and makes them available
193-
# to all tag parsers and vehicles. Country borders (see countries.geojson) are always included automatically.
195+
# to all tag parsers, vehicles and custom models. All GeoJSON Features require to have the "id" property.
196+
# Country borders are included automatically (see countries.geojson).
194197
# custom_areas.directory: path/to/custom_areas
195198

196199

core/src/main/java/com/graphhopper/reader/osm/OSMReader.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ protected void setArtificialWayTags(PointList pointList, ReaderWay way, double d
262262
Country country = Country.MISSING;
263263
CustomArea prevCustomArea = null;
264264
for (CustomArea customArea : customAreas) {
265+
if (customArea.getProperties() == null) continue;
265266
Object alpha3 = customArea.getProperties().get(Country.ISO_ALPHA3);
266267
if (alpha3 == null)
267268
continue;

core/src/main/java/com/graphhopper/util/GHUtility.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,7 @@ public static List<CustomArea> readCountries() {
720720
public static CustomArea getFirstDuplicateArea(List<CustomArea> areas, String id) {
721721
Set<String> result = new HashSet<>(areas.size());
722722
for (CustomArea area : areas) {
723+
if (area.getProperties() == null) continue;
723724
String countryCode = (String) area.getProperties().get(id);
724725
// in our country file there are not only countries but "subareas" (with ISO3166-2) or other unnamed areas
725726
// like Metropolitan Netherlands

web-bundle/src/main/java/com/graphhopper/http/GraphHopperManaged.java

Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
package com.graphhopper.http;
2020

21+
import com.bedatadriven.jackson.datatype.jts.JtsModule;
2122
import com.fasterxml.jackson.databind.ObjectMapper;
2223
import com.graphhopper.GraphHopper;
2324
import com.graphhopper.GraphHopperConfig;
@@ -28,11 +29,20 @@
2829
import com.graphhopper.routing.weighting.custom.CustomWeighting;
2930
import com.graphhopper.util.CustomModel;
3031
import com.graphhopper.util.Helper;
32+
import com.graphhopper.util.JsonFeature;
33+
import com.graphhopper.util.JsonFeatureCollection;
3134
import io.dropwizard.lifecycle.Managed;
3235
import org.slf4j.Logger;
3336
import org.slf4j.LoggerFactory;
3437

38+
import java.io.BufferedReader;
3539
import java.io.File;
40+
import java.io.IOException;
41+
import java.io.UncheckedIOException;
42+
import java.nio.charset.StandardCharsets;
43+
import java.nio.file.DirectoryStream;
44+
import java.nio.file.Files;
45+
import java.nio.file.Path;
3646
import java.nio.file.Paths;
3747
import java.util.ArrayList;
3848
import java.util.List;
@@ -50,13 +60,29 @@ public GraphHopperManaged(GraphHopperConfig configuration) {
5060
}
5161

5262
String customModelFolder = configuration.getString("custom_model_folder", "");
53-
List<Profile> newProfiles = resolveCustomModelFiles(customModelFolder, configuration.getProfiles());
63+
String customAreasDirectory = configuration.getString("custom_areas.directory", "");
64+
JsonFeatureCollection globalAreas = new JsonFeatureCollection();
65+
if (!customAreasDirectory.isEmpty()) {
66+
ObjectMapper mapper = new ObjectMapper().registerModule(new JtsModule());
67+
try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get(customAreasDirectory), "*.{geojson,json}")) {
68+
for (Path customAreaFile : stream) {
69+
try (BufferedReader reader = Files.newBufferedReader(customAreaFile, StandardCharsets.UTF_8)) {
70+
globalAreas.getFeatures().addAll(mapper.readValue(reader, JsonFeatureCollection.class).getFeatures());
71+
}
72+
}
73+
logger.info("Will make " + globalAreas.getFeatures().size() + " areas available to all custom profiles. Found in " + customAreasDirectory);
74+
} catch (IOException e) {
75+
throw new UncheckedIOException(e);
76+
}
77+
}
78+
79+
List<Profile> newProfiles = resolveCustomModelFiles(customModelFolder, configuration.getProfiles(), globalAreas);
5480
configuration.setProfiles(newProfiles);
5581

5682
graphHopper.init(configuration);
5783
}
5884

59-
public static List<Profile> resolveCustomModelFiles(String customModelFolder, List<Profile> profiles) {
85+
public static List<Profile> resolveCustomModelFiles(String customModelFolder, List<Profile> profiles, JsonFeatureCollection globalAreas) {
6086
ObjectMapper jsonOM = Jackson.newObjectMapper();
6187
List<Profile> newProfiles = new ArrayList<>();
6288
for (Profile profile : profiles) {
@@ -65,37 +91,47 @@ public static List<Profile> resolveCustomModelFiles(String customModelFolder, Li
6591
continue;
6692
}
6793
Object cm = profile.getHints().getObject("custom_model", null);
94+
CustomModel customModel;
6895
if (cm != null) {
6996
try {
7097
// custom_model can be an object tree (read from config) or an object (e.g. from tests)
71-
CustomModel customModel = jsonOM.readValue(jsonOM.writeValueAsBytes(cm), CustomModel.class);
98+
customModel = jsonOM.readValue(jsonOM.writeValueAsBytes(cm), CustomModel.class);
7299
newProfiles.add(new CustomProfile(profile).setCustomModel(customModel));
73-
continue;
74100
} catch (Exception ex) {
75101
throw new RuntimeException("Cannot load custom_model from " + cm + " for profile " + profile.getName()
76102
+ ". If you are trying to load from a file, use 'custom_model_file' instead.", ex);
77103
}
78-
}
79-
String customModelFileName = profile.getHints().getString("custom_model_file", "");
80-
if (customModelFileName.isEmpty())
81-
throw new IllegalArgumentException("Missing 'custom_model' or 'custom_model_file' field in profile '"
82-
+ profile.getName() + "'. To use default specify custom_model_file: empty");
83-
if ("empty".equals(customModelFileName))
84-
newProfiles.add(new CustomProfile(profile).setCustomModel(new CustomModel()));
85-
else {
86-
if (customModelFileName.contains(File.separator))
87-
throw new IllegalArgumentException("Use custom_model_folder for the custom_model_file parent");
88-
if (!customModelFileName.endsWith(".json"))
89-
throw new IllegalArgumentException("Yaml is no longer supported, see #2672. Use JSON with optional comments //");
90-
try {
91-
// Somehow dropwizard makes it very hard to find out the folder of config.yml -> use an extra parameter for the folder
92-
String string = Helper.readJSONFileWithoutComments(Paths.get(customModelFolder).resolve(customModelFileName).toFile().getAbsolutePath());
93-
CustomModel customModel = jsonOM.readValue(string, CustomModel.class);
94-
newProfiles.add(new CustomProfile(profile).setCustomModel(customModel));
95-
} catch (Exception ex) {
96-
throw new RuntimeException("Cannot load custom_model from location " + customModelFileName + " for profile " + profile.getName(), ex);
104+
} else {
105+
String customModelFileName = profile.getHints().getString("custom_model_file", "");
106+
if (customModelFileName.isEmpty())
107+
throw new IllegalArgumentException("Missing 'custom_model' or 'custom_model_file' field in profile '"
108+
+ profile.getName() + "'. To use default specify custom_model_file: empty");
109+
if ("empty".equals(customModelFileName))
110+
newProfiles.add(new CustomProfile(profile).setCustomModel(customModel = new CustomModel()));
111+
else {
112+
if (customModelFileName.contains(File.separator))
113+
throw new IllegalArgumentException("Use custom_model_folder for the custom_model_file parent");
114+
if (!customModelFileName.endsWith(".json"))
115+
throw new IllegalArgumentException("Yaml is no longer supported, see #2672. Use JSON with optional comments //");
116+
try {
117+
// Somehow dropwizard makes it very hard to find out the folder of config.yml -> use an extra parameter for the folder
118+
String string = Helper.readJSONFileWithoutComments(Paths.get(customModelFolder).resolve(customModelFileName).toFile().getAbsolutePath());
119+
customModel = jsonOM.readValue(string, CustomModel.class);
120+
newProfiles.add(new CustomProfile(profile).setCustomModel(customModel));
121+
} catch (Exception ex) {
122+
throw new RuntimeException("Cannot load custom_model from location " + customModelFileName + " for profile " + profile.getName(), ex);
123+
}
97124
}
98125
}
126+
127+
// we can fill in all areas here as in the created template we include only the areas that are used in statements (see CustomModelParser)
128+
for (JsonFeature feature : globalAreas.getFeatures()) {
129+
if (customModel.getAreas().containsKey(feature.getId()))
130+
throw new IllegalArgumentException("The area '" + feature.getId() + "' in profile configuration '"
131+
+ profile.getName() + "' cannot have same ID as global area.");
132+
else
133+
customModel.getAreas().put(feature.getId(), feature);
134+
}
99135
}
100136
return newProfiles;
101137
}

web/src/test/java/com/graphhopper/application/resources/RouteResourceCustomModelTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,13 @@ private static GraphHopperServerConfiguration createConfig() {
6464
putObject("graph.location", DIR).
6565
putObject("graph.encoded_values", "max_height,max_weight,max_width,hazmat,toll,surface,track_type,hgv").
6666
putObject("custom_model_folder", "./src/test/resources/com/graphhopper/application/resources").
67+
putObject("custom_areas.directory", "./src/test/resources/com/graphhopper/application/resources/areas").
6768
putObject("import.osm.ignored_highways", "").
6869
setProfiles(Arrays.asList(
6970
new Profile("wheelchair"),
7071
new CustomProfile("roads").setCustomModel(new CustomModel()).setVehicle("roads"),
7172
new CustomProfile("car").setCustomModel(new CustomModel().setDistanceInfluence(70d)).setVehicle("car"),
73+
new CustomProfile("car_with_area").setCustomModel(new CustomModel().addToPriority(If("in_external_area52", MULTIPLY, "0.05"))),
7274
new CustomProfile("bike").setCustomModel(new CustomModel().setDistanceInfluence(0d)).setVehicle("bike"),
7375
new Profile("bike_fastest").setWeighting("fastest").setVehicle("bike"),
7476
new CustomProfile("bus").setVehicle("roads").putHint("custom_model_file", "bus.json"),
@@ -192,6 +194,12 @@ public void testAvoidArea(double priority, double expectedDistance) {
192194
assertEquals(expectedDistance, path.get("distance").asDouble(), 10);
193195
}
194196

197+
@Test
198+
public void testAvoidArea() {
199+
JsonNode path = getPath("{\"points\": [[11.58199, 50.0141], [11.5865, 50.0095]], \"profile\": \"car_with_area\", \"ch.disable\": true, \"custom_model\": {}}");
200+
assertEquals(path.get("distance").asDouble(), 3073, 10);
201+
}
202+
195203
@Test
196204
public void testCargoBike() throws IOException {
197205
String body = "{\"points\": [[11.58199, 50.0141], [11.5865, 50.0095]], \"profile\": \"bike\", \"custom_model\": {}, \"ch.disable\": true}";
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"type": "FeatureCollection",
3+
"features": [
4+
{
5+
"id": "external_area52",
6+
"type": "Feature",
7+
"geometry": {
8+
"type": "Polygon",
9+
"coordinates": [
10+
[
11+
[
12+
11.5818,
13+
50.0126
14+
],
15+
[
16+
11.5818,
17+
50.0119
18+
],
19+
[
20+
11.5861,
21+
50.0119
22+
],
23+
[
24+
11.5861,
25+
50.0126
26+
],
27+
[
28+
11.5818,
29+
50.0126
30+
]
31+
]
32+
]
33+
}
34+
}
35+
]
36+
}
37+

0 commit comments

Comments
 (0)