GraphHopper.com | Forum | GitHub | Maps | Blog

Understanding Road Bike VehicleProfile


#1

Hi,

as java developer and road bike cycle enthusiast I am very happy to be able to adjust the existing road bike vehicle profile (RacingBikeFlagEncoder) to my personal preferences.

After two hard days with the code and test data I have now a working profile that generates the routing I personally prefer - but at the core of my java-developer-heart I am struggling understanding the impact of my changes/adjustments…

In order to understand my dilemma, I want to explain briefly what are my personal/private preferences when it comes to road bike cycling.

  1. cycle ways are fine in general - but except for ‘trunk’ or ‘primary’ highways I personally prefer to stay on the road (no matter of OSM attribution like ‘bicycle:use_sidepath’ or ‘cycleway:track’) - this becomes even more important when there are roundabouts on my way… Here are two examples:
    https://graphhopper.com/maps/?point=51.96616%2C8.243737&point=51.964901%2C8.245153&locale=de-de&vehicle=racingbike&weighting=fastest&elevation=true&use_miles=false&layer=Omniscale
    or
    https://graphhopper.com/maps/?point=52.065163%2C8.385776&point=52.064589%2C8.38605&locale=de-de&vehicle=racingbike&weighting=fastest&elevation=true&use_miles=false&layer=Omniscale

  2. highway=‘service’ is not a route I want to ride - again with short example: https://graphhopper.com/maps/?point=52.065413%2C8.386168&point=52.064866%2C8.38694&locale=de-de&vehicle=racingbike&weighting=fastest&elevation=true&use_miles=false&layer=Omniscale

  3. the current implementation tries to send me on cycle ways as often as possible - again that’s a fine behavior when it comes to a cycle tour with my kids - but for my sports activity this behavior ist not my preferred one - I tried to solve this with additional ‘via’ points - but this can’t be the correct approach - https://graphhopper.com/maps/?point=51.799831%2C8.427265&point=51.795352%2C8.428402&point=51.788975%2C8.429024&locale=de-de&vehicle=bike&weighting=fastest&elevation=true&use_miles=false&layer=Omniscale

  4. I personally do not care that much about possible turn restrictions at a junction - but of course I always follow the StVO!

  5. there are probably a bit more - (like prefer tertiary (or unclassified) over secondary or primary roads) - but this is not that essential…

Again - this are all just my personal preferences and I don’t expect that the gh road bike vehicle profile will be adjusted in any kind - I have already created a custom version of the RacingBikeFlagEncoder (created my own FactoryImpl etc…) that solves these issues above (of course only tested briefly within my cycle area).

The only thing I am currently unhappy with is, that I am not able to understand why, which adjustment was/is required in order to archive that I archived (and wanted to archive)…

So in general the adjustments are straight forward:

  1. make sure that we try to avoid residential areas…
this.preferHighwayTags.remove("residential");
this.preferHighwayTags.add("unclassified");
  1. make sure that ‘yes’, ‘designated’, ‘official’ or ‘permissive’ will be ignored! - this was/is required to ignore the cycleway here https://graphhopper.com/maps/?point=52.06478%2C8.385519&point=52.064582%2C8.386023&locale=de-de&vehicle=racingbike&weighting=fastest&elevation=true&use_miles=false&layer=Omniscale
this.intendedValues.clear();
  1. few adjustments to surface and highway speeds - but this is IMHO not critical…

  2. Next one is required in order to stay on the road in this roundabout https://graphhopper.com/maps/?point=51.96616%2C8.243737&point=51.964901%2C8.245153&locale=de-de&vehicle=racingbike&weighting=fastest&elevation=true&use_miles=false&layer=Omniscale - and this is the change I am worried the most about - or better I still struggling to understand why this is required - I have tried "everything i could imagine that have to do with speeds - even setting ‘tertiary’ speed to 60 and cycleway speed to 4 (and also ensured that SurfaceSpeed would also not be higher then then ‘4’) - the routing always taking the cycleway - so finally I only could solve that by setting cycleways (and service) as pushing

this.addPushingSection("cycleway");
this.addPushingSection("service");
  1. additionally I made few adjustments to the ‘collect(…)’ method - or better in the collect(…) I don’t call super.collect(…) and instead I used a local copy of BikeCommonFlagEncoder.collect(…) and removed the handling of ‘bicycle:use_sidepath’ or ‘cycleway:track’ (that have caused that roads that have his additional attributes would had been tried to avoid (if possible).

So again what’s the root cause of this topic? - On the one hand side as road bike cyclist I could be happy - since I have now a private profile that actually generates the routing I am interested in - but as developer I am more or less desperate - since I am just adjusted to code by “try & error” in order to archive what I wanted to archive - and I am looking for help (where I can find additional explanations) how VehicleFlagEncoders are working in general so that my programmer heart finally understand what I actually did…

If these adjustments are totally crap - please let me know - and please be so kind and push me into a direction that is way more reasonable - tia

Matthias


#2

as so often - writing about your worries simply helps…

finally I think I have understood why I had the need to declare the cycleway as pushingsection… IMHO the root cause can be found in the getSpeed(…) method of the BikeCommonFlagEncoder…

In my case of the roundabouts and the cycleways around them - I have to realize, that the declared surface speed will always win (except for pushingSections)…

Since in the example https://graphhopper.com/maps/?point=51.96616%2C8.243737&point=51.964901%2C8.245153&locale=de-de&vehicle=racingbike&weighting=fastest&elevation=true&use_miles=false&layer=Omniscale the cycleway and the street have both the surface:asphalt tag - both will get the same speed applied - and this will end of course in the routing that we see…

So with all respect I wanted to discuss this section of the BikeCommonFlagEncoder class:

    String s = way.getTag("surface");
    if (!Helper.isEmpty(s)) {
        Integer surfaceSpeed = surfaceSpeeds.get(s);
        if (surfaceSpeed != null) {
            speed = surfaceSpeed;
            // boost handling for good surfaces but avoid boosting if pushing section
            if (highwaySpeed != null && surfaceSpeed > highwaySpeed) {
                if (pushingSectionsHighways.contains(highwayTag)) {
                    speed = highwaySpeed;
                } else {
                    speed = surfaceSpeed;
                }
            }
        }
    } else {
        ...

so as soon as a osm way have a surface attribute that can be found in the surfaceSpeeds map this speed wins over all other defined speeds - except the highway tag is in the pushingSectionsHighways

So finally my developer heart now understand what the cycle enthusiast have implemented - but I want to start a short discussion if the code above could be improved/adjusted?


#3

The code could be surely improved. Nothing is perfect :slight_smile:

Do you have a suggestion or reasoning how it should be done?


#4

Hi Peter,

of course since it’s code it’s probably not perfect - Since I am aware that there have been so many hours invested into the gh development the intention of my question was more toward the direction, if you have spend already a massive amount of time into this section of the code?

So there might be good reasons for your implementation - that e.g. will make plenty of sense in cities or in other countries? - I don’t know.

From my perspective the highways speed should not be always up/downgradeable by a surface speed - and same applies to surfaces - not all of them should upgrade a certain highway speed (or at least there should be a max/min)…

So setting the Highway, Surface & Track speeds should IMHO not be implemented with a simple int - I would like to suggest that this should be an Object that have a setting in with direction it could be overwritten.

But again - I simple just might not have enough gh experience to make already an appropriate suggestion.


#5

Maybe @ratrun can comment on this a bit better?


#6

Hi!
The road bike profile is probably the one which is the most difficult for tuning. My intention with the current implementation is that prominent, well equipped cycle routes such as the Euro Velo routes are also taken and not just secondary and tertiary highways. A road bike profile for bike professionals is probably better served by a car profile with the avoidance of motorways.


#7

Thanks for your reply ratrun - I am totally fine with the emphasize on tagged official cycle routes…

And I also agree that for a ‘professional’ road bike rider the car profile might be a good alternative - that was also my initial start/attempt - but in the case of the car you are restricted to one ways and a lot of barriers that are not passable by car are no deal for a bike…

But the main issue I see/have (and where I ask if you are willing to accept an incoming PR) is the fact, that you surfaces (and tracktypes) are always manipulate “highway” speeds in any direction. This implies that if two different types of highways (e.g. tertiary and a footpath) both with the same surface (e.g. asphalt) will be considered to have the equal speed…

And this behavior is IMHO the root cause why in the case of the road-bike routing plenty of times cycle ways will be preferred over regular streets (specially in roundabouts)


#8

If you create a PR don’t forget to update and add the new corner cases to the testcases. I plan to check your PR.