Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Take stop and give way signs into account during routing #6426

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
8a4af59
wip
SiarheiFedartsou Oct 27, 2022
7f635f2
Take stop signs into account during routing
SiarheiFedartsou Oct 27, 2022
72ea34f
Take stop signs into account during routing
SiarheiFedartsou Oct 28, 2022
36f3a5e
Take stop signs into account during routing
SiarheiFedartsou Oct 28, 2022
3558ec9
Take stop signs into account during routing
SiarheiFedartsou Oct 28, 2022
4432756
Take stop signs into account during routing
SiarheiFedartsou Oct 28, 2022
350862d
Take stop signs into account during routing
SiarheiFedartsou Oct 28, 2022
9788317
Take stop signs into account during routing
SiarheiFedartsou Oct 28, 2022
c28a683
Take stop signs into account during routing
SiarheiFedartsou Oct 30, 2022
62298cf
Take stop signs into account during routing
SiarheiFedartsou Oct 31, 2022
be1b866
Take stop signs into account during routing
SiarheiFedartsou Oct 31, 2022
b887e72
Take stop signs into account during routing
SiarheiFedartsou Oct 31, 2022
eb72ca4
Merge branch 'master' into sf-stop-sign
SiarheiFedartsou Oct 31, 2022
a7142ee
Take stop signs into account during routing
SiarheiFedartsou Oct 31, 2022
3080be5
Take stop signs into account during routing
SiarheiFedartsou Oct 31, 2022
ee008e7
Take stop signs into account during routing
SiarheiFedartsou Oct 31, 2022
7508262
Take stop signs into account during routing
SiarheiFedartsou Oct 31, 2022
3f7a629
Take stop signs into account during routing
SiarheiFedartsou Oct 31, 2022
04ee126
Take stop signs into account during routing
SiarheiFedartsou Oct 31, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions features/car/stop_sign_penalties.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
@routing @car @stop_sign
Copy link
Member Author

@SiarheiFedartsou SiarheiFedartsou Oct 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @mjjbell !

May I ask you to take a look here?
What I actually did is took our existing traffic lights logic and completely repeated it for stop signs. There is a lot of things to be done here(at least we need to get rid of copy-paste), but before starting doing it, would be great to have your opinion here. Don't you see any fundamental problems in this approach? I am not very familiar with OSRM internals yet(one of the goals of this PR is to educate myself :) ) and tbh some parts of PR were "blindly" copy-pasted from traffic lights, so asking.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes if we're viewing all of them as node penalties applicable by direction, then it's basically the same code.

If we wanted to model the impact of each sign/traffic light more accurately (e.g. junction size, unprotected turns, all the other stuff in #2652, etc) then we'd probably want to handle them differently, but I don't think we're at that stage yet.

Feature: Car - Handle stop signs

Background:
Given the profile "car"

Scenario: Car - Encounters a stop sign
Given the node map
"""
a-1-b-2-c

d-3-e-4-f

g-h-i k-l-m
| |
j n

"""

And the ways
| nodes | highway |
| abc | primary |
| def | primary |
| ghi | primary |
| klm | primary |
| hj | primary |
| ln | primary |

And the nodes
| node | highway |
| e | stop |
| l | stop |

When I route I should get
| from | to | time | # |
| 1 | 2 | 11.1s | no turn with no stop sign |
| 3 | 4 | 13.1s | no turn with stop sign |
| g | j | 18.7s | turn with no stop sign |
| k | n | 20.7s | turn with stop sign |
2 changes: 1 addition & 1 deletion features/support/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ module.exports = function () {
.defer(rimraf, this.scenarioLogFile)
.awaitAll(callback);
// uncomment to get path to logfile
// console.log(' Writing logging output to ' + this.scenarioLogFile);
console.log(' Writing logging output to ' + this.scenarioLogFile);
});

this.After((scenario, callback) => {
Expand Down
3 changes: 3 additions & 0 deletions include/extractor/edge_based_graph_factory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class EdgeBasedGraphFactory
const CompressedEdgeContainer &compressed_edge_container,
const std::unordered_set<NodeID> &barrier_nodes,
const TrafficSignals &traffic_signals,
const StopSigns &stop_signs,
const std::vector<util::Coordinate> &coordinates,
const NameTable &name_table,
const std::unordered_set<EdgeID> &segregated_edges,
Expand Down Expand Up @@ -136,6 +137,8 @@ class EdgeBasedGraphFactory

const std::unordered_set<NodeID> &m_barrier_nodes;
const TrafficSignals &m_traffic_signals;
const StopSigns &m_stop_signs;

const CompressedEdgeContainer &m_compressed_edge_container;

const NameTable &name_table;
Expand Down
16 changes: 16 additions & 0 deletions include/extractor/extraction_containers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,24 @@ class ExtractionContainers
using ReferencedWays = std::unordered_map<OSMWayID, NodesOfWay>;
using ReferencedTrafficSignals =
std::pair<std::unordered_set<OSMNodeID>, std::unordered_multimap<OSMNodeID, OSMNodeID>>;
using ReferencedStopSigns =
std::pair<std::unordered_set<OSMNodeID>, std::unordered_multimap<OSMNodeID, OSMNodeID>>;

// The relationship between way and nodes is lost during node preparation.
// We identify the ways and nodes relevant to restrictions/overrides/signals prior to
// node processing so that they can be referenced in the preparation phase.
ReferencedWays IdentifyRestrictionWays();
ReferencedWays IdentifyManeuverOverrideWays();
ReferencedTrafficSignals IdentifyTrafficSignals();
ReferencedStopSigns IdentifyStopSigns();


void PrepareNodes();
void PrepareManeuverOverrides(const ReferencedWays &maneuver_override_ways);
void PrepareRestrictions(const ReferencedWays &restriction_ways);
void PrepareTrafficSignals(const ReferencedTrafficSignals &referenced_traffic_signals);
void PrepareStopSigns(const ReferencedStopSigns &referenced_stop_signs);

void PrepareEdges(ScriptingEnvironment &scripting_environment);

void WriteCharData(const std::string &file_name);
Expand All @@ -55,6 +62,8 @@ class ExtractionContainers
using WayIDVector = std::vector<OSMWayID>;
using WayNodeIDOffsets = std::vector<size_t>;
using InputTrafficSignal = std::pair<OSMNodeID, TrafficLightClass::Direction>;
using InputStopSign = std::pair<OSMNodeID, StopSign::Direction>;
using InputGiveWay = std::pair<OSMNodeID, GiveWay::Direction>;

std::vector<OSMNodeID> barrier_nodes;
NodeIDVector used_node_id_list;
Expand All @@ -72,6 +81,13 @@ class ExtractionContainers
std::vector<InputTrafficSignal> external_traffic_signals;
TrafficSignals internal_traffic_signals;


std::vector<InputStopSign> external_stop_signs;
StopSigns internal_stop_signs;

std::vector<InputStopSign> external_give_ways;
GiveWaySigns internal_give_ways;

std::vector<NodeBasedEdge> used_edges;

// List of restrictions (conditional and unconditional) before we transform them into the
Expand Down
8 changes: 8 additions & 0 deletions include/extractor/extraction_node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,30 @@
#define EXTRACTION_NODE_HPP

#include "traffic_lights.hpp"
#include <cstdint>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed?


namespace osrm
{
namespace extractor
{


struct ExtractionNode
{
ExtractionNode() : traffic_lights(TrafficLightClass::NONE), barrier(false) {}
void clear()
{
traffic_lights = TrafficLightClass::NONE;
stop_sign = StopSign::Direction::NONE;
give_way = GiveWay::Direction::NONE;
barrier = false;
}
TrafficLightClass::Direction traffic_lights;
bool barrier;


StopSign::Direction stop_sign;
GiveWay::Direction give_way;
};
} // namespace extractor
} // namespace osrm
Expand Down
4 changes: 3 additions & 1 deletion include/extractor/extraction_turn.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ struct ExtractionTurn
int number_of_roads,
bool is_u_turn,
bool has_traffic_light,
bool has_stop_sign,
bool is_left_hand_driving,
bool source_restricted,
TravelMode source_mode,
Expand All @@ -75,7 +76,7 @@ struct ExtractionTurn
const std::vector<ExtractionTurnLeg> &roads_on_the_right,
const std::vector<ExtractionTurnLeg> &roads_on_the_left)
: angle(180. - angle), number_of_roads(number_of_roads), is_u_turn(is_u_turn),
has_traffic_light(has_traffic_light), is_left_hand_driving(is_left_hand_driving),
has_traffic_light(has_traffic_light), has_stop_sign(has_stop_sign), is_left_hand_driving(is_left_hand_driving),

source_restricted(source_restricted), source_mode(source_mode),
source_is_motorway(source_is_motorway), source_is_link(source_is_link),
Expand All @@ -100,6 +101,7 @@ struct ExtractionTurn
const int number_of_roads;
const bool is_u_turn;
const bool has_traffic_light;
const bool has_stop_sign;
const bool is_left_hand_driving;

// source info
Expand Down
2 changes: 2 additions & 0 deletions include/extractor/extractor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class Extractor
std::vector<TurnRestriction> turn_restrictions;
std::vector<UnresolvedManeuverOverride> unresolved_maneuver_overrides;
TrafficSignals traffic_signals;
StopSigns stop_signs;
std::unordered_set<NodeID> barriers;
std::vector<util::Coordinate> osm_coordinates;
extractor::PackedOSMIDs osm_node_ids;
Expand All @@ -87,6 +88,7 @@ class Extractor
const CompressedEdgeContainer &compressed_edge_container,
const std::unordered_set<NodeID> &barrier_nodes,
const TrafficSignals &traffic_signals,
const StopSigns &stop_signs,
const RestrictionGraph &restriction_graph,
const std::unordered_set<EdgeID> &segregated_edges,
const NameTable &name_table,
Expand Down
1 change: 1 addition & 0 deletions include/extractor/graph_compressor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class GraphCompressor
public:
void Compress(const std::unordered_set<NodeID> &barrier_nodes,
const TrafficSignals &traffic_signals,
const StopSigns &stop_signs,
ScriptingEnvironment &scripting_environment,
std::vector<TurnRestriction> &turn_restrictions,
std::vector<UnresolvedManeuverOverride> &maneuver_overrides,
Expand Down
5 changes: 4 additions & 1 deletion include/extractor/node_based_graph_factory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class NodeBasedGraphFactory
std::vector<TurnRestriction> &turn_restrictions,
std::vector<UnresolvedManeuverOverride> &maneuver_overrides,
const TrafficSignals &traffic_signals,
const StopSigns &stop_signs,

std::unordered_set<NodeID> &&barriers,
std::vector<util::Coordinate> &&coordinates,
extractor::PackedOSMIDs &&osm_node_ids,
Expand Down Expand Up @@ -73,7 +75,8 @@ class NodeBasedGraphFactory
void Compress(ScriptingEnvironment &scripting_environment,
std::vector<TurnRestriction> &turn_restrictions,
std::vector<UnresolvedManeuverOverride> &maneuver_overrides,
const TrafficSignals &traffic_signals);
const TrafficSignals &traffic_signals,
const StopSigns &stop_signs);

// Most ways are bidirectional, making the geometry in forward and backward direction the same,
// except for reversal. We make use of this fact by keeping only one representation of the
Expand Down
Empty file.
31 changes: 31 additions & 0 deletions include/extractor/traffic_lights.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef OSRM_EXTRACTOR_TRAFFIC_LIGHTS_DATA_HPP_
#define OSRM_EXTRACTOR_TRAFFIC_LIGHTS_DATA_HPP_

#include <cstdint>
namespace osrm
{
namespace extractor
Expand All @@ -19,6 +20,36 @@ enum Direction
};
} // namespace TrafficLightClass

// Stop Signs tagged on nodes can be present or not. In addition Stop Signs have
// an optional way direction they apply to. If the direction is unknown from the
// data we have to compute by checking the distance to the next intersection.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, looks like identifying the direction will require something additional to the traffic signal logic.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the direction is unknown from the data we have to compute by checking the distance to the next intersection.

Do you mean this sentence? Tbh it was a copy-paste from some of older PRs and I noticed it only now...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like ~77% of stop signs tagged with direction.

I will take a look what it will take to compute this "distance to the nearest intersection" - should be just a couple of heuristics, but not sure how to do that from code point of view, but if it is TOO difficult to be part of this PR I would probably propose to just ignore stop signs without direction for the time being and then return to it in separate PRs.

https://taginfo.openstreetmap.org/tags/highway=stop#combinations
Screenshot 2022-10-28 at 16 39 59

//
// Impl. detail: namespace + enum instead of enum class to make Luabind happy
namespace StopSign
{
enum Direction : std::uint8_t
{
NONE = 0,
DIRECTION_ALL = 1,
DIRECTION_FORWARD = 2,
DIRECTION_REVERSE = 3
};
}

// Give Way is the complement to priority roads. Tagging is the same as Stop Signs.
// See explanation above.
namespace GiveWay
{
enum Direction : std::uint8_t
{
NONE = 0,
DIRECTION_ALL = 1,
DIRECTION_FORWARD = 2,
DIRECTION_REVERSE = 3
};
}


} // namespace extractor
} // namespace osrm

Expand Down
11 changes: 10 additions & 1 deletion include/extractor/traffic_signals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ namespace osrm
namespace extractor
{

struct TrafficSignals
// TODO: better naming
struct RoadObjects
{
std::unordered_set<NodeID> bidirectional_nodes;
std::unordered_set<std::pair<NodeID, NodeID>, boost::hash<std::pair<NodeID, NodeID>>>
Expand All @@ -22,6 +23,14 @@ struct TrafficSignals
return bidirectional_nodes.count(to) > 0 || unidirectional_segments.count({from, to}) > 0;
}
};

struct TrafficSignals final : public RoadObjects {};

// TODO: better naming ?
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like we can try to generalise the whole this logic to any kind of objects which follows the same "idea" as traffic lights, i.e. some nodes which can be directed and give additional penalty in routing.

Copy link
Member

@mjjbell mjjbell Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe TrafficFlowControl?

If we view them as node penalties applicable by traversal direction, then I can see them being part of the same logical grouping.

struct StopSigns final : public RoadObjects {};
struct GiveWaySigns final : public RoadObjects {};


} // namespace extractor
} // namespace osrm

Expand Down
12 changes: 12 additions & 0 deletions profiles/car.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Sequence = require('lib/sequence')
Handlers = require("lib/way_handlers")
Relations = require("lib/relations")
TrafficSignal = require("lib/traffic_signal")
StopSign = require("lib/stop_sign")
GiveWay = require("lib/give_way")
find_access_tag = require("lib/access").find_access_tag
limit = require("lib/maxspeed").limit
Utils = require("lib/utils")
Expand Down Expand Up @@ -362,6 +364,12 @@ function process_node(profile, node, result, relations)

-- check if node is a traffic light
result.traffic_lights = TrafficSignal.get_value(node)

-- check if node is stop sign
result.stop_sign = StopSign.get_value(node)

-- check if node is a give way sign
result.give_way = GiveWay.get_value(node)
end

function process_way(profile, way, result, relations)
Expand Down Expand Up @@ -472,8 +480,12 @@ function process_turn(profile, turn)

if turn.has_traffic_light then
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially I had a question "what to do if node has traffic light and stop sign at the same time?", IMO the answer is quite simple: here we just apply one of penalties, but end users may go further and apply combination of penalties in this case if they want or change their priority.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think applying the max penalty out of the three would make sense to me. This logic achieves that too.

turn.duration = profile.properties.traffic_light_penalty
elseif turn.has_stop_sign then
-- TODO: use another constant
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

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)))
Expand Down
23 changes: 23 additions & 0 deletions profiles/lib/give_way.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
local GiveWay = {}

function GiveWay.get_value(node)
local tag = node:get_value_by_key("highway")
if "give_way" == tag then
local direction = node:get_value_by_key("direction")
if direction then
if "forward" == direction then
return give_way.direction_forward
end
if "backward" == direction then
return give_way.direction_reverse
end
end
-- return give_way.direction_all
return true
end
-- return give_way.none
return false
end

return GiveWay

21 changes: 21 additions & 0 deletions profiles/lib/stop_sign.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
local StopSign = {}

function StopSign.get_value(node)
local tag = node:get_value_by_key("highway")
if "stop" == tag then
local direction = node:get_value_by_key("direction")
if direction then
if "forward" == direction then
return stop_sign.direction_forward
end
if "backward" == direction then
return stop_sign.direction_reverse
end
end
return stop_sign.direction_all
end
return stop_sign.none
end

return StopSign

6 changes: 5 additions & 1 deletion src/extractor/edge_based_graph_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,15 @@ EdgeBasedGraphFactory::EdgeBasedGraphFactory(
const CompressedEdgeContainer &compressed_edge_container,
const std::unordered_set<NodeID> &barrier_nodes,
const TrafficSignals &traffic_signals,
const StopSigns &stop_signs,
const std::vector<util::Coordinate> &coordinates,
const NameTable &name_table,
const std::unordered_set<EdgeID> &segregated_edges,
const extractor::LaneDescriptionMap &lane_description_map)
: m_edge_based_node_container(node_data_container), m_connectivity_checksum(0),
m_number_of_edge_based_nodes(0), m_coordinates(coordinates),
m_node_based_graph(node_based_graph), m_barrier_nodes(barrier_nodes),
m_traffic_signals(traffic_signals), m_compressed_edge_container(compressed_edge_container),
m_traffic_signals(traffic_signals), m_stop_signs(stop_signs), m_compressed_edge_container(compressed_edge_container),
name_table(name_table), segregated_edges(segregated_edges),
lane_description_map(lane_description_map)
{
Expand Down Expand Up @@ -643,6 +644,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
// the traffic signal direction was potentially ambiguously annotated on the junction
// node But we'll check anyway.
const auto is_traffic_light = m_traffic_signals.HasSignal(from_node, intersection_node);
const auto is_stop_sign = m_stop_signs.HasSignal(from_node, intersection_node);
std::cerr << "IS STOP SIGN " << is_stop_sign << std::endl;
const auto is_uturn =
guidance::getTurnDirection(turn_angle) == guidance::DirectionModifier::UTurn;

Expand All @@ -652,6 +655,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
road_legs_on_the_right.size() + road_legs_on_the_left.size() + 2 - is_uturn,
is_uturn,
is_traffic_light,
is_stop_sign,
m_edge_based_node_container.GetAnnotation(edge_data1.annotation_data)
.is_left_hand_driving,
// source info
Expand Down
Loading