CARLA
Loading...
Searching...
No Matches
road/Lane.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 "carla/road/Lane.h"
8
9#include <limits>
10
11#include "carla/Debug.h"
12#include "carla/geom/Math.h"
19#include "carla/road/MapData.h"
20#include "carla/road/Road.h"
21
22namespace carla {
23namespace road {
24
26 return _lane_section;
27 }
28
30 DEBUG_ASSERT(_lane_section != nullptr);
31 return _lane_section->GetRoad();
32 }
33
35 return _id;
36 }
37
39 return _type;
40 }
41
42 bool Lane::GetLevel() const {
43 return _level;
44 }
45
46 double Lane::GetDistance() const {
47 DEBUG_ASSERT(_lane_section != nullptr);
48 return _lane_section->GetDistance();
49 }
50
51 double Lane::GetLength() const {
52 const auto *road = GetRoad();
53 DEBUG_ASSERT(road != nullptr);
54 const auto s = GetDistance();
55 return road->UpperBound(s) - s;
56 }
57
58 double Lane::GetWidth(const double s) const {
60 const auto width_info = GetInfo<element::RoadInfoLaneWidth>(s);
61 if(width_info != nullptr){
62 return width_info->GetPolynomial().Evaluate(s);
63 }
64 return 0.0;
65 }
66
67 bool Lane::IsStraight() const {
68 Road *road = GetRoad();
69 RELEASE_ASSERT(road != nullptr);
70 auto *geometry = road->GetInfo<element::RoadInfoGeometry>(GetDistance());
71 DEBUG_ASSERT(geometry != nullptr);
72 auto geometry_type = geometry->GetGeometry().GetType();
73 if (geometry_type != element::GeometryType::LINE) {
74 return false;
75 }
76 if(GetDistance() < geometry->GetDistance() ||
78 geometry->GetDistance() + geometry->GetGeometry().GetLength()) {
79 return false;
80 }
81 auto lane_offsets = GetInfos<element::RoadInfoLaneOffset>();
82 for (auto *lane_offset : lane_offsets) {
83 if (std::abs(lane_offset->GetPolynomial().GetC()) > 0 ||
84 std::abs(lane_offset->GetPolynomial().GetD()) > 0) {
85 return false;
86 }
87 }
88 auto elevations = road->GetInfos<element::RoadInfoElevation>();
89 for (auto *elevation : elevations) {
90 if (std::abs(elevation->GetPolynomial().GetC()) > 0 ||
91 std::abs(elevation->GetPolynomial().GetD()) > 0) {
92 return false;
93 }
94 }
95 return true;
96 }
97
98 /// Returns a pair containing first = width, second = tangent,
99 /// for an specific Lane given an s and a iterator over lanes
100 template <typename T>
101 static std::pair<double, double> ComputeTotalLaneWidth(
102 const T container,
103 const double s,
104 const LaneId lane_id) {
105
106 // lane_id can't be 0
107 RELEASE_ASSERT(lane_id != 0);
108
109 const bool negative_lane_id = lane_id < 0;
110 double dist = 0.0;
111 double tangent = 0.0;
112 for (const auto &lane : container) {
113 auto info = lane.second.template GetInfo<element::RoadInfoLaneWidth>(s);
114 RELEASE_ASSERT(info != nullptr);
115 const auto current_polynomial = info->GetPolynomial();
116 auto current_dist = current_polynomial.Evaluate(s);
117 auto current_tang = current_polynomial.Tangent(s);
118 if (lane.first != lane_id) {
119 dist += negative_lane_id ? current_dist : -current_dist;
120 tangent += negative_lane_id ? current_tang : -current_tang;
121 } else {
122 current_dist *= 0.5;
123 dist += negative_lane_id ? current_dist : -current_dist;
124 tangent += (negative_lane_id ? current_tang : -current_tang) * 0.5;
125 break;
126 }
127 }
128 return std::make_pair(dist, tangent);
129 }
130
132 const Road *road = GetRoad();
133 DEBUG_ASSERT(road != nullptr);
134
135 // must s be smaller (or eq) than road length and bigger (or eq) than 0?
136 RELEASE_ASSERT(s <= road->GetLength());
137 RELEASE_ASSERT(s >= 0.0);
138
139 const auto *lane_section = GetLaneSection();
140 DEBUG_ASSERT(lane_section != nullptr);
141 const std::map<LaneId, Lane> &lanes = lane_section->GetLanes();
142
143 // check that lane_id exists on the current s
144 RELEASE_ASSERT(!lanes.empty());
145 RELEASE_ASSERT(GetId() >= lanes.begin()->first);
146 RELEASE_ASSERT(GetId() <= lanes.rbegin()->first);
147
148 // These will accumulate the lateral offset (t) and lane heading of all
149 // the lanes in between the current lane and lane 0, where the main road
150 // geometry is described
151 float lane_t_offset = 0.0f;
152 float lane_tangent = 0.0f;
153
154 if (GetId() < 0) {
155 // right lane
156 const auto side_lanes = MakeListView(
157 std::make_reverse_iterator(lanes.lower_bound(0)), lanes.rend());
158 const auto computed_width =
159 ComputeTotalLaneWidth(side_lanes, s, GetId());
160 lane_t_offset = static_cast<float>(computed_width.first);
161 lane_tangent = static_cast<float>(computed_width.second);
162 } else if (GetId() > 0) {
163 // left lane
164 const auto side_lanes = MakeListView(lanes.lower_bound(1), lanes.end());
165 const auto computed_width =
166 ComputeTotalLaneWidth(side_lanes, s, GetId());
167 lane_t_offset = static_cast<float>(computed_width.first);
168 lane_tangent = static_cast<float>(computed_width.second);
169 }
170
171 // Compute the tangent of the road's (lane 0) "laneOffset" on the current s
172 const auto lane_offset_info = road->GetInfo<element::RoadInfoLaneOffset>(s);
173 const auto lane_offset_tangent =
174 static_cast<float>(lane_offset_info->GetPolynomial().Tangent(s));
175
176 // Update the road tangent with the "laneOffset" information at current s
177 lane_tangent -= lane_offset_tangent;
178
179 // Get a directed point on the center of the current lane given an s
180 element::DirectedPoint dp = road->GetDirectedPointIn(s);
181
182 // Transform from the center of the road to the center of the lane
183 dp.ApplyLateralOffset(lane_t_offset);
184
185 // Apply the road's lateral surface profile (<lateralProfile><shape>).
186 // ApplyLateralOffset(k) places the point at OpenDRIVE t = -k; the lane-0
187 // laneOffset (already applied inside GetDirectedPointIn as -offset)
188 // contributes +offset to t.
189 const auto shape_lane_offset = road->GetInfo<element::RoadInfoLaneOffset>(s);
190 const double shape_t0 = (shape_lane_offset != nullptr)
191 ? shape_lane_offset->GetPolynomial().Evaluate(s) : 0.0;
192 dp.location.z += static_cast<float>(
193 road->GetLateralShapeZ(s, shape_t0 - lane_t_offset));
194
195 // Update the lane tangent with the road "laneOffset" at current s
196 dp.tangent -= lane_tangent;
197
198 // Unreal's Y axis hack
199 dp.location.y *= -1;
200 dp.tangent *= -1;
201
202 geom::Rotation rot(
203 geom::Math::ToDegrees(static_cast<float>(dp.pitch)),
204 geom::Math::ToDegrees(static_cast<float>(dp.tangent)),
205 0.0f);
206
207 // Fix the direction of the possitive lanes
208 if (!IsPositiveDirection()) {
209 rot.yaw += 180.0f;
210 rot.pitch = 360.0f - rot.pitch;
211 }
212
213 return geom::Transform(dp.location, rot);
214 }
215
216 std::pair<geom::Vector3D, geom::Vector3D> Lane::GetCornerPositions(
217 const double parameter_s, const float extra_width) const {
218 const Road *road = GetRoad();
219 DEBUG_ASSERT(road != nullptr);
220
221 const auto *lane_section = GetLaneSection();
222 DEBUG_ASSERT(lane_section != nullptr);
223 const std::map<LaneId, Lane> &lanes = lane_section->GetLanes();
224
225 // check that lane_id exists on the current s
226 RELEASE_ASSERT(!lanes.empty());
227 RELEASE_ASSERT(GetId() >= lanes.begin()->first);
228 RELEASE_ASSERT(GetId() <= lanes.rbegin()->first);
229
230 const double s = geom::Math::Clamp(parameter_s, 0.0, road->GetLength());
231 float lane_t_offset = 0.0f;
232
233 if (GetId() < 0) {
234 // right lane
235 const auto side_lanes = MakeListView(
236 std::make_reverse_iterator(lanes.lower_bound(0)), lanes.rend());
237 const auto computed_width =
238 ComputeTotalLaneWidth(side_lanes, s, GetId());
239 lane_t_offset = static_cast<float>(computed_width.first);
240 } else if (GetId() > 0) {
241 // left lane
242 const auto side_lanes = MakeListView(lanes.lower_bound(1), lanes.end());
243 const auto computed_width =
244 ComputeTotalLaneWidth(side_lanes, s, GetId());
245 lane_t_offset = static_cast<float>(computed_width.first);
246 }
247
248 float lane_width = static_cast<float>(GetWidth(s)) / 2.0f;
249 if (extra_width != 0.f && road->IsJunction() && GetType() == Lane::LaneType::Driving) {
250 lane_width += extra_width;
251 }
252
253 // Get two points on the center of the road on given s
254 element::DirectedPoint dp_r, dp_l;
255 dp_r = dp_l = road->GetDirectedPointIn(s);
256
257 // Transform from the center of the road to each of lane corners
258 dp_r.ApplyLateralOffset(lane_t_offset + lane_width);
259 dp_l.ApplyLateralOffset(lane_t_offset - lane_width);
260
261 // Apply the road's lateral surface profile (<lateralProfile><shape>) so
262 // the mesh follows the authored cross-section (crown/banking). Without
263 // this, crossing junction roads whose surfaces agree in the data end up
264 // as stacked flat meshes at their (differing) centerline elevations.
265 {
266 const auto corner_lane_offset = road->GetInfo<element::RoadInfoLaneOffset>(s);
267 const double corner_t0 = (corner_lane_offset != nullptr)
268 ? corner_lane_offset->GetPolynomial().Evaluate(s) : 0.0;
269 dp_r.location.z += static_cast<float>(
270 road->GetLateralShapeZ(s, corner_t0 - (lane_t_offset + lane_width)));
271 dp_l.location.z += static_cast<float>(
272 road->GetLateralShapeZ(s, corner_t0 - (lane_t_offset - lane_width)));
273 }
274
275 // Unreal's Y axis hack
276 dp_r.location.y *= -1;
277 dp_l.location.y *= -1;
278
279 // Apply an offset to the Sidewalks
280 if (GetType() == LaneType::Sidewalk) {
281 // RoadRunner doesn't export it right now and as a workarround where 15.24 cm
282 // is the exact height that match with most of the RoadRunner sidewalks
283 dp_r.location.z += 0.1524f;
284 dp_l.location.z += 0.1524f;
285 /// @TODO: use the OpenDRIVE 5.3.7.2.1.1.9 Lane Height Record
286 }
287
288 return std::make_pair(dp_r.location, dp_l.location);
289 }
290
292 const auto *road = GetRoad();
293 DEBUG_ASSERT(road != nullptr);
294 if (road->IsRHT() && GetId() <= 0){
295 return true;
296 } else if (!road->IsRHT() && GetId() >= 0){
297 return true;
298 }
299 return false;
300 }
301
302} // road
303} // carla
float elevation
#define DEBUG_ASSERT(predicate)
Definition Debug.h:66
#define RELEASE_ASSERT(pred)
Definition Debug.h:84
std::map< std::string, unsigned int > road
value_type Evaluate(const value_type &x) const
Evaluates f(x) = a + bx + cx^2 + dx^3.
static constexpr T ToDegrees(T rad)
Definition Math.h:37
static constexpr T Clamp(T a, T min=T(0), T max=T(1))
Definition Math.h:49
Euler angles of a rotation in CARLA's / Unreal's left-handed frame: x forward, y right,...
Definition Rotation.h:45
A rigid transform in CARLA's / Unreal's left-handed frame: x forward, y right, z up,...
bool IsStraight() const
Checks whether the geometry is straight or not.
Definition road/Lane.cpp:67
Road * GetRoad() const
Definition road/Lane.cpp:29
LaneType GetType() const
Definition road/Lane.cpp:38
double GetWidth(const double s) const
Returns the total lane width given a s.
Definition road/Lane.cpp:58
geom::Transform ComputeTransform(const double s) const
const LaneSection * GetLaneSection() const
Definition road/Lane.cpp:25
LaneType
Can be used as flags.
Definition road/Lane.h:29
bool GetLevel() const
Definition road/Lane.cpp:42
LaneSection * _lane_section
Definition road/Lane.h:128
double GetDistance() const
Definition road/Lane.cpp:46
std::pair< geom::Vector3D, geom::Vector3D > GetCornerPositions(const double s, const float extra_width=0.f) const
Computes the location of the edges given a s.
LaneId GetId() const
Definition road/Lane.cpp:34
bool IsPositiveDirection() const
double GetLength() const
Definition road/Lane.cpp:51
The lane offset record defines a lateral shift of the lane reference line(which is usually identical ...
const geom::CubicPolynomial & GetPolynomial() const
int32_t LaneId
Definition RoadTypes.h:19
static std::pair< double, double > ComputeTotalLaneWidth(const T container, const double s, const LaneId lane_id)
Returns a pair containing first = width, second = tangent, for an specific Lane given an s and a iter...
This file contains definitions of common data structures used in traffic manager.