CARLA
Loading...
Searching...
No Matches
Profiles.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// This work is licensed under the terms of the MIT license.
5// For a copy, see <https://opensource.org/licenses/MIT>.
6
7#include "Profiles.h"
8
9#include <algorithm>
10#include <cctype>
11#include <cstdlib>
12#include <map>
13#include <set>
14
15namespace twinmodel {
16
17 namespace {
18
19 const std::map<std::string, ClassDefaults> &ClassTable() {
20 static const std::map<std::string, ClassDefaults> table = {
21 {"motorway", ClassDefaults{3.5, 2, std::nullopt, 2.5, 1.0}},
22 {"motorway_link", ClassDefaults{3.5, 1, std::nullopt, 1.5, 0.75}},
23 {"trunk", ClassDefaults{3.5, 2, std::nullopt, 1.5, std::nullopt}},
24 {"trunk_link", ClassDefaults{3.5, 1, std::nullopt, 1.0, std::nullopt}},
25 {"primary", ClassDefaults{3.6, 2, 2.0, std::nullopt, std::nullopt}},
26 {"primary_link", ClassDefaults{3.6, 1, 2.0, std::nullopt, std::nullopt}},
27 {"secondary", ClassDefaults{3.5, 2, 2.0, std::nullopt, std::nullopt}},
28 {"secondary_link", ClassDefaults{3.5, 1, 2.0, std::nullopt, std::nullopt}},
29 {"tertiary", ClassDefaults{3.5, 2, 2.0, std::nullopt, std::nullopt}},
30 {"tertiary_link", ClassDefaults{3.5, 1, 2.0, std::nullopt, std::nullopt}},
31 {"unclassified", ClassDefaults{3.5, 2, 2.0, std::nullopt, std::nullopt}},
32 {"residential", ClassDefaults{3.5, 2, 2.0, std::nullopt, std::nullopt}},
33 {"living_street", ClassDefaults{3.5, 2, 2.0, std::nullopt, std::nullopt}},
34 {"pedestrian", ClassDefaults{3.5, 1, 2.0, std::nullopt, std::nullopt}},
35 {"service", ClassDefaults{3.5, 2, std::nullopt, std::nullopt, std::nullopt}},
36 };
37 return table;
38 }
39
40 bool ParseInt(const std::string &text, int &out) {
41 try {
42 std::size_t used = 0u;
43 const int value = std::stoi(text, &used);
44 if (used == 0u) {
45 return false;
46 }
47 out = value;
48 return true;
49 } catch (...) {
50 return false;
51 }
52 }
53
54 bool ParseDouble(const std::string &text, double &out) {
55 try {
56 std::size_t used = 0u;
57 const double value = std::stod(text, &used);
58 if (used == 0u) {
59 return false;
60 }
61 out = value;
62 return true;
63 } catch (...) {
64 return false;
65 }
66 }
67
68 } // namespace
69
70 bool IsDrivable(const std::string &highway) {
71 static const std::set<std::string> drivable = {
72 "motorway", "motorway_link", "trunk", "trunk_link", "primary", "primary_link",
73 "secondary", "secondary_link", "tertiary", "tertiary_link", "unclassified",
74 "residential", "living_street", "service"};
75 return drivable.count(highway) != 0u;
76 }
77
78 const ClassDefaults *FindClassDefaults(const std::string &highway) {
79 const auto &table = ClassTable();
80 const auto it = table.find(highway);
81 return (it == table.end()) ? nullptr : &it->second;
82 }
83
84 std::optional<double> ParseMaxSpeed(const std::string &value) {
85 if (value.empty() || value == "none" || value == "signals") {
86 return std::nullopt;
87 }
88 double number = 0.0;
89 if (!ParseDouble(value, number) || number <= 0.0) {
90 return std::nullopt;
91 }
92 const bool mph = value.find("mph") != std::string::npos;
93 return mph ? number * 0.44704 : number / 3.6;
94 }
95
96 LaneProfile ResolveProfile(const std::string &highway, const Tags &tags) {
97 LaneProfile profile;
98 const ClassDefaults *defaults = FindClassDefaults(highway);
99 const ClassDefaults fallback;
100 const ClassDefaults &base = (defaults != nullptr) ? *defaults : fallback;
101
102 profile.lane_width = base.lane_width;
103 profile.sidewalk_width = base.sidewalk_width;
104 profile.shoulder = base.shoulder;
105 profile.shoulder_inner = base.shoulder_inner;
106
107 const std::string *oneway = FindTag(tags, "oneway");
108 profile.oneway = (oneway != nullptr) && (*oneway == "yes" || *oneway == "1" || *oneway == "-1");
109 if (highway == "motorway" || highway == "motorway_link") {
110 profile.oneway = true;
111 }
112
113 int total = base.lanes;
114 const std::string *lanes_tag = FindTag(tags, "lanes");
115 if (lanes_tag != nullptr) {
116 int parsed = 0;
117 if (ParseInt(*lanes_tag, parsed) && parsed > 0) {
118 total = parsed;
119 }
120 }
121
122 if (profile.oneway) {
123 profile.forward_lanes = std::max(1, total);
124 profile.backward_lanes = 0;
125 } else {
126 profile.forward_lanes = std::max(1, total / 2);
127 profile.backward_lanes = std::max(1, total - profile.forward_lanes);
128 }
129
130 const std::string *forward_tag = FindTag(tags, "lanes:forward");
131 const std::string *backward_tag = FindTag(tags, "lanes:backward");
132 int parsed = 0;
133 if (forward_tag != nullptr && ParseInt(*forward_tag, parsed) && parsed >= 0) {
134 profile.forward_lanes = parsed;
135 }
136 if (backward_tag != nullptr && ParseInt(*backward_tag, parsed) && parsed >= 0) {
137 profile.backward_lanes = parsed;
138 }
139
140 const std::string *width_tag = FindTag(tags, "width");
141 if (width_tag != nullptr) {
142 double width = 0.0;
143 const int count = std::max(1, profile.forward_lanes + profile.backward_lanes);
144 if (ParseDouble(*width_tag, width) && width > 0.0) {
145 profile.lane_width = std::max(2.5, width / count);
146 }
147 }
148
149 const std::string *maxspeed = FindTag(tags, "maxspeed");
150 if (maxspeed != nullptr) {
151 profile.speed_limit = ParseMaxSpeed(*maxspeed);
152 }
153
154 const std::string *sidewalk = FindTag(tags, "sidewalk");
155 if (sidewalk != nullptr && (*sidewalk == "no" || *sidewalk == "none")) {
156 profile.sidewalk_width = std::nullopt;
157 }
158 return profile;
159 }
160
161} // namespace twinmodel
std::string highway
Definition LaneGraph.cpp:24
Tags tags
Definition LaneGraph.cpp:25
std::optional< double > ParseMaxSpeed(const std::string &value)
Parse an OSM maxspeed value into m/s; "50", "50 km/h" and "30 mph".
Definition Profiles.cpp:84
LaneProfile ResolveProfile(const std::string &highway, const Tags &tags)
Resolve the lane layout from the way's tags, falling back to the class defaults.
Definition Profiles.cpp:96
const std::string * FindTag(const Tags &tags, const std::string &key)
Definition Model.h:20
bool IsDrivable(const std::string &highway)
True when the way carries motor traffic and should become a Road.
Definition Profiles.cpp:70
std::map< std::string, std::string > Tags
Definition Model.h:18
const ClassDefaults * FindClassDefaults(const std::string &highway)
Definition Profiles.cpp:78
Per highway class defaults, ported from profiles.py's _EU_CLASSES.
Definition Profiles.h:18
std::optional< double > shoulder
Definition Profiles.h:22
std::optional< double > sidewalk_width
Definition Profiles.h:21
std::optional< double > shoulder_inner
Definition Profiles.h:23
The lane layout an OSM way resolves to, before it is cut at junctions.
Definition Profiles.h:27
std::optional< double > speed_limit
Definition Profiles.h:34
std::optional< double > shoulder_inner
Definition Profiles.h:33
std::optional< double > sidewalk_width
Definition Profiles.h:31
std::optional< double > shoulder
Definition Profiles.h:32