diff --git a/profiles/fuel.lua b/profiles/fuel.lua new file mode 100644 index 000000000..56dfcf411 --- /dev/null +++ b/profiles/fuel.lua @@ -0,0 +1,525 @@ +-- Fuel profile + +api_version = 4 + +Set = require('lib/set') +Sequence = require('lib/sequence') +Handlers = require("lib/way_handlers") +Relations = require("lib/relations") +find_access_tag = require("lib/access").find_access_tag +limit = require("lib/maxspeed").limit +Utils = require("lib/utils") +Measure = require("lib/measure") + +-- By vinay +-- local Debug = require("lib/profile_debugger") +-- local pprint = require('lib/pprint') + +-- Debug.load_profile('fuel') + +function setup() + return { + properties = { + max_speed_for_map_matching = 180/3.6, -- 180kmph -> m/s + -- For routing based on duration, but weighted for preferring certain roads + weight_name = 'routability', + -- For shortest duration without penalties for accessibility + -- weight_name = 'duration', + -- For shortest distance without penalties for accessibility + -- weight_name = 'distance', + process_call_tagless_node = false, + u_turn_penalty = 20, + continue_straight_at_waypoint = true, + use_turn_restrictions = true, + left_hand_driving = false, + traffic_light_penalty = 2, + }, + + default_mode = mode.driving, + default_speed = 10, + oneway_handling = true, + side_road_multiplier = 0.8, + turn_penalty = 7.5, + speed_reduction = 0.8, + turn_bias = 1.075, + cardinal_directions = false, + + -- Size of the vehicle, to be limited by physical restriction of the way + vehicle_height = 2.5, -- in meters, 2.5m is the height of van + vehicle_width = 1.9, -- in meters, ways with narrow tag are considered narrower than 2.2m + + -- Size of the vehicle, to be limited mostly by legal restriction of the way + vehicle_length = 4.8, -- in meters, 4.8m is the length of large or familly car + vehicle_weight = 2000, -- in kilograms + + -- a list of suffixes to suppress in name change instructions. The suffixes also include common substrings of each other + suffix_list = { + 'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East', 'Nor', 'Sou', 'We', 'Ea' + }, + + barrier_whitelist = Set { + 'cattle_grid', + 'border_control', + 'toll_booth', + 'sally_port', + 'gate', + 'lift_gate', + 'no', + 'entrance', + 'height_restrictor', + 'arch' + }, + + access_tag_whitelist = Set { + 'yes', + 'motorcar', + 'motor_vehicle', + 'vehicle', + 'permissive', + 'designated', + 'hov' + }, + + access_tag_blacklist = Set { + 'no', + 'agricultural', + 'forestry', + 'emergency', + 'psv', + 'customers', + 'private', + 'delivery', + 'destination' + }, + + -- tags disallow access to in combination with highway=service + service_access_tag_blacklist = Set { + 'private' + }, + + restricted_access_tag_list = Set { + 'private', + 'delivery', + 'destination', + 'customers', + }, + + access_tags_hierarchy = Sequence { + 'motorcar', + 'motor_vehicle', + 'vehicle', + 'access' + }, + + service_tag_forbidden = Set { + 'emergency_access' + }, + + restrictions = Sequence { + 'motorcar', + 'motor_vehicle', + 'vehicle' + }, + + classes = Sequence { + 'toll', 'motorway', 'ferry', 'restricted', 'tunnel' + }, + + -- classes to support for exclude flags + excludable = Sequence { + Set {'toll'}, + Set {'motorway'}, + Set {'ferry'} + }, + + avoid = Set { + 'area', + -- 'toll', -- uncomment this to avoid tolls + 'reversible', + 'impassable', + 'hov_lanes', + 'steps', + 'construction', + 'proposed' + }, + + speeds = Sequence { + highway = { + motorway = 90, + motorway_link = 45, + trunk = 85, + trunk_link = 40, + primary = 65, + primary_link = 30, + secondary = 55, + secondary_link = 25, + tertiary = 40, + tertiary_link = 20, + unclassified = 25, + residential = 25, + living_street = 10, + service = 15 + } + }, + + service_penalties = { + alley = 0.5, + parking = 0.5, + parking_aisle = 0.5, + driveway = 0.5, + ["drive-through"] = 0.5, + ["drive-thru"] = 0.5 + }, + + restricted_highway_whitelist = Set { + 'motorway', + 'motorway_link', + 'trunk', + 'trunk_link', + 'primary', + 'primary_link', + 'secondary', + 'secondary_link', + 'tertiary', + 'tertiary_link', + 'residential', + 'living_street', + 'unclassified', + 'service' + }, + + construction_whitelist = Set { + 'no', + 'widening', + 'minor', + }, + + route_speeds = { + ferry = 5, + shuttle_train = 10 + }, + + bridge_speeds = { + movable = 5 + }, + + -- surface/trackype/smoothness + -- values were estimated from looking at the photos at the relevant wiki pages + + -- max speed for surfaces + surface_speeds = { + asphalt = nil, -- nil mean no limit. removing the line has the same effect + concrete = nil, + ["concrete:plates"] = nil, + ["concrete:lanes"] = nil, + paved = nil, + + cement = 80, + compacted = 80, + fine_gravel = 80, + + paving_stones = 60, + metal = 60, + bricks = 60, + + grass = 40, + wood = 40, + sett = 40, + grass_paver = 40, + gravel = 40, + unpaved = 40, + ground = 40, + dirt = 40, + pebblestone = 40, + tartan = 40, + + cobblestone = 30, + clay = 30, + + earth = 20, + stone = 20, + rocky = 20, + sand = 20, + + mud = 10 + }, + + -- max speed for tracktypes + tracktype_speeds = { + grade1 = 60, + grade2 = 40, + grade3 = 30, + grade4 = 25, + grade5 = 20 + }, + + -- max speed for smoothnesses + smoothness_speeds = { + intermediate = 80, + bad = 40, + very_bad = 20, + horrible = 10, + very_horrible = 5, + impassable = 0 + }, + + -- http://wiki.openstreetmap.org/wiki/Speed_limits + maxspeed_table_default = { + urban = 50, + rural = 90, + trunk = 110, + motorway = 130 + }, + + -- List only exceptions + maxspeed_table = { + ["at:rural"] = 100, + ["at:trunk"] = 100, + ["be:motorway"] = 120, + ["be-vlg:rural"] = 70, + ["by:urban"] = 60, + ["by:motorway"] = 110, + ["ch:rural"] = 80, + ["ch:trunk"] = 100, + ["ch:motorway"] = 120, + ["cz:trunk"] = 0, + ["cz:motorway"] = 0, + ["de:living_street"] = 7, + ["de:rural"] = 100, + ["de:motorway"] = 0, + ["dk:rural"] = 80, + ["fr:rural"] = 80, + ["gb:nsl_single"] = (60*1609)/1000, + ["gb:nsl_dual"] = (70*1609)/1000, + ["gb:motorway"] = (70*1609)/1000, + ["nl:rural"] = 80, + ["nl:trunk"] = 100, + ['no:rural'] = 80, + ['no:motorway'] = 110, + ['pl:rural'] = 100, + ['pl:trunk'] = 120, + ['pl:motorway'] = 140, + ["ro:trunk"] = 100, + ["ru:living_street"] = 20, + ["ru:urban"] = 60, + ["ru:motorway"] = 110, + ["uk:nsl_single"] = (60*1609)/1000, + ["uk:nsl_dual"] = (70*1609)/1000, + ["uk:motorway"] = (70*1609)/1000, + ['za:urban'] = 60, + ['za:rural'] = 100, + ["none"] = 140 + }, + + relation_types = Sequence { + "route" + }, + + -- classify highway tags when necessary for turn weights + highway_turn_classification = { + }, + + -- classify access tags when necessary for turn weights + access_turn_classification = { + } + } +end + +function process_node(profile, node, result, relations) + -- parse access and barrier tags + local access = find_access_tag(node, profile.access_tags_hierarchy) + if access then + if profile.access_tag_blacklist[access] and not profile.restricted_access_tag_list[access] then + result.barrier = true + end + else + local barrier = node:get_value_by_key("barrier") + if barrier then + -- check height restriction barriers + local restricted_by_height = false + if barrier == 'height_restrictor' then + local maxheight = Measure.get_max_height(node:get_value_by_key("maxheight"), node) + restricted_by_height = maxheight and maxheight < profile.vehicle_height + end + + -- make an exception for rising bollard barriers + local bollard = node:get_value_by_key("bollard") + local rising_bollard = bollard and "rising" == bollard + + if not profile.barrier_whitelist[barrier] and not rising_bollard or restricted_by_height then + result.barrier = true + end + end + end + + -- check if node is a traffic light + local tag = node:get_value_by_key("highway") + if "traffic_signals" == tag then + result.traffic_lights = true + end +end + +function process_way(profile, way, result, relations) + -- the intial filtering of ways based on presence of tags + -- affects processing times significantly, because all ways + -- have to be checked. + -- to increase performance, prefetching and intial tag check + -- is done in 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 + local data = { + -- prefetch tags + highway = way:get_value_by_key('highway'), + bridge = way:get_value_by_key('bridge'), + route = way:get_value_by_key('route') + } + + -- perform an quick initial check and abort if the way is + -- obviously not routable. + -- highway or route tags must be in data table, bridge is optional + if (not data.highway or data.highway == '') and + (not data.route or data.route == '') + then + return + end + + 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, + WayHandlers.avoid_ways, + WayHandlers.handle_height, + WayHandlers.handle_width, + WayHandlers.handle_length, + WayHandlers.handle_weight, + + -- determine access status by checking our hierarchy of + -- access tags, e.g: motorcar, motor_vehicle, vehicle + WayHandlers.access, + + -- check whether forward/backward directions are routable + WayHandlers.oneway, + + -- check a road's destination + WayHandlers.destinations, + + -- check whether we're using a special transport mode + WayHandlers.ferries, + WayHandlers.movables, + + -- handle service road restrictions + WayHandlers.service, + + -- handle hov + WayHandlers.hov, + + -- compute speed taking into account way type, maxspeed tags, etc. + WayHandlers.speed, + WayHandlers.surface, + WayHandlers.maxspeed, + WayHandlers.penalties, -- commented by vinay + + -- compute class labels + WayHandlers.classes, + + -- handle turn lanes and road classification, used for guidance + WayHandlers.turn_lanes, + WayHandlers.classification, + + -- handle various other flags + WayHandlers.roundabouts, + WayHandlers.startpoint, + WayHandlers.driving_side, + + -- set name, ref and pronunciation + WayHandlers.names, + + -- set weight properties of the way + WayHandlers.weights, + + -- set classification of ways relevant for turns + WayHandlers.way_classification_for_turn + } + + WayHandlers.run(profile, way, result, data, handlers, relations) + + if profile.cardinal_directions then + Relations.process_way_refs(way, relations, result) + end + + --print(result.forward_speed) + --print("s=>r") + --print(result.forward_rate) + --print("\n") + + +end + +function process_turn(profile, turn) + -- Use a sigmoid function to return a penalty that maxes out at turn_penalty + -- over the space of 0-180 degrees. Values here were chosen by fitting + -- the function to some turn penalty samples from real driving. + local turn_penalty = profile.turn_penalty + local turn_bias = turn.is_left_hand_driving and 1. / profile.turn_bias or profile.turn_bias + + if turn.has_traffic_light then + turn.duration = profile.properties.traffic_light_penalty + end + + if turn.number_of_roads > 2 or turn.source_mode ~= turn.target_mode or turn.is_u_turn then + if turn.angle >= 0 then + turn.duration = turn.duration + turn_penalty / (1 + math.exp( -((13 / turn_bias) * turn.angle/180 - 6.5*turn_bias))) + else + turn.duration = turn.duration + turn_penalty / (1 + math.exp( -((13 * turn_bias) * -turn.angle/180 - 6.5/turn_bias))) + end + + if turn.is_u_turn then + turn.duration = turn.duration + profile.properties.u_turn_penalty + end + end + + -- for distance based routing we don't want to have penalties based on turn angle + if profile.properties.weight_name == 'distance' then + turn.weight = 0 + else + turn.weight = 0 + end + + if profile.properties.weight_name == 'routability' then + -- penalize turns from non-local access only segments onto local access only tags + if not turn.source_restricted and turn.target_restricted then + turn.weight = constants.max_turn_weight + end + end +end + + +function process_segment (profile, segment) + local edge_distance = segment.distance + local edge_duration = segment.duration + + local velocity = (edge_distance / edge_duration) * 3.6 -- calculating velocity along the edge in km/h + + local fuel_per_km = 54.7+(496/velocity) - 0.542*velocity + 0.00042*velocity*velocity + + segment.weight = (fuel_per_km/1000) * edge_distance + +end + +return { + setup = setup, + process_way = process_way, + process_node = process_node, + process_turn = process_turn, + process_segment = process_segment +}