@karussell We are aware that we theoretically can do everything in java, but we don’t want to. We want to be able to configure the routers at start time rather than hardcoding a number profiles, and we would like to be able to use configuration syntax which matches those that you officially document, rather than having to roll our own config layer there and trying to keep it in sync with GH upstream.
@easbar our current setup looks close to what you suggest, and what we are doing appears to work nicely when we simply specify profiles that use custom_models_file: []
:
// Create object mapper for reading the GH config file
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
objectMapper.registerModule(new GraphHopperConfigModule());
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
// load primary config
GraphHopperConfig graphHopperConfig = objectMapper.readValue(configStream, GraphHopperConfig.class);
The whole creation of the hopper object, incl. init()
and importOrLoad()
is happening later. For now I am concerned with correctly deserialising the config.
Now when I use the above code to try loading the following file, where I am specifying an explicit custom_model
instead of custom_model_files: []
and which should be well-formed based on your documentation, I am getting some kind of jackson deserialisation exception, that I am unsure what to do with. Note that this is all done in GH 8.0:
##### Routing Profiles ####
graph.elevation.provider: srtm
profiles:
- name: bike
vehicle: bike
custom_model: {
"speed": [
{
"if": "average_slope >= 8",
"multiply_by": "0.8"
},
{
"else_if": "average_slope >= 4",
"multiply_by": "0.9"
},
{
"else_if": "average_slope <= -4",
"multiply_by": "1.1"
},
{
"if": "average_slope >= 12",
"limit_to": "6"
},
{
"if": "average_slope >= 15",
"limit_to": "3"
}
]
}
profiles_ch:
- profile: bike
prepare.min_network_size: 200
##### Path Details ######
graph.encoded_values: surface,average_slope,max_slope
##### Storage #####
import.osm.ignored_highways: motorway,trunk
graph.dataaccess: RAM_STORE
Exception (the class GraphHopperBridgeFactory
is custom stuff from us and handles router configuraiton and creation):
[ERROR] 2024-09-05 13:43:30,067 GraphHopperBridgeFactory:73: Failed to load specified GH config file, returning null
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.graphhopper.json.Statement` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (BufferedInputStream); line: 15, column: 11] (through reference chain: com.graphhopper.GraphHopperConfig["profiles"]->java.util.ArrayList[0]->com.graphhopper.config.Profile["custom_model"]->com.graphhopper.util.CustomModel["speed"]->java.util.ArrayList[0])
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1887)
at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:414)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1375)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1508)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:348)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:361)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:274)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:30)
at com.fasterxml.jackson.databind.deser.impl.SetterlessProperty.deserializeAndSet(SetterlessProperty.java:134)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:310)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:310)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:361)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:246)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:30)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:310)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4905)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3885)
at com.ioki.mac.ktm.core.routing.GraphHopperBridgeFactory.loadConfig(GraphHopperBridgeFactory.java:156)
What am I missing here … it looks like the readValue
thing is already trying to also deserialize the JSON model directly, rather than storing it as a string for later. While from your source code, it looks like the GraphHopper
class itself rather expects to find a string under the custom_model
key in its config class, which is then deserialised internally (as part of the init()
call, I think) …