Skip to content

Commit a46d09e

Browse files
committed
fix: account for road width in map matching
1 parent f984023 commit a46d09e

21 files changed

Lines changed: 507 additions & 48 deletions

‎features/testbot/matching.feature‎

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,35 @@ Feature: Basic Map Matching
159159
| trace | matchings |
160160
| afcde | abcde |
161161

162+
Scenario Outline: Testbot - Map matching accounts for road surface width from <source>
163+
Given a grid size of 4 meters
164+
Given the node map
165+
"""
166+
a b c d
167+
168+
169+
1 2 3 4
170+
e f g h
171+
"""
172+
173+
And the ways
174+
| nodes | oneway | lanes | width | width:carriageway | width:forward | width:backward | width:lanes | width:lanes:forward | width:lanes:backward |
175+
| abcd | no | <lanes> | <width> | <width_carriageway> | <width_forward> | <width_backward> | <width_lanes> | <width_lanes_forward> | <width_lanes_backward> |
176+
| efgh | no | | | | | | | | |
177+
178+
When I match I should get
179+
| trace | matchings |
180+
| 1234 | abcd |
181+
182+
Examples:
183+
| source | lanes | width | width_carriageway | width_forward | width_backward | width_lanes | width_lanes_forward | width_lanes_backward |
184+
| width | | 26 | | | | | | |
185+
| width:carriageway | | | 26 | | | | | |
186+
| width:forward/backward | | | | 13 | 13 | | | |
187+
| width:lanes | | | | | | 6.5\|6.5\|6.5\|6.5 | | |
188+
| width:lanes directions | | | | | | | 13 | 13 |
189+
| lanes fallback | 24 | | | | | | | |
190+
162191
Scenario: Testbot - Map matching with oneways
163192
Given a grid size of 10 meters
164193
Given the node map
@@ -826,4 +855,3 @@ Feature: Basic Map Matching
826855
When I match I should get
827856
| trace | matchings | confidence |
828857
| abcd | abcd | 1 ~5% |
829-

‎include/engine/datafacade/contiguous_internalmem_datafacade.hpp‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
167167
util::vector_view<TurnPenalty> m_turn_duration_penalties;
168168
extractor::SegmentDataView segment_data;
169169
extractor::EdgeBasedNodeDataView edge_based_node_data;
170+
double m_max_road_half_width = 0.;
170171
std::optional<osrm::guidance::TurnDataView> turn_data;
171172

172173
std::optional<util::vector_view<util::guidance::LaneTupleIdPair>> m_lane_tuple_id_pairs;
@@ -230,6 +231,7 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
230231
new SharedGeospatialQuery(m_static_rtree, m_coordinate_list, *this));
231232

232233
edge_based_node_data = make_ebn_data_view(index, "/common/ebg_node_data");
234+
m_max_road_half_width = edge_based_node_data.ComputeMaxRoadHalfWidth();
233235

234236
if (isIndexed(index, "/common/turn_data"))
235237
{
@@ -431,6 +433,18 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
431433
return edge_based_node_data.GetClassData(edge_based_node_id);
432434
}
433435

436+
std::uint8_t GetNumberOfLanes(const NodeID edge_based_node_id) const override final
437+
{
438+
return edge_based_node_data.GetNumberOfLanes(edge_based_node_id);
439+
}
440+
441+
double GetRoadWidth(const NodeID edge_based_node_id) const override final
442+
{
443+
return edge_based_node_data.GetRoadWidth(edge_based_node_id);
444+
}
445+
446+
double GetMaxRoadHalfWidth() const override final { return m_max_road_half_width; }
447+
434448
bool ExcludeNode(const NodeID edge_based_node_id) const override final
435449
{
436450
return (edge_based_node_data.GetClassData(edge_based_node_id) & exclude_mask) > 0;

‎include/engine/datafacade/datafacade_base.hpp‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,12 @@ class BaseDataFacade
114114

115115
virtual extractor::ClassData GetClassData(const NodeID edge_based_node_id) const = 0;
116116

117+
virtual std::uint8_t GetNumberOfLanes(const NodeID /*edge_based_node_id*/) const { return 0; }
118+
119+
virtual double GetRoadWidth(const NodeID /*edge_based_node_id*/) const { return 0.; }
120+
121+
virtual double GetMaxRoadHalfWidth() const { return 0.; }
122+
117123
virtual bool ExcludeNode(const NodeID edge_based_node_id) const = 0;
118124

119125
virtual std::vector<std::string> GetClasses(const extractor::ClassData class_data) const = 0;

‎include/engine/geospatial_query.hpp‎

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef GEOSPATIAL_QUERY_HPP
22
#define GEOSPATIAL_QUERY_HPP
33

4+
#include "extractor/intersection/constants.hpp"
45
#include "engine/approach.hpp"
56
#include "engine/bearing.hpp"
67
#include "engine/phantom_node.hpp"
@@ -57,12 +58,12 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
5758
{
5859
auto results = rtree.SearchInRange(
5960
input_coordinate,
60-
max_distance,
61+
max_distance + datafacade.GetMaxRoadHalfWidth(),
6162
[this, approach, &input_coordinate, &bearing_with_range, &use_all_edges, max_distance](
6263
const CandidateSegment &segment)
6364
{
6465
auto invalidDistance =
65-
CheckSegmentDistance(input_coordinate, segment, max_distance);
66+
CheckSegmentDistance(input_coordinate, segment, max_distance, true);
6667
if (invalidDistance)
6768
{
6869
return std::make_pair(false, false);
@@ -106,7 +107,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
106107
{
107108
return (num_results >= max_results) ||
108109
(max_distance && max_distance != -1.0 &&
109-
CheckSegmentDistance(input_coordinate, segment, *max_distance));
110+
CheckSegmentDistance(input_coordinate, segment, *max_distance, false));
110111
});
111112

112113
return MakePhantomNodes(input_coordinate, results);
@@ -495,11 +496,44 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
495496
wsg84_coordinate);
496497
}
497498

499+
double GetRoadHalfWidth(const NodeID node_id) const
500+
{
501+
const double road_width = std::min(datafacade.GetRoadWidth(node_id),
502+
extractor::intersection::MAX_ROAD_SURFACE_WIDTH);
503+
if (road_width > 0.)
504+
{
505+
return 0.5 * road_width;
506+
}
507+
508+
return 0.5 * std::min<double>(datafacade.GetNumberOfLanes(node_id) *
509+
extractor::intersection::ASSUMED_LANE_WIDTH,
510+
extractor::intersection::MAX_ROAD_SURFACE_WIDTH);
511+
}
512+
513+
double GetRoadHalfWidth(const CandidateSegment &segment) const
514+
{
515+
if (segment.data.forward_segment_id.enabled &&
516+
segment.data.forward_segment_id.id != SPECIAL_SEGMENTID)
517+
{
518+
return GetRoadHalfWidth(segment.data.forward_segment_id.id);
519+
}
520+
521+
if (segment.data.reverse_segment_id.enabled &&
522+
segment.data.reverse_segment_id.id != SPECIAL_SEGMENTID)
523+
{
524+
return GetRoadHalfWidth(segment.data.reverse_segment_id.id);
525+
}
526+
527+
return 0.;
528+
}
529+
498530
bool CheckSegmentDistance(const Coordinate input_coordinate,
499531
const CandidateSegment &segment,
500-
const double max_distance) const
532+
const double max_distance,
533+
const bool account_road_surface) const
501534
{
502-
return GetSegmentDistance(input_coordinate, segment) > max_distance;
535+
return GetSegmentDistance(input_coordinate, segment) >
536+
max_distance + (account_road_surface ? GetRoadHalfWidth(segment) : 0.);
503537
}
504538

505539
std::pair<bool, bool> CheckSegmentExclude(const CandidateSegment &segment) const

‎include/extractor/extraction_way.hpp‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ struct ExtractionWay
5151
exits.clear();
5252
turn_lanes_forward.clear();
5353
turn_lanes_backward.clear();
54+
road_width = 0.;
5455
road_classification = RoadClassification();
5556
forward_travel_mode = TRAVEL_MODE_INACCESSIBLE;
5657
backward_travel_mode = TRAVEL_MODE_INACCESSIBLE;
@@ -110,6 +111,7 @@ struct ExtractionWay
110111
std::string exits;
111112
std::string turn_lanes_forward;
112113
std::string turn_lanes_backward;
114+
double road_width;
113115
RoadClassification road_classification;
114116
TravelMode forward_travel_mode : 4;
115117
TravelMode backward_travel_mode : 4;

‎include/extractor/graph_compressor.hpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class GraphCompressor
2424
std::vector<TurnRestriction> &turn_restrictions,
2525
std::vector<UnresolvedManeuverOverride> &maneuver_overrides,
2626
util::NodeBasedDynamicGraph &graph,
27-
const std::vector<NodeBasedEdgeAnnotation> &node_data_container,
27+
std::vector<NodeBasedEdgeAnnotation> &node_data_container,
2828
CompressedEdgeContainer &geometry_compressor);
2929

3030
private:

‎include/extractor/intersection/constants.hpp‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ const double constexpr PRIORITY_DISTINCTION_FACTOR = 1.75;
2222

2323
// the lane width we assume for a single lane
2424
const auto constexpr ASSUMED_LANE_WIDTH = 3.25;
25+
// cap pathological width tags and lane-derived widths when approximating a road surface
26+
const auto constexpr MAX_ROAD_SURFACE_WIDTH = 80.;
2527

2628
// how far apart can roads be at the most, when thinking about merging them?
2729
const auto constexpr MERGABLE_ANGLE_DIFFERENCE = 95.0;

‎include/extractor/node_based_edge.hpp‎

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ struct NodeBasedEdgeAnnotation
6868
StringViewID string_view_id; // 32 4
6969
LaneDescriptionID lane_description_id; // 16 2
7070
ClassData classes; // 8 1
71+
std::uint8_t number_of_lanes; // 8 1
72+
std::uint16_t road_width; // 16 2, centimeters
7173
TravelMode travel_mode : 4; // 4
7274
bool is_left_hand_driving : 1; // 1
7375

@@ -80,16 +82,26 @@ struct NodeBasedEdgeAnnotation
8082
other.is_left_hand_driving));
8183
}
8284

85+
bool CanCompressWith(const NodeBasedEdgeAnnotation &other) const
86+
{
87+
return CanCombineWith(other) && road_width == other.road_width;
88+
}
89+
8390
bool operator<(const NodeBasedEdgeAnnotation &other) const
8491
{
85-
return (
86-
std::tie(
87-
string_view_id, lane_description_id, classes, travel_mode, is_left_hand_driving) <
88-
std::tie(other.string_view_id,
89-
other.lane_description_id,
90-
other.classes,
91-
other.travel_mode,
92-
other.is_left_hand_driving));
92+
return (std::tie(string_view_id,
93+
lane_description_id,
94+
classes,
95+
number_of_lanes,
96+
road_width,
97+
travel_mode,
98+
is_left_hand_driving) < std::tie(other.string_view_id,
99+
other.lane_description_id,
100+
other.classes,
101+
other.number_of_lanes,
102+
other.road_width,
103+
other.travel_mode,
104+
other.is_left_hand_driving));
93105
}
94106
};
95107

‎include/extractor/node_data_container.hpp‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "extractor/class_data.hpp"
55
#include "extractor/edge_based_node.hpp"
6+
#include "extractor/intersection/constants.hpp"
67
#include "extractor/node_based_edge.hpp"
78
#include "extractor/travel_mode.hpp"
89

@@ -13,6 +14,8 @@
1314
#include "util/typedefs.hpp"
1415
#include "util/vector_view.hpp"
1516

17+
#include <algorithm>
18+
1619
namespace osrm::extractor
1720
{
1821

@@ -89,6 +92,32 @@ template <storage::Ownership Ownership> class EdgeBasedNodeDataContainerImpl
8992
return annotation_data[nodes[node_id].annotation_id].classes;
9093
}
9194

95+
std::uint8_t GetNumberOfLanes(const NodeID node_id) const
96+
{
97+
return annotation_data[nodes[node_id].annotation_id].number_of_lanes;
98+
}
99+
100+
double GetRoadWidth(const NodeID node_id) const
101+
{
102+
return annotation_data[nodes[node_id].annotation_id].road_width / 100.;
103+
}
104+
105+
double ComputeMaxRoadHalfWidth() const
106+
{
107+
double max_road_half_width = 0.;
108+
for (const auto &annotation : annotation_data)
109+
{
110+
const double road_width = std::min<double>(annotation.road_width / 100.,
111+
intersection::MAX_ROAD_SURFACE_WIDTH);
112+
const double fallback_width =
113+
std::min<double>(annotation.number_of_lanes * intersection::ASSUMED_LANE_WIDTH,
114+
intersection::MAX_ROAD_SURFACE_WIDTH);
115+
max_road_half_width =
116+
std::max(max_road_half_width, 0.5 * std::max(road_width, fallback_width));
117+
}
118+
return max_road_half_width;
119+
}
120+
92121
friend void serialization::read<Ownership>(storage::tar::FileReader &reader,
93122
const std::string &name,
94123
EdgeBasedNodeDataContainerImpl &ebn_data_container);

‎profiles/lib/guidance.lua‎

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
local Tags = require('lib/tags')
22
local Set = require('lib/set')
3+
local Measure = require('lib/measure')
34

45
local Guidance = {}
56

@@ -77,6 +78,70 @@ local function to_number_uint(s)
7778
return nil
7879
end
7980

81+
local function first_width(way, keys)
82+
for _, key in ipairs(keys) do
83+
local width = Measure.get_max_width(way:get_value_by_key(key))
84+
if width and width > 0 then
85+
return width
86+
end
87+
end
88+
end
89+
90+
local function sum_width_lanes(value)
91+
if not value then
92+
return nil
93+
end
94+
95+
local total = 0
96+
local found = false
97+
for lane_width in (value .. '|'):gmatch("([^|]*)|") do
98+
local width = Measure.get_max_width(lane_width)
99+
if width and width > 0 then
100+
total = total + width
101+
found = true
102+
end
103+
end
104+
105+
if found then
106+
return total
107+
end
108+
end
109+
110+
local function first_width_lanes(way, keys)
111+
for _, key in ipairs(keys) do
112+
local width = sum_width_lanes(way:get_value_by_key(key))
113+
if width and width > 0 then
114+
return width
115+
end
116+
end
117+
end
118+
119+
function Guidance.get_road_width(way)
120+
local width = first_width(way, { 'width:carriageway', 'width', 'est_width' })
121+
if width then
122+
return width
123+
end
124+
125+
local forward_width = first_width(way, { 'width:forward' })
126+
local backward_width = first_width(way, { 'width:backward' })
127+
128+
if forward_width or backward_width then
129+
return (forward_width or 0) + (backward_width or 0)
130+
end
131+
132+
forward_width = first_width_lanes(way, { 'width:lanes:forward' })
133+
backward_width = first_width_lanes(way, { 'width:lanes:backward' })
134+
135+
if forward_width or backward_width then
136+
return (forward_width or 0) + (backward_width or 0)
137+
end
138+
139+
width = first_width_lanes(way, { 'width:lanes' })
140+
if width then
141+
return width
142+
end
143+
end
144+
80145
function Guidance.set_classification (highway, result, input_way)
81146
if motorway_types[highway] then
82147
result.road_classification.motorway_class = true
@@ -140,6 +205,11 @@ function Guidance.set_classification (highway, result, input_way)
140205
result.road_classification.num_lanes = total_count
141206
end
142207
end
208+
209+
local road_width = Guidance.get_road_width(input_way)
210+
if road_width then
211+
result.road_width = road_width
212+
end
143213
end
144214

145215
-- returns forward,backward psv lane count

0 commit comments

Comments
 (0)