abrensch / brouter

configurable OSM offline router with elevation awareness, Java + Android
MIT License
494 stars 118 forks source link

`Stick to cycleroute` does not minimize the non-bike-lane path #718

Closed Iey4iej3 closed 2 months ago

Iey4iej3 commented 2 months ago

I chose stick to cycleroutes. However, the suggested path for https://brouter.de/brouter-web/#map=14/52.3471/4.9636/standard,Waymarked_Trails-Cycling&lonlats=4.921017,52.349295;4.951916,52.355069 is not optimal in the sense that there are a quite portion of the route in which there is no bike lane. According to the data, 0.57 km is "unclassified" (which is in fact a route with cars and bikes mixed). If I switch to the first alternative, then it is much better.

the profile ``` # *** The trekking profile is for slow travel # *** and avoiding car traffic, but still with # *** a focus on approaching your destination # *** efficiently. ---context:global # following code refers to global config # Bike profile assign validForBikes = true # Use the following switches to change behaviour assign allow_steps = true # %allow_steps% | Set false to disallow steps | boolean assign allow_ferries = true # %allow_ferries% | Set false to disallow ferries | boolean assign ignore_cycleroutes = false # %ignore_cycleroutes% | Set true for better elevation results | boolean assign stick_to_cycleroutes = true # %stick_to_cycleroutes% | Set true to just follow cycleroutes | boolean assign avoid_unsafe = true # %avoid_unsafe% | Set true to avoid standard highways | boolean assign consider_noise = false # %consider_noise% | Activate to prefer a low-noise route | boolean assign consider_river = false # %consider_river% | Activate to prefer a route along rivers, lakes, etc. | boolean assign consider_forest = false # %consider_forest% | Activate to prefer a route in forest or parks | boolean assign consider_town = false # %consider_town% | Activate to bypass cities / big towns as far as possible | boolean assign consider_traffic = false # %consider_traffic% | Activate to consider traffic estimates | boolean # Change elevation parameters assign consider_elevation = true # %consider_elevation% | Set true to favor a route with few elevation meters | boolean assign downhillcost = 60 # %downhillcost% | Cost for going downhill | number assign downhillcutoff = 1.5 # %downhillcutoff% | Gradients below this value in percents are not counted. | number assign uphillcost = 0 # %uphillcost% | Cost for going uphill | number assign uphillcutoff = 1.5 # %uphillcutoff% | Gradients below this value in percents are not counted. | number assign downhillcost = if consider_elevation then downhillcost else 0 assign uphillcost = if consider_elevation then uphillcost else 0 # Kinematic model parameters (travel time computation) assign totalMass = 90 # %totalMass% | Mass (in kg) of the bike + biker, for travel time computation | number assign maxSpeed = 20 # %maxSpeed% | Absolute maximum speed (in km/h), for travel time computation | number assign S_C_x = 0.225 # %S_C_x% | Drag coefficient times the reference area (in m^2), for travel time computation | number assign C_r = 0.01 # %C_r% | Rolling resistance coefficient (dimensionless), for travel time computation | number assign bikerPower = 100 # %bikerPower% | Average power (in W) provided by the biker, for travel time computation | number # Turn instructions settings assign turnInstructionMode = 1 # %turnInstructionMode% | Mode for the generated turn instructions | [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style, 7=locus-old-style] assign turnInstructionCatchingRange = 40 # %turnInstructionCatchingRange% | Within this distance (in m) several turning instructions are combined into one and the turning angles are better approximated to the general direction | number assign turnInstructionRoundabouts = true # %turnInstructionRoundabouts% | Set "false" to avoid generating special turning instructions for roundabouts | boolean assign considerTurnRestrictions = true # %considerTurnRestrictions% | Set true to take turn restrictions into account | boolean assign processUnusedTags = false # %processUnusedTags% | Set true to output unused tags in data tab | boolean ---context:way # following code refers to way-tags # classifier constants assign classifier_none = 1 assign classifier_ferry = 2 # # pre-calculate some logical expressions # assign any_cycleroute = if route_bicycle_icn=yes then true else if route_bicycle_ncn=yes then true else if route_bicycle_rcn=yes then true else if route_bicycle_lcn=yes then true else false assign nodeaccessgranted = if any_cycleroute then true else lcn=yes assign is_ldcr = if ignore_cycleroutes then false else any_cycleroute assign isbike = or bicycle_road=yes or bicycle=yes or or bicycle=permissive bicycle=designated lcn=yes assign ispaved = surface=paved|asphalt|concrete|paving_stones|sett assign isunpaved = not or surface= or ispaved surface=fine_gravel|cobblestone assign probablyGood = or ispaved and ( or isbike highway=footway ) not isunpaved # # this is the cost (in Meter) for a 90-degree turn # The actual cost is calculated as turncost*cos(angle) # (Suppressing turncost while following longdistance-cycleways # makes them a little bit more magnetic) # assign turncost = if is_ldcr then 0 else if junction=roundabout then 0 else 90 # # for any change in initialclassifier, initialcost is added once # assign initialclassifier = if route=ferry then classifier_ferry else classifier_none # # calculate the initial cost # this is added to the total cost each time the costfactor # changed # assign initialcost = if ( equal initialclassifier classifier_ferry ) then 10000 else 0 # # implicit access here just from the motorroad tag # (implicit access rules from highway tag handled elsewhere) # assign defaultaccess = if access= then not motorroad=yes else if access=private|no then false else true # # calculate logical bike access # assign bikeaccess = if bicycle= then ( if bicycle_road=yes then true else if vehicle= then ( if highway=footway then false else defaultaccess ) else not vehicle=private|no ) else not bicycle=private|no|dismount|use_sidepath # # calculate logical foot access # assign footaccess = if bikeaccess then true else if bicycle=dismount then true else if foot= then defaultaccess else not foot=private|no # # if not bike-, but foot-acess, just a moderate penalty, # otherwise access is forbidden # assign accesspenalty = if bikeaccess then 0 else if footaccess then 4 else if any_cycleroute then 15 else 10000 # # handle one-ways. On primary roads, wrong-oneways should # be close to forbidden, while on other ways we just add # 4 to the costfactor (making it at least 5 - you are allowed # to push your bike) # assign badoneway = if reversedirection=yes then if oneway:bicycle=yes then true else if oneway= then junction=roundabout else oneway=yes|true|1 else oneway=-1 assign onewaypenalty = if ( badoneway ) then ( if ( cycleway=opposite|opposite_lane|opposite_track ) then 0 else if ( cycleway:left=opposite|opposite_lane|opposite_track ) then 0 else if ( cycleway:right=opposite|opposite_lane|opposite_track ) then 0 else if ( oneway:bicycle=no ) then 0 else if ( cycleway:left:oneway=no ) then 0 else if ( cycleway:right:oneway=no ) then 0 else if ( junction=roundabout|circular ) then 60 else if ( highway=primary|primary_link ) then 50 else if ( highway=secondary|secondary_link ) then 30 else if ( highway=tertiary|tertiary_link ) then 20 else 4.0 ) else 0.0 # add estimate tags assign traffic_penalty switch consider_traffic switch estimated_traffic_class= 0 switch estimated_traffic_class=1|2 0.2 switch estimated_traffic_class=3 0.4 switch estimated_traffic_class=4 0.6 switch estimated_traffic_class=5 0.8 switch estimated_traffic_class=6|7 1 99 0 assign noise_penalty switch consider_noise switch estimated_noise_class= 0 switch estimated_noise_class=1 0.3 switch estimated_noise_class=2 0.5 switch estimated_noise_class=3 0.8 switch estimated_noise_class=4 1.4 switch estimated_noise_class=5 1.7 switch estimated_noise_class=6 2 0 0 assign no_river_penalty switch consider_river switch estimated_river_class= 2 switch estimated_river_class=1 1.3 switch estimated_river_class=2 1 switch estimated_river_class=3 0.7 switch estimated_river_class=4 0.4 switch estimated_river_class=5 0.1 switch estimated_river_class=6 0 99 0 assign no_forest_penalty switch consider_forest switch estimated_forest_class= 1 switch estimated_forest_class=1 0.5 switch estimated_forest_class=2 0.4 switch estimated_forest_class=3 0.25 switch estimated_forest_class=4 0.15 switch estimated_forest_class=5 0.1 switch estimated_forest_class=6 0 99 0 assign town_penalty switch consider_town switch estimated_town_class= 0 switch estimated_town_class=1 0.5 switch estimated_town_class=2 0.9 switch estimated_town_class=3 1.2 switch estimated_town_class=4 1.3 switch estimated_town_class=5 1.4 switch estimated_town_class=6 1.6 99 0 # # calculate the cost-factor, which is the factor # by which the distance of a way-segment is multiplied # to calculate the cost of that segment. The costfactor # must be >=1 and it's supposed to be close to 1 for # the type of way the routing profile is searching for # assign isresidentialorliving = or highway=residential|living_street living_street=yes assign costfactor # # exclude rivers, rails etc. # if ( and highway= not route=ferry ) then 10000 # # exclude motorways and proposed roads # else if ( highway=motorway|motorway_link ) then 10000 else if ( highway=proposed|abandoned ) then 10000 # # all other exclusions below (access, steps, ferries,..) # should not be deleted by the decoder, to be available # in voice-hint-processing # else min 9999 add town_penalty add no_forest_penalty add no_river_penalty add noise_penalty add traffic_penalty # # apply oneway-and access-penalties # add max onewaypenalty accesspenalty # # steps and ferries are special. Note this is handled # before the cycleroute-switch, to be able # to really exlude them be setting cost to infinity # if ( highway=steps ) then ( if allow_steps then 40 else 10000 ) else if ( route=ferry ) then ( if allow_ferries then 5.67 else 10000 ) # # handle long-distance cycle-routes. # else if ( is_ldcr ) then 1 # always treated as perfect (=1) else add ( if stick_to_cycleroutes then 0.5 else 0.05 ) # everything else somewhat up # # some other highway types # if ( highway=pedestrian ) then 3 else if ( highway=bridleway ) then 5 else if ( highway=cycleway ) then 1 else if ( isresidentialorliving ) then ( if isunpaved then 1.5 else 1.1 ) else if ( highway=service ) then ( if isunpaved then 1.6 else 1.3 ) # # tracks and track-like ways are rated mainly be tracktype/grade # But note that if no tracktype is given (mainly for road/path/footway) # it can be o.k. if there's any other hint for quality # else if ( highway=track|road|path|footway ) then ( if ( tracktype=grade1 ) then ( if probablyGood then 1.0 else 1.3 ) else if ( tracktype=grade2 ) then ( if probablyGood then 1.1 else 2.0 ) else if ( tracktype=grade3 ) then ( if probablyGood then 1.5 else 3.0 ) else if ( tracktype=grade4 ) then ( if probablyGood then 2.0 else 5.0 ) else if ( tracktype=grade5 ) then ( if probablyGood then 3.0 else 5.0 ) else ( if probablyGood then 1.0 else 5.0 ) ) # # When avoiding unsafe ways, avoid highways without a bike hint # else add ( if ( and avoid_unsafe not isbike ) then 2 else 0 ) # # actuals roads are o.k. if we have a bike hint # if ( highway=trunk|trunk_link ) then ( if isbike then 1.5 else 10 ) else if ( highway=primary|primary_link ) then ( if isbike then 1.2 else 3 ) else if ( highway=secondary|secondary_link ) then ( if isbike then 1.1 else 1.6 ) else if ( highway=tertiary|tertiary_link ) then ( if isbike then 1.0 else 1.4 ) else if ( highway=unclassified ) then ( if isbike then 1.0 else 1.3 ) # # default for any other highway type not handled above # else 2.0 # way priorities used for voice hint generation assign priorityclassifier = if ( highway=motorway ) then 30 else if ( highway=motorway_link ) then 29 else if ( highway=trunk ) then 28 else if ( highway=trunk_link ) then 27 else if ( highway=primary ) then 26 else if ( highway=primary_link ) then 25 else if ( highway=secondary ) then 24 else if ( highway=secondary_link ) then 23 else if ( highway=tertiary ) then 22 else if ( highway=tertiary_link ) then 21 else if ( highway=unclassified ) then 20 else if ( isresidentialorliving ) then 6 else if ( highway=service ) then 6 else if ( highway=cycleway ) then 6 else if ( or bicycle=designated bicycle_road=yes ) then 6 else if ( highway=track ) then if tracktype=grade1 then 6 else 4 else if ( highway=bridleway|road|path|footway ) then 4 else if ( highway=steps ) then 2 else if ( highway=pedestrian ) then 2 else 0 # some more classifying bits used for voice hint generation... assign isbadoneway = not equal onewaypenalty 0 assign isgoodoneway = if reversedirection=yes then oneway=-1 else if oneway= then junction=roundabout else oneway=yes|true|1 assign isroundabout = junction=roundabout assign islinktype = highway=motorway_link|trunk_link|primary_link|secondary_link|tertiary_link assign isgoodforcars = if greater priorityclassifier 6 then true else if ( or isresidentialorliving highway=service ) then true else if ( and highway=track tracktype=grade1 ) then true else false # ... encoded into a bitmask assign classifiermask add isbadoneway add multiply isgoodoneway 2 add multiply isroundabout 4 add multiply islinktype 8 multiply isgoodforcars 16 # include `smoothness=` tags in the response's WayTags for track analysis assign dummyUsage = smoothness= ---context:node # following code refers to node tags assign defaultaccess = if ( access= ) then true # add default barrier restrictions here! else if ( access=private|no ) then false else true assign bikeaccess = if nodeaccessgranted=yes then true else if bicycle= then ( if vehicle= then defaultaccess else not vehicle=private|no ) else not bicycle=private|no|dismount assign footaccess = if bicycle=dismount then true else if foot= then defaultaccess else not foot=private|no assign initialcost = if bikeaccess then 0 else ( if footaccess then 100 else 1000000 ) ```
EssBee59 commented 2 months ago

Hello,

The default setting in this trekking profile is to prefer cycle_routes to every other highway! (I liked to change this behaviour, but no chance till yet..)

So to get the optimal route, you can activate the options "ignore_cycleroutes" or use another profile, as example my own trekking (or racebike) ==> racebike https://brouter.de/essbee/#map=10/52.3795/5.0366/osm-mapnik-german_style ==> Trekking https://brouter.de/essbee/#map=10/52.3795/5.0366/osm-mapnik-german_style&profile=trekking_SB

regards

poutnikl commented 2 months ago

Hmm, but cycleroutes are not physical highways in the OSM sense. Cycleways are.

EssBee59 commented 2 months ago

Hello Poutnikl,

Of course, my formulation should be changed a bit...

...currently the trekking profile prefers highways with a "cycle_route" tag to any other highway...

in the case above (see route_bicycle_rcn), the cost per km is 1000: 2 247 1000 0 0 0 0 reversedirection=yes highway=residential surface=paving_stones oneway=yes oneway:bicycle=no smoothness=intermediate route_bicycle_rcn=yes
2 4 1000 0 0 0 0 reversedirection=yes highway=residential surface=paving_stones oneway=yes oneway:bicycle=no smoothness=intermediate route_bicycle_rcn=yes 2 7 1000 0 0 0 0 highway=residential smoothness=intermediate route_bicycle_rcn=yes 2 8 1000 0 0 0 0 highway=unclassified surface=asphalt smoothness=good route_bicycle_rcn=yes 152 15283 2 197 1000 0 0 0 0 highway=unclassified surface=asphalt smoothness=good route_bicycle_rcn=yes 3 173 1000 0 0 0 0 highway=unclassified surface=paving_stones smoothness=good route_bicycle_rcn=yes 3 125 1000 0 0 0 0 highway=unclassified surface=asphalt smoothness=good route_bicycle_rcn=yes 245 24551 3 17 1000 0 0 0 0 reversedirection=yes highway=unclassified surface=asphalt smoothness=intermediate route_bicycle_rcn=yes 1 209 1000 0 0 0 0 highway=cycleway surface=paving_stones foot=yes oneway=no smoothness=intermediate route_bicycle_rcn=yes 1 44 1000 0 0 0 0 highway=cycleway surface=paving_stones foot=use_sidepath oneway=no smoothness=intermediate route_bicycle_rcn=yes 0 113 1000 0 0 0 0 highway=cycleway surface=paving_stones foot=yes oneway=no smoothness=intermediate route_bicycle_rcn=yes 0 31 1000 0 0 0 0 reversedirection=yes highway=unclassified surface=paving_stones route_bicycle_rcn=yes 0 6 1000 0 0 0 0 reversedirection=yes highway=unclassified surface=paving_stones route_bicycle_rcn=yes 0 15 1000 0 0 0 0 reversedirection=yes highway=unclassified surface=paving_stones route_bicycle_rcn=yes 1 7 1000 0 0 0 0 reversedirection=yes highway=cycleway surface=asphalt foot=use_sidepath bicycle=designated smoothness=excellent route_bicycle_rcn=yes 1 3 1000 0 0 0 0 reversedirection=yes highway=cycleway surface=asphalt foot=use_sidepath bicycle=designated smoothness=excellent route_bicycle_rcn=yes 1 32 1000 0 0 0 0 reversedirection=yes highway=cycleway surface=asphalt foot=designated bicycle=designated smoothness=excellent route_bicycle_rcn=yes 1 221 1000 0 0 0 0 reversedirection=yes highway=cycleway surface=asphalt foot=designated bicycle=designated oneway=no smoothness=excellent route_bicycle_rcn=yes

quaelnix commented 2 months ago

@Iey4iej3, stick_to_cycleroutes basically does the opposite of what you want.

I liked to change this behaviour, but no chance till yet..

The solution is trivial, select the "Trekking bike (ignore cycle routes)" profil: https://brouter.de/brouter-web/#map=15/52.3517/4.9336/standard,Waymarked_Trails-Cycling&lonlats=4.921017,52.349295;4.951916,52.355069&profile=trekking-ignore-cr

Iey4iej3 commented 2 months ago

@Iey4iej3, stick_to_cycleroutes basically does the opposite of what you want.

I liked to change this behaviour, but no chance till yet..

The solution is trivial, chose the "Trekking bike (ignore cycle routes)" profil: https://brouter.de/brouter-web/#map=15/52.3517/4.9336/standard,Waymarked_Trails-Cycling&lonlats=4.921017,52.349295;4.951916,52.355069&profile=trekking-ignore-cr

My wording might be ambiguous. I want to minimize the length of the path in which there is no bike lane, i.e. avoiding mixture of bikes and cars. This is precisely what "stick to cycleroute" means.

poutnikl commented 2 months ago

My wording might be ambiguous. I want to minimize the length of the path in which there is no bike lane, i.e. avoiding mixture of bikes and cars. This is precisely what "stick to cycleroute" means.

Being a cycleroute has no direct relation to the physical state of the given way, namely separation of car and bike ways. It is waymarking, similar to hiking marks.

It goes in ideal cases along cycleways, independent or adjacent tracks or lanes. But also along tertiary, secondary or primary roads in case of lack of better options, or as cycleroute author decision. Literally anywhere where bicycles are allowed.

It can be expected a cycleroute would use existing bike lanes or cycleways, mostly in cities. But generally, it does not necessarily minimize ways shared with cars.

Illustratively, imagine two similar, 10 km alternatives:

To achieve your goal, you should not fix yourself to cycleroutes (what is a good way generally, but does not guarantee what you want). You need a profile that is penalising ways not friendly to bikes more.

EssBee59 commented 2 months ago

Hello Iey4iej3,

You want to favor highways with a lane or so (tag cycleway=lane in OSM) to a same highway without cycleway=lane ?

I do so in my profiles described above! Feel free to test! (compared to the standard trekking profile many further features are available)

EssBee59 commented 2 months ago

to better understand the routing... The route Brouter suggest the route with the lowest "cost". As example here the costs my trekking profile is generating per km on "secondary" highways, depending on the tags (not only the highway type is considered, also the traffic, the max_speed, lane if available, etc...)

cost / km highway tags 3210 highway=secondary surface=asphalt maxspeed=50 smoothness=good estimated_traffic_class=5 2000 highway=secondary maxspeed=50 cycleway=lane estimated_traffic_class=5 3210 reversedirection=yes highway=secondary surface=asphalt maxspeed=50 smoothness=good estimated_traffic_class=5 3130 reversedirection=yes highway=secondary surface=asphalt maxspeed=30 smoothness=good estimated_traffic_class=5 3080 reversedirection=yes highway=secondary surface=asphalt maxspeed=30 smoothness=good route_bicycle_rcn=yes estimated_traffic_class=5 3130 reversedirection=yes highway=secondary surface=asphalt maxspeed=30 smoothness=good estimated_traffic_class=5 2830 reversedirection=yes highway=secondary surface=asphalt maxspeed=30 smoothness=good estimated_traffic_class=4 2910 highway=secondary surface=asphalt maxspeed=50 junction=roundabout smoothness=excellent estimated_traffic_class=4 2910 reversedirection=yes highway=secondary surface=asphalt maxspeed=50 smoothness=good estimated_traffic_class=4 2000 reversedirection=yes highway=secondary surface=asphalt cycleway=lane smoothness=good estimated_traffic_class=4 maxspeed:backward=100 5150 reversedirection=yes highway=secondary surface=asphalt maxspeed=100 smoothness=good estimated_traffic_class=4

you can see taht "cycleway=lane" grantly decrease the costs when present

Iey4iej3 commented 2 months ago

OK, I see. I misunderstood. The testing of trekking profile should take place elsewhere.