diff --git a/docs/profiles.md b/docs/profiles.md index fa74b8780..7d548877b 100644 --- a/docs/profiles.md +++ b/docs/profiles.md @@ -337,7 +337,7 @@ function setup() end ``` -The input data must an ASCII file with rows of integers. e.g.: +The input data must be an ASCII file with rows of integers. e.g.: ``` 0 0 0 0 @@ -367,6 +367,18 @@ end See [rasterbot.lua](../profiles/rasterbot.lua) and [rasterbotinterp.lua](../profiles/rasterbotinterp.lua) for examples. +#### Using elevation data +If you want to make your routing-machine elevation aware, you can use the profile +[elevation_aware_bicycle](../profiles/examples/elevation_aware_bicycle). +The following preprocessing steps are necessary to use this profile: + +1. Download the elevation data as ASCII tiles e.g. from [SRTM](http://srtm.csi.cgiar.org/srtmdata/) +1. Adapt and run [generate_rastersource.py](../profiles/examples/elevation_aware_bicycle) to fuse the raw tiles and obtain `rastersource.asc`. +1. Set the boundaries of your rastersource in `raster:load()` in [elevation_aware_bicycle.lua](../profiles/examples/elevation_aware_bicycle/elevation_aware_bicycle.lua) +1. [Quick start](https://github.com/Project-OSRM/osrm-backend/wiki/Running-OSRM#quickstart) OSRM, +but use [elevation_aware_bicycle.lua](../profiles/examples/elevation_aware_bicycle/elevation_aware_bicycle.lua) +and `rastersource.asc` instead of `car.lua`. + ### Helper functions There are a few helper functions defined in the global scope that profiles can use: diff --git a/profiles/examples/elevation_aware_bicycle/elevation_aware_bicycle.lua b/profiles/examples/elevation_aware_bicycle/elevation_aware_bicycle.lua new file mode 100644 index 000000000..10730e2ed --- /dev/null +++ b/profiles/examples/elevation_aware_bicycle/elevation_aware_bicycle.lua @@ -0,0 +1,750 @@ +-- Bicycle profile +-- +-- Takes elevation into account. +-- + +api_version = 4 + +Set = require('lib/set') +Sequence = require('lib/sequence') +Handlers = require("lib/way_handlers") +find_access_tag = require("lib/access").find_access_tag +limit = require("lib/maxspeed").limit + +list = {0.4040111813705065,0.4077509707194649,0.4115930094776843,0.41554190707638683,0.419602561205936,0.42378018077868934,0.4280803111034602,0.43250886151983375,0.43707213577212073,0.4417768654386842,0.44663024677340407,0.4516399813629164,0.45681432105685077,0.46216211768961296,0.46769287818247157,0.47341682569514565,0.4793449675882814,0.4854891710638476,0.4918622474715554,0.49847804640803445,0.5053515608941388,0.5124990450969183,0.5199381462693039,0.5276880528151419,0.5357696606525774,0.5442057603471956,0.5530212478192406,0.5622433617966259,0.5719019515848806,0.5820297791502957,0.5926628599510663,0.6038408473818764,0.6156074660856684,0.6280109996784702,0.6411048385477748,0.6549480932017125,0.6696062779906035,0.6851520686459992,0.7016661346382446,0.7192380433680241,0.7379672270496413,0.7579639940024726,0.7793505529460762,0.8022620006557876,0.826847198831215,0.8532694343893874,0.8817067185549876,0.9123515356952666,0.9454098073807813,0.9810987996660977,1.0196436869904062,1.0612725155267435,1.1062094072354913,1.154666035145556,1.206831686614459,1.262862588811012,1.3228715297368607,1.386919058946579,1.4550075750313445,1.5270793258312663,1.603018782583213,1.6826591333307126,1.7657919757398155,1.8521788610886971,1.9415632418225015,2.0336815766458516,2.1282727347390007,2.2250852757628166,2.3238825566437655,2.4244458751913984,2.526575998468329,2.6300934640510762,2.734838018511459,2.8406675004812123,2.9474564080893972,3.05509432606533,3.1634843330276547,3.2725414664448174,3.382191290874559,3.492368592633609,3.6030162089714017,3.7140839902231964,3.825527887713802,3.937309157144547,4.049393665912615,4.1617512926354046,4.274355407635642,4.387182423985552,4.50021140971591,4.613423752846485,4.726802871914744,4.840333965631132,4.954003796150395,5.0678005012152,5.181713431100397,5.295733006870892,5.409850596970382,5.524058409591401,5.638349398647695,5.7527171814864015} +N = 100 +min_slope = -0.25 +max_slope = 0.25 + +function setup() + + local default_speed = 15 + local walking_speed = 4 + + local raster_path = os.getenv('OSRM_RASTER_SOURCE') or "rastersource.asc" + + return { + properties = { + force_split_edges = true, + u_turn_penalty = 20, + traffic_light_penalty = 2, + --weight_name = 'cyclability', + weight_name = 'duration', + process_call_tagless_node = false, + max_speed_for_map_matching = 110/3.6, -- kmph -> m/s + use_turn_restrictions = false, + continue_straight_at_waypoint = false, + mode_change_penalty = 30, + }, + + raster_source = raster:load( + raster_path, + 5, -- lon_min + 15, -- lon_max + 45, -- lat_min + 50, -- lat_max + 6000, -- nrows + 12000 -- ncols + ), + + default_mode = mode.cycling, + default_speed = default_speed, + walking_speed = walking_speed, + oneway_handling = true, + turn_penalty = 6, + turn_bias = 1.4, + use_public_transport = true, + + allowed_start_modes = Set { + mode.cycling, + mode.pushing_bike + }, + + barrier_blacklist = Set { + 'yes', + 'wall', + 'fence' + }, + + access_tag_whitelist = Set { + 'yes', + 'permissive', + 'designated' + }, + + access_tag_blacklist = Set { + 'no', + 'private', + 'agricultural', + 'forestry', + 'delivery' + }, + + restricted_access_tag_list = Set { }, + + restricted_highway_whitelist = Set { }, + + -- tags disallow access to in combination with highway=service + service_access_tag_blacklist = Set { }, + + construction_whitelist = Set { + 'no', + 'widening', + 'minor', + }, + + access_tags_hierarchy = Sequence { + 'bicycle', + 'vehicle', + 'access' + }, + + restrictions = Set { + 'bicycle' + }, + + cycleway_tags = Set { + 'track', + 'lane', + 'share_busway', + 'sharrow', + 'shared', + 'shared_lane' + }, + + opposite_cycleway_tags = Set { + 'opposite', + 'opposite_lane', + 'opposite_track', + }, + + -- reduce the driving speed by 30% for unsafe roads + -- only used for cyclability metric + unsafe_highway_list = { + primary = 0.5, + secondary = 0.65, + tertiary = 0.8, + primary_link = 0.5, + secondary_link = 0.65, + tertiary_link = 0.8, + }, + + service_penalties = { + alley = 0.5, + }, + + bicycle_speeds = { + cycleway = default_speed, + primary = default_speed, + primary_link = default_speed, + secondary = default_speed, + secondary_link = default_speed, + tertiary = default_speed, + tertiary_link = default_speed, + residential = default_speed, + unclassified = default_speed, + living_street = default_speed, + road = default_speed, + service = default_speed, + track = 12, + path = 12 + }, + + pedestrian_speeds = { + footway = walking_speed, + pedestrian = walking_speed, + steps = 2 + }, + + railway_speeds = { + train = 10, + railway = 10, + subway = 10, + light_rail = 10, + monorail = 10, + tram = 10 + }, + + platform_speeds = { + platform = walking_speed + }, + + amenity_speeds = { + parking = 10, + parking_entrance = 10 + }, + + man_made_speeds = { + pier = walking_speed + }, + + route_speeds = { + ferry = 5 + }, + + bridge_speeds = { + movable = 5 + }, + + surface_speeds = { + asphalt = default_speed, + ["cobblestone:flattened"] = 10, + paving_stones = 10, + compacted = 10, + cobblestone = 6, + unpaved = 6, + fine_gravel = 6, + gravel = 6, + pebblestone = 6, + ground = 6, + dirt = 6, + earth = 6, + grass = 6, + mud = 3, + sand = 3, + sett = 10 + }, + + classes = Sequence { + 'ferry', 'tunnel' + }, + + -- Which classes should be excludable + -- This increases memory usage so its disabled by default. + excludable = Sequence { +-- Set {'ferry'} + }, + + tracktype_speeds = { + }, + + smoothness_speeds = { + }, + + avoid = Set { + 'impassable', + 'construction' + } + } +end + +function process_segment (profile, segment) + local sourceData = raster:interpolate(profile.raster_source, segment.source.lon, segment.source.lat) + local targetData = raster:interpolate(profile.raster_source, segment.target.lon, segment.target.lat) + + local invalid = sourceData.invalid_data() + local scaled_weight = segment.weight + local scaled_duration = segment.duration + --io.write("evaluating segment: " .. sourceData.datum .. " m to " .. targetData.datum .. " m with distance " .. segment.distance .. "\n") + + if sourceData.datum ~= invalid and targetData.datum ~= invalid and segment.distance > 0 then + local slope = (targetData.datum - sourceData.datum) / segment.distance + local slope_idx = math.floor((slope - min_slope) / (max_slope - min_slope) * N) + if slope_idx < 1 then + slope_idx = 1 + end + if slope_idx > N then + slope_idx = N + end + + scaled_weight = list[slope_idx] * scaled_weight + scaled_duration = list[slope_idx] * scaled_duration + + --original calculation: is undefined for slope = 0.2 + --scaled_weight = scaled_weight / (1.0 - (slope * 5.0)) + --scaled_duration = scaled_duration / (1.0 - (slope * 5.0)) + --io.write(" slope: " .. slope .. "\n") + --io.write(" was weight: " .. segment.weight .. "\n") + --io.write(" new weight: " .. scaled_weight .. "\n") + --io.write(" was duration: " .. segment.duration .. "\n") + --io.write(" new duration: " .. scaled_duration .. "\n") + end + + segment.weight = scaled_weight + segment.duration = scaled_duration +end + +local function parse_maxspeed(source) + if not source then + return 0 + end + local n = tonumber(source:match("%d*")) + if not n then + n = 0 + end + if string.match(source, "mph") or string.match(source, "mp/h") then + n = (n*1609)/1000 + end + return n +end + +function process_node(profile, node, result) + -- io.write("process_node\n") + -- parse access and barrier tags + local highway = node:get_value_by_key("highway") + local is_crossing = highway and highway == "crossing" + + local access = find_access_tag(node, profile.access_tags_hierarchy) + if access and access ~= "" then + -- access restrictions on crossing nodes are not relevant for + -- the traffic on the road + if profile.access_tag_blacklist[access] and not is_crossing then + result.barrier = true + end + else + local barrier = node:get_value_by_key("barrier") + if barrier and "" ~= barrier then + if profile.barrier_blacklist[barrier] then + result.barrier = true + end + end + end + + -- check if node is a traffic light + local tag = node:get_value_by_key("highway") + if tag and "traffic_signals" == tag then + result.traffic_lights = true + end +end + +function handle_bicycle_tags(profile,way,result,data) + -- initial routability check, filters out buildings, boundaries, etc + data.route = way:get_value_by_key("route") + data.man_made = way:get_value_by_key("man_made") + data.railway = way:get_value_by_key("railway") + data.amenity = way:get_value_by_key("amenity") + data.public_transport = way:get_value_by_key("public_transport") + data.bridge = way:get_value_by_key("bridge") + + if (not data.highway or data.highway == '') and + (not data.route or data.route == '') and + (not profile.use_public_transport or not data.railway or data.railway=='') and + (not data.amenity or data.amenity=='') and + (not data.man_made or data.man_made=='') and + (not data.public_transport or data.public_transport=='') and + (not data.bridge or data.bridge=='') + then + return false + end + + -- access + data.access = find_access_tag(way, profile.access_tags_hierarchy) + if data.access and profile.access_tag_blacklist[data.access] then + return false + end + + -- other tags + data.junction = way:get_value_by_key("junction") + data.maxspeed = parse_maxspeed(way:get_value_by_key ( "maxspeed") ) + data.maxspeed_forward = parse_maxspeed(way:get_value_by_key( "maxspeed:forward")) + data.maxspeed_backward = parse_maxspeed(way:get_value_by_key( "maxspeed:backward")) + data.barrier = way:get_value_by_key("barrier") + data.oneway = way:get_value_by_key("oneway") + data.oneway_bicycle = way:get_value_by_key("oneway:bicycle") + data.cycleway = way:get_value_by_key("cycleway") + data.cycleway_left = way:get_value_by_key("cycleway:left") + data.cycleway_right = way:get_value_by_key("cycleway:right") + data.duration = way:get_value_by_key("duration") + data.service = way:get_value_by_key("service") + data.foot = way:get_value_by_key("foot") + data.foot_forward = way:get_value_by_key("foot:forward") + data.foot_backward = way:get_value_by_key("foot:backward") + data.bicycle = way:get_value_by_key("bicycle") + + speed_handler(profile,way,result,data) + + oneway_handler(profile,way,result,data) + + cycleway_handler(profile,way,result,data) + + bike_push_handler(profile,way,result,data) + + + -- maxspeed + limit( result, data.maxspeed, data.maxspeed_forward, data.maxspeed_backward ) + + -- not routable if no speed assigned + -- this avoid assertions in debug builds + if result.forward_speed <= 0 and result.duration <= 0 then + result.forward_mode = mode.inaccessible + end + if result.backward_speed <= 0 and result.duration <= 0 then + result.backward_mode = mode.inaccessible + end + + safety_handler(profile,way,result,data) +end + +function speed_handler(profile,way,result,data) + + data.way_type_allows_pushing = false + + -- speed + local bridge_speed = profile.bridge_speeds[data.bridge] + if (bridge_speed and bridge_speed > 0) then + data.highway = data.bridge + if data.duration and durationIsValid(data.duration) then + result.duration = math.max( parseDuration(data.duration), 1 ) + end + result.forward_speed = bridge_speed + result.backward_speed = bridge_speed + data.way_type_allows_pushing = true + elseif profile.route_speeds[data.route] then + -- ferries (doesn't cover routes tagged using relations) + result.forward_mode = mode.ferry + result.backward_mode = mode.ferry + if data.duration and durationIsValid(data.duration) then + result.duration = math.max( 1, parseDuration(data.duration) ) + else + result.forward_speed = profile.route_speeds[data.route] + result.backward_speed = profile.route_speeds[data.route] + end + -- railway platforms (old tagging scheme) + elseif data.railway and profile.platform_speeds[data.railway] then + result.forward_speed = profile.platform_speeds[data.railway] + result.backward_speed = profile.platform_speeds[data.railway] + data.way_type_allows_pushing = true + -- public_transport platforms (new tagging platform) + elseif data.public_transport and profile.platform_speeds[data.public_transport] then + result.forward_speed = profile.platform_speeds[data.public_transport] + result.backward_speed = profile.platform_speeds[data.public_transport] + data.way_type_allows_pushing = true + -- railways + elseif profile.use_public_transport and data.railway and profile.railway_speeds[data.railway] and profile.access_tag_whitelist[data.access] then + result.forward_mode = mode.train + result.backward_mode = mode.train + result.forward_speed = profile.railway_speeds[data.railway] + result.backward_speed = profile.railway_speeds[data.railway] + elseif data.amenity and profile.amenity_speeds[data.amenity] then + -- parking areas + result.forward_speed = profile.amenity_speeds[data.amenity] + result.backward_speed = profile.amenity_speeds[data.amenity] + data.way_type_allows_pushing = true + elseif profile.bicycle_speeds[data.highway] then + -- regular ways + result.forward_speed = profile.bicycle_speeds[data.highway] + result.backward_speed = profile.bicycle_speeds[data.highway] + data.way_type_allows_pushing = true + elseif data.access and profile.access_tag_whitelist[data.access] then + -- unknown way, but valid access tag + result.forward_speed = profile.default_speed + result.backward_speed = profile.default_speed + data.way_type_allows_pushing = true + end +end + +function oneway_handler(profile,way,result,data) + -- oneway + data.implied_oneway = data.junction == "roundabout" or data.junction == "circular" or data.highway == "motorway" + data.reverse = false + + if data.oneway_bicycle == "yes" or data.oneway_bicycle == "1" or data.oneway_bicycle == "true" then + result.backward_mode = mode.inaccessible + elseif data.oneway_bicycle == "no" or data.oneway_bicycle == "0" or data.oneway_bicycle == "false" then + -- prevent other cases + elseif data.oneway_bicycle == "-1" then + result.forward_mode = mode.inaccessible + data.reverse = true + elseif data.oneway == "yes" or data.oneway == "1" or data.oneway == "true" then + result.backward_mode = mode.inaccessible + elseif data.oneway == "no" or data.oneway == "0" or data.oneway == "false" then + -- prevent other cases + elseif data.oneway == "-1" then + result.forward_mode = mode.inaccessible + data.reverse = true + elseif data.implied_oneway then + result.backward_mode = mode.inaccessible + end +end + +function cycleway_handler(profile,way,result,data) + -- cycleway + data.has_cycleway_forward = false + data.has_cycleway_backward = false + data.is_twoway = result.forward_mode ~= mode.inaccessible and result.backward_mode ~= mode.inaccessible and not data.implied_oneway + + -- cycleways on normal roads + if data.is_twoway then + if data.cycleway and profile.cycleway_tags[data.cycleway] then + data.has_cycleway_backward = true + data.has_cycleway_forward = true + end + if (data.cycleway_right and profile.cycleway_tags[data.cycleway_right]) or (data.cycleway_left and profile.opposite_cycleway_tags[data.cycleway_left]) then + data.has_cycleway_forward = true + end + if (data.cycleway_left and profile.cycleway_tags[data.cycleway_left]) or (data.cycleway_right and profile.opposite_cycleway_tags[data.cycleway_right]) then + data.has_cycleway_backward = true + end + else + local has_twoway_cycleway = (data.cycleway and profile.opposite_cycleway_tags[data.cycleway]) or (data.cycleway_right and profile.opposite_cycleway_tags[data.cycleway_right]) or (data.cycleway_left and profile.opposite_cycleway_tags[data.cycleway_left]) + local has_opposite_cycleway = (data.cycleway_left and profile.opposite_cycleway_tags[data.cycleway_left]) or (data.cycleway_right and profile.opposite_cycleway_tags[data.cycleway_right]) + local has_oneway_cycleway = (data.cycleway and profile.cycleway_tags[data.cycleway]) or (data.cycleway_right and profile.cycleway_tags[data.cycleway_right]) or (data.cycleway_left and profile.cycleway_tags[data.cycleway_left]) + + -- set cycleway even though it is an one-way if opposite is tagged + if has_twoway_cycleway then + data.has_cycleway_backward = true + data.has_cycleway_forward = true + elseif has_opposite_cycleway then + if not data.reverse then + data.has_cycleway_backward = true + else + data.has_cycleway_forward = true + end + elseif has_oneway_cycleway then + if not data.reverse then + data.has_cycleway_forward = true + else + data.has_cycleway_backward = true + end + + end + end + + if data.has_cycleway_backward then + result.backward_mode = mode.cycling + result.backward_speed = profile.bicycle_speeds["cycleway"] + end + + if data.has_cycleway_forward then + result.forward_mode = mode.cycling + result.forward_speed = profile.bicycle_speeds["cycleway"] + end +end + +function bike_push_handler(profile,way,result,data) + -- pushing bikes - if no other mode found + if result.forward_mode == mode.inaccessible or result.backward_mode == mode.inaccessible or + result.forward_speed == -1 or result.backward_speed == -1 then + if data.foot ~= 'no' then + local push_forward_speed = nil + local push_backward_speed = nil + + if profile.pedestrian_speeds[data.highway] then + push_forward_speed = profile.pedestrian_speeds[data.highway] + push_backward_speed = profile.pedestrian_speeds[data.highway] + elseif data.man_made and profile.man_made_speeds[data.man_made] then + push_forward_speed = profile.man_made_speeds[data.man_made] + push_backward_speed = profile.man_made_speeds[data.man_made] + else + if data.foot == 'yes' then + push_forward_speed = profile.walking_speed + if not data.implied_oneway then + push_backward_speed = profile.walking_speed + end + elseif data.foot_forward == 'yes' then + push_forward_speed = profile.walking_speed + elseif data.foot_backward == 'yes' then + push_backward_speed = profile.walking_speed + elseif data.way_type_allows_pushing then + push_forward_speed = profile.walking_speed + if not data.implied_oneway then + push_backward_speed = profile.walking_speed + end + end + end + + if push_forward_speed and (result.forward_mode == mode.inaccessible or result.forward_speed == -1) then + result.forward_mode = mode.pushing_bike + result.forward_speed = push_forward_speed + end + if push_backward_speed and (result.backward_mode == mode.inaccessible or result.backward_speed == -1)then + result.backward_mode = mode.pushing_bike + result.backward_speed = push_backward_speed + end + + end + + end + + -- dismount + if data.bicycle == "dismount" then + result.forward_mode = mode.pushing_bike + result.backward_mode = mode.pushing_bike + result.forward_speed = profile.walking_speed + result.backward_speed = profile.walking_speed + end +end + +function safety_handler(profile,way,result,data) + -- convert duration into cyclability + if profile.properties.weight_name == 'cyclability' then + local safety_penalty = profile.unsafe_highway_list[data.highway] or 1. + local is_unsafe = safety_penalty < 1 + + -- primaries that are one ways are probably huge primaries where the lanes need to be separated + if is_unsafe and data.highway == 'primary' and not data.is_twoway then + safety_penalty = safety_penalty * 0.5 + end + if is_unsafe and data.highway == 'secondary' and not data.is_twoway then + safety_penalty = safety_penalty * 0.6 + end + + local forward_is_unsafe = is_unsafe and not data.has_cycleway_forward + local backward_is_unsafe = is_unsafe and not data.has_cycleway_backward + local is_undesireable = data.highway == "service" and profile.service_penalties[data.service] + local forward_penalty = 1. + local backward_penalty = 1. + if forward_is_unsafe then + forward_penalty = math.min(forward_penalty, safety_penalty) + end + if backward_is_unsafe then + backward_penalty = math.min(backward_penalty, safety_penalty) + end + + if is_undesireable then + forward_penalty = math.min(forward_penalty, profile.service_penalties[data.service]) + backward_penalty = math.min(backward_penalty, profile.service_penalties[data.service]) + end + + if result.forward_speed > 0 then + -- convert from km/h to m/s + result.forward_rate = result.forward_speed / 3.6 * forward_penalty + end + if result.backward_speed > 0 then + -- convert from km/h to m/s + result.backward_rate = result.backward_speed / 3.6 * backward_penalty + end + if result.duration > 0 then + result.weight = result.duration / forward_penalty + end + + if data.highway == "bicycle" then + safety_bonus = safety_bonus + 0.2 + if result.forward_speed > 0 then + -- convert from km/h to m/s + result.forward_rate = result.forward_speed / 3.6 * safety_bonus + end + if result.backward_speed > 0 then + -- convert from km/h to m/s + result.backward_rate = result.backward_speed / 3.6 * safety_bonus + end + if result.duration > 0 then + result.weight = result.duration / safety_bonus + end + end + end +end + +function process_way(profile, way, result) + -- the initial filtering of ways based on presence of tags + -- affects processing times significantly, because all ways + -- have to be checked. + -- to increase performance, prefetching and initial tag check + -- is done directly instead of via a handler. + + -- in general we should try to abort as soon as + -- possible if the way is not routable, to avoid doing + -- unnecessary work. this implies we should check things that + -- commonly forbids access early, and handle edge cases later. + + -- data table for storing intermediate values during processing + + --io.write("process_way\n") + + local data = { + -- prefetch tags + highway = way:get_value_by_key('highway'), + + route = nil, + man_made = nil, + railway = nil, + amenity = nil, + public_transport = nil, + bridge = nil, + + access = nil, + + junction = nil, + maxspeed = nil, + maxspeed_forward = nil, + maxspeed_backward = nil, + barrier = nil, + oneway = nil, + oneway_bicycle = nil, + cycleway = nil, + cycleway_left = nil, + cycleway_right = nil, + duration = nil, + service = nil, + foot = nil, + foot_forward = nil, + foot_backward = nil, + bicycle = nil, + + way_type_allows_pushing = false, + has_cycleway_forward = false, + has_cycleway_backward = false, + is_twoway = true, + reverse = false, + implied_oneway = false + } + + local handlers = Sequence { + -- set the default mode for this profile. if can be changed later + -- in case it turns we're e.g. on a ferry + WayHandlers.default_mode, + + -- check various tags that could indicate that the way is not + -- routable. this includes things like status=impassable, + -- toll=yes and oneway=reversible + WayHandlers.blocked_ways, + + -- our main handler + handle_bicycle_tags, + + -- compute speed taking into account way type, maxspeed tags, etc. + WayHandlers.surface, + + -- handle turn lanes and road classification, used for guidance + WayHandlers.classification, + + -- handle allowed start/end modes + WayHandlers.startpoint, + + -- handle roundabouts + WayHandlers.roundabouts, + + -- set name, ref and pronunciation + WayHandlers.names, + + -- set classes + WayHandlers.classes, + + -- set weight properties of the way + WayHandlers.weights + } + + WayHandlers.run(profile, way, result, data, handlers) +end + +function process_turn(profile, turn) + -- compute turn penalty as angle^2, with a left/right bias + local normalized_angle = turn.angle / 90.0 + if normalized_angle >= 0.0 then + turn.duration = normalized_angle * normalized_angle * profile.turn_penalty / profile.turn_bias + else + turn.duration = normalized_angle * normalized_angle * profile.turn_penalty * profile.turn_bias + end + + if turn.is_u_turn then + turn.duration = turn.duration + profile.properties.u_turn_penalty + end + + if turn.has_traffic_light then + turn.duration = turn.duration + profile.properties.traffic_light_penalty + end + if profile.properties.weight_name == 'cyclability' then + turn.weight = turn.duration + end + if turn.source_mode == mode.cycling and turn.target_mode ~= mode.cycling then + turn.weight = turn.weight + profile.properties.mode_change_penalty + end +end + +return { + setup = setup, + process_way = process_way, + process_segment = process_segment, + process_node = process_node, + process_turn = process_turn +} diff --git a/profiles/examples/elevation_aware_bicycle/generate_rastersource.py b/profiles/examples/elevation_aware_bicycle/generate_rastersource.py new file mode 100644 index 000000000..b4b8d99e4 --- /dev/null +++ b/profiles/examples/elevation_aware_bicycle/generate_rastersource.py @@ -0,0 +1,9 @@ +import numpy as np + +# SRTM Elevation Raster Tiles: http://srtm.csi.cgiar.org/srtmdata/ +# This example fuses the two srtm tiles covering Switzerland. +# Change this python script as needed by arranging any number of tiles with `np.hstack` and `np.vstack`. +srtm_39_03 = np.loadtxt("srtm_39_03.asc", skiprows=6, dtype=np.int) +srtm_38_03 = np.loadtxt("srtm_38_03.asc", skiprows=6, dtype=np.int) +rastersource = np.hstack((srtm_38_03, srtm_39_03)) +np.savetxt("rastersource.asc", rastersource, fmt="%d") \ No newline at end of file diff --git a/profiles/examples/elevation_aware_bicycle/speed_by_elevation_of_bicycle/bike_formula.py b/profiles/examples/elevation_aware_bicycle/speed_by_elevation_of_bicycle/bike_formula.py new file mode 100644 index 000000000..c9781b6ae --- /dev/null +++ b/profiles/examples/elevation_aware_bicycle/speed_by_elevation_of_bicycle/bike_formula.py @@ -0,0 +1,46 @@ +import numpy as np +import matplotlib.pyplot as plt + +## bike modelling + +P = 150 # W +l = 1000 # m +t = 1000/5 # s +m = 70 #kg +g = 9.81 #m / s ** 2 +N = 100 +min_slope = -0.25 +max_slope = 0.25 + +c2 = m*g +s_bar = np.linspace(min_slope, max_slope, N) +t_scaled = np.empty(N) +for i in range(N): + for root in np.roots([P*(t**3)/l, -s_bar[i]*c2*(t**2), 0, -P * t ** 3 / l]): + if np.isreal(root): + t_scaled[i] = root + + +# export the function of scaled time to the lua file +print("list = {", end='') +for t_elem in t_scaled: + print(t_elem, end='') + if t_elem != t_scaled[-1]: + print(",", end='') +print("}") +print("N =", N) +print("min_slope =", min_slope) +print("max_slope =", max_slope) + +# see a plot with the generated function of scaled time. + +t_simple = 1 / (1 - s_bar * 5.0) + +plt.plot(s_bar, t_scaled, s_bar, t_simple) +plt.ylim([-1, 6]) +plt.title("How much longer a bicycle takes when going uphill") +plt.xlabel('$slope [d_{vertical}/d_{horizontal}]$') +plt.ylabel('$time [t_{withelevation} / t_{flat}]$') +plt.legend(['$t_{proper}$', '$t_{simple}$']) +plt.savefig('bike_slope.png') +plt.show() \ No newline at end of file diff --git a/profiles/examples/elevation_aware_bicycle/speed_by_elevation_of_bicycle/bike_slope.png b/profiles/examples/elevation_aware_bicycle/speed_by_elevation_of_bicycle/bike_slope.png new file mode 100644 index 000000000..445f8a0a3 Binary files /dev/null and b/profiles/examples/elevation_aware_bicycle/speed_by_elevation_of_bicycle/bike_slope.png differ