Flexible / Configurable Weighting Update

Changing it to weighting: custom worked - thanks.

Another question…is it possible to add conditional adjustments to priority, e.g.

priority: surface: {other: 0.0}

…but only if not a road?

Conditional adjustments are not possible yet. What do you mean with “not a road”? Like in terms of road_environment i.e. a tunnel or ferry? Can you explain your use case (maybe there is another solution)?

I was thinking of a “road bike” profile that avoids all unpaved surfaces completely, with something along the following lines:

priority:
surface: {
paving_stones: 0.0,
cobblestone: 0.0,
unpaved: 0.0,
gravel: 0.0,
fine_gravel: 0.0,
dirt: 0.0,
grass: 0.0,
sand: 0.0,
ground: 0.0,
other: 0.0
}

However, usage of the surface tag can sometimes be patchy in OSM, so there are some roads with no surface type added which would be blocked by “other: 0.0”. It would be useful to be able to make some assumptions that certain classes of roads are very likely to be paved and therefore only apply the surface restrictions conditionally.

And would it help if you would have a new without entry for surface that means “without a surface tag” (probably the most roads would have this entry then) and then other would only contain roads that have a surface tag but are not explicitly listed.

I don’t think a “without” option would help in the use case I’m looking at right now but I’m sure this would be helpful for other scenarios.

Unfortunately there are lots of roads without a surface tag - even Primary roads (e.g.Guildford Road here: https://www.openstreetmap.org/search?query=51.2770%2C-0.3735#map=16/51.2770/-0.3735), so it wouldn’t be reliable to consider them on the basis of surface alone. Ideally I’d like to be able to say…include all Primary roads regardless of surface, but only include more minor roads if they meet the surface test, or something along those lines.

Then I do not fully understand your usecase.

Unfortunately there are lots of roads without a surface tag

Yes, the most roads have no surface tag.

include all Primary roads regardless of surface, but only include more minor roads if they meet the surface test, or something along those lines.

Let’s say we introduce a without option for surface, then this would mean that other would not include roads without a surface tag. So when you exclude all unpaved sufaces and also other then this might be what you are after because you are no longer excluding roads without surface tag.

But I would need to also exclude “without” as well as “other” otherwise it would result in routes on paths and unclassified roads that don’t have a surface tag, which could very well could be unpaved.

I’m trying to find a way to avoid any path or road which is definitely unpaved but also allow any where the surface isn’t known but there is a very high likelihood of it being paved (e.g. primary roads), without blocking paths and minor roads that have a paved surface tag.

Maybe there is a way to combine surface and road_class priorities to achieve this?

Yes, you can combine them:

priority: {
  surface: { unpaved: 0, other: 0 },
  road_class: { unclassified: 0 }
}

It will use the priority 0 if a surface tag is unpaved or other, or if the road_class is unclassified. In fact it multiplies the priorities.

Wouldn’t that block unclassified roads that are paved though? As well as blocking any other roads that are missing a surface tag?

My aim is to allow roads and paths that are very likely to be paved but block roads and paths that are definitely not paved. I don’t think that’s possible as it currently stands, as I think this would need conditional priority rules.

At the moment yes, but that was the reason I asked if it would help if we feed untagged roads into a separate without value (or call it missing).

Wouldn’t that block unclassified roads that are paved though?

Either you want to block all unclassified roads (then you do it like I proposed with the two entries) or you want to block all unpaved roads (then there is no need for the second entry).

So wouldn’t this be what you are looking for:

priority: {
  surface: { unpaved: 0, other: 0, missing: 1, paved: 1 },
}

?

The value missing: 1 and paved: 1 is optional

Or do you have another use case? If yes, can you describe the tagging of certain roads that would be handled incorrectly by this proposal?

Sorry for the confusion!

Hopefully the following table will clarify what I’m trying to do. Basically it is a road bike use case. I want to only allow routing on more minor roads and paths if they are known to be paved (by paved I would include asphalt, concrete, etc but I’ve just shown it as paved below for simplicity).

I would like to “allow” the following (obviously subject to normal bike access rules):

road class surface
primary any
secondary any
tertiary any
unclassified only if paved
residential any
living_street only if paved
service only if paved
pedestrian any
all other types ... only if paved

motorway and trunk roads would also be disallowed.

Thanks, I think I understand the problem now. I’m sometimes a bit slow :slight_smile:

This is currently indeed not possible. An ugly workaround would be to not fully avoid unpaved roads (e.g. unpaved: 0.5) and also avoid unclassified, living_street etc via unclassified: 0.5 etc. Then if it is unpaved and unclassified both values are multiplied resulting in 0.25.

To fix this we could try to extend the “condition” feature we already have for area:

priority:
   area_somecity: 0

areas:
  somecity: # define area via geosjon

and do something like this instead:

priority:
   in_somecity: 0

conditions:
  in_somecity: # define area via geosjon
  other_condition: 
      road_class: unclassified
      road_class: living_street

Which would be then for your use case:

priority:
   "small_roads and paved_surface": 0

conditions:
   small_roads: 
      road_class: unclassified
      road_class: living_street
      ...
   paved_surface:
      surface: paved
      surface: concrete
      ...

This cries for a more flexible and generic scripting language (Java/JavaScript/python/…). The downside of a generic scripting language is that we cannot check per-request if it is compatible to an existing landmark vehicle profile (to use the faster landmark A*) and so we would either have to accept that the script triggers suboptimal routes (if it increases the weight for certain edges) or the response time is much slower (due to fallback to beeline A*) or requires a slow graph scan to compare every edge.

Hmmh, or we limit the power of the scripting engine when executed per-request and say it can only decrease the weight and either throw an error if it increases it or ignore it silently.

No worries, I can understand that there are a number of challenges to adding support for conditional rules. It would still be useful to add “missing” though, so that different priorities can be used if the surface information hasn’t been added.

Can you also clarify what “other” means please? I’m assuming it means any value not on the list that you get when you hover over the surface rules on your demo server (including missing tags)…? Or is there another definitive list of what is not “other” somewhere?

Yes, this is the meaning and also how you know the values (see the /info endpoint).

Have integrated the missing value for surface in master: https://github.com/graphhopper/graphhopper/commit/5620ea0a307ea96bb8554281770fad7a41405e49

Thank you. Does that mean it has also now been added to the version 1.0 web service jar or will this update be applied to a future version of this?

I think a simple workaround for such ‘conditional’ use-cases should be doing some kind of pre-processing: Process all roads and add some custom tag depending on other tags (like surface+road_class) and then use this new custom tag in the custom weighting. Maybe we should make this process more accessible/easier and keep the ‘scripting/custom weighting’ simple.

No, commiting to master does not add the change to an already existing version/jar (like 1.0), but the change will be included in the next (pre)release, the last one of which was on july 17th: Releases · graphhopper/graphhopper · GitHub

1 Like

@plotaroute there is now a new branch in a good shape that allows you to define more complex conditions like:

priority:
  road_class == UNCLASSIFIED && surface != PAVED: 0.5
  road_class == LIVING_STREET && (surface != PAVED || surface == OTHER): 0.3

Here every statement will be evaluated and the values will be multiplied for priority.

Or something like an if-then-else-if flow where only one value is picked:

priority:
  first_match:
    road_class == PRIMARY || road_class == SECONDARY: 1.0
    surface == UNPAVED: 0

It is slightly more verbose but much more powerful and readable IMO. Let me know what you think. For a migration guide there is currently only the examples and the changed profiles.md documentation in the branch.

2 Likes

Wow, that sounds great - thanks. Looks really flexible. We’re deeply tied up with another project at the moment but will have a look at it when we get chance.