CARLA
Loading...
Searching...
No Matches
road/Map.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/Map.h"
8#include "carla/Exception.h"
9#include "carla/geom/Math.h"
10#include "carla/geom/Vector3D.h"
23
25
26#include <algorithm>
27#include <limits>
28#include <vector>
29#include <unordered_map>
30#include <unordered_set>
31#include <stdexcept>
32#include <chrono>
33#include <thread>
34#include <iomanip>
35#include <cmath>
36
37namespace carla {
38namespace road {
39
40 using namespace carla::road::element;
41
42 /// We use this epsilon to shift the waypoints away from the edges of the lane
43 /// sections to avoid floating point precision errors.
44 static constexpr double EPSILON = 10.0 * std::numeric_limits<double>::epsilon();
45 static constexpr double TREE_PLACEMENT_EPSILON = 1.0e-4;
46
47 // ===========================================================================
48 // -- Static local methods ---------------------------------------------------
49 // ===========================================================================
50
51 template <typename T>
52 static std::vector<T> ConcatVectors(std::vector<T> dst, std::vector<T> src) {
53 if (src.size() > dst.size()) {
54 return ConcatVectors(src, dst);
55 }
56 dst.insert(
57 dst.end(),
58 std::make_move_iterator(src.begin()),
59 std::make_move_iterator(src.end()));
60 return dst;
61 }
62
63 static double GetDistanceAtStartOfLane(const Lane &lane) {
64 if (lane.IsPositiveDirection()) {
65 return lane.GetDistance() + 10.0 * EPSILON;
66 } else {
67 return lane.GetDistance() + lane.GetLength() - 10.0 * EPSILON;
68 }
69 }
70
71 static double GetDistanceAtEndOfLane(const Lane &lane) {
72 if (!lane.IsPositiveDirection()) {
73 return lane.GetDistance() + 10.0 * EPSILON;
74 } else {
75 return lane.GetDistance() + lane.GetLength() - 10.0 * EPSILON;
76 }
77 }
78
79 /// Return a waypoint for each drivable lane on @a lane_section.
80 template <typename FuncT>
83 const LaneSection &lane_section,
84 double distance,
85 FuncT &&func) {
86 for (const auto &pair : lane_section.GetLanes()) {
87 const auto &lane = pair.second;
88 if (lane.GetId() == 0) {
89 continue;
90 }
91 if ((static_cast<uint32_t>(lane.GetType()) & static_cast<uint32_t>(Lane::LaneType::Driving)) > 0) {
92 std::forward<FuncT>(func)(Waypoint{
93 road_id,
94 lane_section.GetId(),
95 lane.GetId(),
97 }
98 }
99 }
100
101 template <typename FuncT>
102 static void ForEachLaneImpl(
104 const LaneSection &lane_section,
105 double distance,
106 Lane::LaneType lane_type,
107 FuncT &&func) {
108 for (const auto &pair : lane_section.GetLanes()) {
109 const auto &lane = pair.second;
110 if (lane.GetId() == 0) {
111 continue;
112 }
113 if ((static_cast<int32_t>(lane.GetType()) & static_cast<int32_t>(lane_type)) > 0) {
114 std::forward<FuncT>(func)(Waypoint{
115 road_id,
116 lane_section.GetId(),
117 lane.GetId(),
119 }
120 }
121 }
122
123 /// Return a waypoint for each drivable lane on each lane section of @a road.
124 template <typename FuncT>
125 static void ForEachDrivableLane(const Road &road, FuncT &&func) {
126 for (const auto &lane_section : road.GetLaneSections()) {
128 road.GetId(),
129 lane_section,
130 -1.0, // At start of the lane
131 std::forward<FuncT>(func));
132 }
133 }
134
135 /// Return a waypoint for each lane of the specified type on each lane section of @a road.
136 template <typename FuncT>
137 static void ForEachLane(const Road &road, Lane::LaneType lane_type, FuncT &&func) {
138 for (const auto &lane_section : road.GetLaneSections()) {
140 road.GetId(),
141 lane_section,
142 -1.0, // At start of the lane
143 lane_type,
144 std::forward<FuncT>(func));
145 }
146 }
147
148 /// Return a waypoint for each drivable lane at @a distance on @a road.
149 template <typename FuncT>
150 static void ForEachDrivableLaneAt(const Road &road, double distance, FuncT &&func) {
151 for (const auto &lane_section : road.GetLaneSectionsAt(distance)) {
153 road.GetId(),
154 lane_section,
155 distance,
156 std::forward<FuncT>(func));
157 }
158 }
159
160 /// Assumes road_id and section_id are valid.
161 static bool IsLanePresent(const MapData &data, Waypoint waypoint) {
162 const auto &section = data.GetRoad(waypoint.road_id).GetLaneSectionById(waypoint.section_id);
163 return section.ContainsLane(waypoint.lane_id);
164 }
165
166 // ===========================================================================
167 // -- Map: Geometry ----------------------------------------------------------
168 // ===========================================================================
169
170 std::optional<Waypoint> Map::GetClosestWaypointOnRoad(
171 const geom::Location &pos,
172 int32_t lane_type) const {
173 std::vector<Rtree::TreeElement> query_result =
175 [&](Rtree::TreeElement const &element) {
176 const Lane &lane = GetLane(element.second.first);
177 return (lane_type & static_cast<int32_t>(lane.GetType())) > 0;
178 });
179
180 if (query_result.size() == 0) {
181 return std::optional<Waypoint>{};
182 }
183
184 Rtree::BSegment segment = query_result.front().first;
185 Rtree::BPoint s1 = segment.first;
186 Rtree::BPoint s2 = segment.second;
187 auto distance_to_segment = geom::Math::DistanceSegmentToPoint(pos,
188 geom::Vector3D(s1.get<0>(), s1.get<1>(), s1.get<2>()),
189 geom::Vector3D(s2.get<0>(), s2.get<1>(), s2.get<2>()));
190
191 Waypoint result_start = query_result.front().second.first;
192 Waypoint result_end = query_result.front().second.second;
193
194 if (GetLane(result_start).IsPositiveDirection()) {
195 double delta_s = distance_to_segment.first;
196 double final_s = result_start.s + delta_s;
197 if (final_s >= result_end.s) {
198 return result_end;
199 } else if (delta_s <= 0) {
200 return result_start;
201 } else {
202 return GetNext(result_start, delta_s).front();
203 }
204 } else {
205 double delta_s = distance_to_segment.first;
206 double final_s = result_start.s - delta_s;
207 if (final_s <= result_end.s) {
208 return result_end;
209 } else if (delta_s <= 0) {
210 return result_start;
211 } else {
212 return GetNext(result_start, delta_s).front();
213 }
214 }
215 }
216
217 std::optional<Waypoint> Map::GetWaypoint(
218 const geom::Location &pos,
219 int32_t lane_type) const {
220 std::optional<Waypoint> w = GetClosestWaypointOnRoad(pos, lane_type);
221
222 if (!w.has_value()) {
223 return w;
224 }
225
226 const auto dist = geom::Math::Distance2D(ComputeTransform(*w).location, pos);
227 const auto lane_width_info = GetLane(*w).GetInfo<RoadInfoLaneWidth>(w->s);
228 const auto half_lane_width =
229 lane_width_info->GetPolynomial().Evaluate(w->s) * 0.5;
230
231 if (dist < half_lane_width) {
232 return w;
233 }
234
235 return std::optional<Waypoint>{};
236 }
237
238 std::optional<Waypoint> Map::GetWaypoint(
240 LaneId lane_id,
241 float s) const {
242
243 // define the waypoint with the known parameters
244 Waypoint waypoint;
245 waypoint.road_id = road_id;
246 waypoint.lane_id = lane_id;
247 waypoint.s = s;
248
249 // check the road
250 if (!_data.ContainsRoad(waypoint.road_id)) {
251 return std::optional<Waypoint>{};
252 }
253 const Road &road = _data.GetRoad(waypoint.road_id);
254
255 // check the 's' distance
256 if (s < 0.0f || s >= road.GetLength()) {
257 return std::optional<Waypoint>{};
258 }
259
260 // check the section
261 bool lane_found = false;
262 for (auto &section : road.GetLaneSectionsAt(s)) {
263 if (section.ContainsLane(lane_id)) {
264 waypoint.section_id = section.GetId();
265 lane_found = true;
266 break;
267 }
268 }
269
270 // check the lane id
271 if (!lane_found) {
272 return std::optional<Waypoint>{};
273 }
274
275 return waypoint;
276 }
277
279 return GetLane(waypoint).ComputeTransform(waypoint.s);
280 }
281
282 // ===========================================================================
283 // -- Map: Road information --------------------------------------------------
284 // ===========================================================================
285
287 return GetLane(waypoint).GetType();
288 }
289
290 double Map::GetLaneWidth(const Waypoint waypoint) const {
291 const auto s = waypoint.s;
292
293 const auto &lane = GetLane(waypoint);
294 RELEASE_ASSERT(lane.GetRoad() != nullptr);
295 RELEASE_ASSERT(s <= lane.GetRoad()->GetLength());
296
297 const auto lane_width_info = lane.GetInfo<RoadInfoLaneWidth>(s);
298 RELEASE_ASSERT(lane_width_info != nullptr);
299
300 return lane_width_info->GetPolynomial().Evaluate(s);
301 }
302
306
309 }
310
311 std::pair<const RoadInfoMarkRecord *, const RoadInfoMarkRecord *>
312 Map::GetMarkRecord(const Waypoint waypoint) const {
313 // if lane Id is 0, just return a pair of nulls
314 if (waypoint.lane_id == 0)
315 return std::make_pair(nullptr, nullptr);
316
317 const auto s = waypoint.s;
318
319 const auto &current_lane = GetLane(waypoint);
320 RELEASE_ASSERT(current_lane.GetRoad() != nullptr);
321 RELEASE_ASSERT(s <= current_lane.GetRoad()->GetLength());
322
323 const auto inner_lane_id = waypoint.lane_id < 0 ?
324 waypoint.lane_id + 1 :
325 waypoint.lane_id - 1;
326
327 const auto &inner_lane = current_lane.GetRoad()->GetLaneById(waypoint.section_id, inner_lane_id);
328
329 auto current_lane_info = current_lane.GetInfo<RoadInfoMarkRecord>(s);
330 auto inner_lane_info = inner_lane.GetInfo<RoadInfoMarkRecord>(s);
331
332 return std::make_pair(current_lane_info, inner_lane_info);
333 }
334
335 std::vector<Map::SignalSearchData> Map::GetSignalsInDistance(
336 Waypoint waypoint, double distance, bool stop_at_junction) const {
337
338 const auto &lane = GetLane(waypoint);
339 const bool forward = lane.IsPositiveDirection();
340 const double signed_distance = forward ? distance : -distance;
341 const double relative_s = waypoint.s - lane.GetDistance();
342 const double remaining_lane_length = forward ? lane.GetLength() - relative_s : relative_s;
343 DEBUG_ASSERT(remaining_lane_length >= 0.0);
344
345 auto &road =_data.GetRoad(waypoint.road_id);
346 std::vector<SignalSearchData> result;
347
348 // If after subtracting the distance we are still in the same lane, return
349 // same waypoint with the extra distance.
350 if (distance <= remaining_lane_length) {
351 auto signals = road.GetInfosInRange<RoadInfoSignal>(
352 waypoint.s, waypoint.s + signed_distance);
353 for(auto* signal : signals){
354 double distance_to_signal = 0;
355 if (lane.IsPositiveDirection()){
356 distance_to_signal = signal->GetDistance() - waypoint.s;
357 } else {
358 distance_to_signal = waypoint.s - signal->GetDistance();
359 }
360 // check that the signal affects the waypoint
361 bool is_valid = false;
362 for (auto &validity : signal->GetValidities()) {
363 if (waypoint.lane_id >= validity._from_lane &&
364 waypoint.lane_id <= validity._to_lane) {
365 is_valid = true;
366 break;
367 }
368 }
369 if(!is_valid){
370 continue;
371 }
372 if (distance_to_signal == 0) {
373 result.emplace_back(SignalSearchData
374 {signal, waypoint,
375 distance_to_signal});
376 } else {
377 result.emplace_back(SignalSearchData
378 {signal, GetNext(waypoint, distance_to_signal).front(),
379 distance_to_signal});
380 }
381
382 }
383 return result;
384 }
385 const double signed_remaining_length = forward ? remaining_lane_length : -remaining_lane_length;
386
387 //result = road.GetInfosInRange<RoadInfoSignal>(waypoint.s, waypoint.s + signed_remaining_length);
388 auto signals = road.GetInfosInRange<RoadInfoSignal>(
389 waypoint.s, waypoint.s + signed_remaining_length);
390 for(auto* signal : signals){
391 double distance_to_signal = 0;
392 if (lane.IsPositiveDirection()){
393 distance_to_signal = signal->GetDistance() - waypoint.s;
394 } else {
395 distance_to_signal = waypoint.s - signal->GetDistance();
396 }
397 // check that the signal affects the waypoint
398 bool is_valid = false;
399 for (auto &validity : signal->GetValidities()) {
400 if (waypoint.lane_id >= validity._from_lane &&
401 waypoint.lane_id <= validity._to_lane) {
402 is_valid = true;
403 break;
404 }
405 }
406 if(!is_valid){
407 continue;
408 }
409 if (distance_to_signal == 0) {
410 result.emplace_back(SignalSearchData
411 {signal, waypoint,
412 distance_to_signal});
413 } else {
414 result.emplace_back(SignalSearchData
415 {signal, GetNext(waypoint, distance_to_signal).front(),
416 distance_to_signal});
417 }
418 }
419 // If we run out of remaining_lane_length we have to go to the successors.
420 for (auto &successor : GetSuccessors(waypoint)) {
421 if(_data.GetRoad(successor.road_id).IsJunction() && stop_at_junction){
422 continue;
423 }
424 auto& sucessor_lane = _data.GetRoad(successor.road_id).
425 GetLaneByDistance(successor.s, successor.lane_id);
426
427 if (GetLane(successor).IsPositiveDirection()) {
428 successor.s = sucessor_lane.GetDistance();
429 } else {
430 successor.s = sucessor_lane.GetDistance() + sucessor_lane.GetLength();
431 }
432 auto sucessor_signals = GetSignalsInDistance(
433 successor, distance - remaining_lane_length, stop_at_junction);
434 for(auto& signal : sucessor_signals){
435 signal.accumulated_s += remaining_lane_length;
436 }
437 result = ConcatVectors(result, sucessor_signals);
438 }
439 return result;
440 }
441
442 std::vector<const element::RoadInfoSignal*>
444 std::vector<const element::RoadInfoSignal*> result;
445 for (const auto& road_pair : _data.GetRoads()) {
446 const auto &road = road_pair.second;
447 auto road_infos = road.GetInfos<element::RoadInfoSignal>();
448 for(const auto* road_info : road_infos) {
449 result.push_back(road_info);
450 }
451 }
452 return result;
453 }
454
455 std::vector<const element::RoadInfoStencil*>
457 std::vector<const element::RoadInfoStencil*> result;
458 for (const auto& road_pair : _data.GetRoads()) {
459 const auto &road = road_pair.second;
460 auto road_infos = road.GetInfos<element::RoadInfoStencil>();
461 for(const auto* road_info : road_infos) {
462 result.push_back(road_info);
463 }
464 }
465 return result;
466 }
467
468 std::vector<Map::StencilSearchData> Map::GetStencilsInDistance(
469 Waypoint waypoint, double distance, bool stop_at_junction) const {
470
471 const auto &lane = GetLane(waypoint);
472 const bool forward = lane.IsPositiveDirection();
473 const double relative_s = waypoint.s - lane.GetDistance();
474 const double remaining_lane_length = forward ? lane.GetLength() - relative_s : relative_s;
475 DEBUG_ASSERT(remaining_lane_length >= 0.0);
476
477 auto &road = _data.GetRoad(waypoint.road_id);
478 std::vector<StencilSearchData> result;
479
480 // Collect the stencils on this lane, up to the lane end or the requested
481 // distance, whichever comes first.
482 const double in_lane_distance = std::min(distance, remaining_lane_length);
483 const double signed_distance = forward ? in_lane_distance : -in_lane_distance;
484 auto stencils = road.GetInfosInRange<element::RoadInfoStencil>(
485 waypoint.s, waypoint.s + signed_distance);
486 for(auto* stencil : stencils){
487 double distance_to_stencil = 0;
488 if (lane.IsPositiveDirection()){
489 distance_to_stencil = stencil->GetS() - waypoint.s;
490 } else {
491 distance_to_stencil = waypoint.s - stencil->GetS();
492 }
493
494 if (distance_to_stencil >= 0) {
496 data.stencil = stencil;
497 data.waypoint = waypoint;
498 data.waypoint.s = stencil->GetS();
499 data.accumulated_s = distance_to_stencil;
500 result.push_back(data);
501 }
502 }
503 if (distance <= remaining_lane_length) {
504 return result;
505 }
506
507 // If we run out of remaining_lane_length we have to go to successors.
508 for (auto &successor : GetSuccessors(waypoint)) {
509 if (stop_at_junction && IsJunction(successor.road_id)) {
510 continue;
511 }
512 auto successor_stencils = GetStencilsInDistance(
513 successor, distance - remaining_lane_length, stop_at_junction);
514 for(auto& stencil : successor_stencils){
515 stencil.accumulated_s += remaining_lane_length;
516 }
517 result = ConcatVectors(result, successor_stencils);
518 }
519 return result;
520 }
521
522 std::vector<LaneMarking> Map::CalculateCrossedLanes(
523 const geom::Location &origin,
524 const geom::Location &destination) const {
525 return LaneCrossingCalculator::Calculate(*this, origin, destination);
526 }
527
528 std::vector<geom::Location> Map::GetAllCrosswalkZones() const {
529 std::vector<geom::Location> result;
530
531 for (const auto &pair : _data.GetRoads()) {
532 const auto &road = pair.second;
533 std::vector<const RoadInfoCrosswalk *> crosswalks = road.GetInfos<RoadInfoCrosswalk>();
534 if (crosswalks.size() > 0) {
535 for (auto crosswalk : crosswalks) {
536 // waypoint only at start position
537 std::vector<geom::Location> points;
538 Waypoint waypoint;
539 geom::Transform base;
540 for (const auto &section : road.GetLaneSectionsAt(crosswalk->GetS())) {
541 // get the section with the center lane
542 for (const auto &lane : section.GetLanes()) {
543 // is the center line
544 if (lane.first == 0) {
545 // get the center point
546 waypoint.road_id = pair.first;
547 waypoint.section_id = section.GetId();
548 waypoint.lane_id = 0;
549 waypoint.s = crosswalk->GetS();
550 base = ComputeTransform(waypoint);
551 }
552 }
553 }
554
555 // The outline corners are given at height 0 above the road surface.
556 // Placing them with the full centre-line transform at the crosswalk's
557 // s applies that one point's pitch and roll to the whole outline, which
558 // on a graded road tilts the slab about the pivot: the corners ahead of
559 // it stand 10-60 cm proud of the asphalt and the ones behind it sink.
560 // Rotate the outline in the horizontal plane only and give every corner
561 // the road's own elevation at that corner's s.
562 geom::Transform pivot = base;
563 pivot.rotation.pitch = 0.0f;
564 pivot.rotation.roll = 0.0f;
565 const float heading_deg =
566 geom::Math::ToDegrees<float>(static_cast<float>(crosswalk->GetHeading()));
567
568 // move perpendicular ('t')
569 pivot.rotation.yaw -= heading_deg;
570 pivot.rotation.yaw -= 90; // move perpendicular to 's' for the lateral offset
571 geom::Vector3D v(static_cast<float>(crosswalk->GetT()), 0.0f, 0.0f);
572 pivot.TransformPoint(v);
573 // restore pivot position and orientation (yaw only)
574 pivot.location = v;
575 pivot.rotation.yaw += 90;
576
577 // s direction of the road at the crosswalk, in the horizontal plane
578 const geom::Vector3D forward =
579 geom::Rotation(0.0f, base.rotation.yaw, 0.0f).GetForwardVector();
580 const double s0 = crosswalk->GetS();
581 const double road_length = road.GetLength();
582 const double z0 = road.GetElevationOn(s0).Evaluate(s0);
583
584 // calculate all the corners
585 for (auto corner : crosswalk->GetPoints()) {
587 static_cast<float>(corner.u),
588 static_cast<float>(corner.v),
589 static_cast<float>(corner.z));
590 // set the width larger to contact with the sidewalk (in case they have gutter area)
591 if (corner.u < 0) {
592 v2.x -= 1.0f;
593 } else {
594 v2.x += 1.0f;
595 }
596 pivot.TransformPoint(v2);
597 // elevation of the road at this corner's own s (not the pivot's)
598 const double ds =
599 static_cast<double>((v2.x - base.location.x) * forward.x +
600 (v2.y - base.location.y) * forward.y);
601 const double s_corner = geom::Math::Clamp(s0 + ds, 0.0, road_length);
602 const double z_corner = road.GetElevationOn(s_corner).Evaluate(s_corner);
603 v2.z = base.location.z + static_cast<float>(z_corner - z0) +
604 static_cast<float>(corner.z);
605 result.push_back(v2);
606 }
607 }
608 }
609 }
610 return result;
611 }
612
613 // ===========================================================================
614 // -- Map: Waypoint generation -----------------------------------------------
615 // ===========================================================================
616
617 std::vector<Waypoint> Map::GetSuccessors(const Waypoint waypoint) const {
618 const auto &next_lanes = GetLane(waypoint).GetNextLanes();
619 std::vector<Waypoint> result;
620 result.reserve(next_lanes.size());
621 for (auto *next_lane : next_lanes) {
622 if (next_lane == nullptr) {
623 continue;
624 }
625 const auto lane_id = next_lane->GetId();
626 if (lane_id == 0) {
627 continue;
628 }
629 const auto *section = next_lane->GetLaneSection();
630 if (section == nullptr) {
631 continue;
632 }
633 const auto *road = next_lane->GetRoad();
634 if (road == nullptr) {
635 continue;
636 }
637 const auto distance = GetDistanceAtStartOfLane(*next_lane);
638 result.emplace_back(Waypoint{road->GetId(), section->GetId(), lane_id, distance});
639 }
640 return result;
641 }
642
643 std::vector<Waypoint> Map::GetPredecessors(const Waypoint waypoint) const {
644 const auto &prev_lanes = GetLane(waypoint).GetPreviousLanes();
645 std::vector<Waypoint> result;
646 result.reserve(prev_lanes.size());
647 for (auto *next_lane : prev_lanes) {
648 if (next_lane == nullptr) {
649 continue;
650 }
651 const auto lane_id = next_lane->GetId();
652 if (lane_id == 0) {
653 continue;
654 }
655 const auto *section = next_lane->GetLaneSection();
656 if (section == nullptr) {
657 continue;
658 }
659 const auto *road = next_lane->GetRoad();
660 if (road == nullptr) {
661 continue;
662 }
663 const auto distance = GetDistanceAtEndOfLane(*next_lane);
664 result.emplace_back(Waypoint{road->GetId(), section->GetId(), lane_id, distance});
665 }
666 return result;
667 }
668
669 std::vector<Waypoint> Map::GetNext(
670 const Waypoint waypoint,
671 const double distance) const {
673 if (distance <= EPSILON) {
674 return {waypoint};
675 }
676 const auto &lane = GetLane(waypoint);
677 const bool forward = lane.IsPositiveDirection();
678 const double signed_distance = forward ? distance : -distance;
679 const double relative_s = waypoint.s - lane.GetDistance();
680 const double remaining_lane_length = forward ? lane.GetLength() - relative_s : relative_s;
681 DEBUG_ASSERT(remaining_lane_length >= 0.0);
682
683 // If after subtracting the distance we are still in the same lane, return
684 // same waypoint with the extra distance.
685 if (distance <= remaining_lane_length) {
686 Waypoint result = waypoint;
687 result.s += signed_distance;
688 result.s += forward ? -EPSILON : EPSILON;
689 RELEASE_ASSERT(result.s > 0.0);
690 return { result };
691 }
692
693 // If we run out of remaining_lane_length we have to go to the successors.
694 std::vector<Waypoint> result;
695 for (const auto &successor : GetSuccessors(waypoint)) {
697 successor.road_id != waypoint.road_id ||
698 successor.section_id != waypoint.section_id ||
699 successor.lane_id != waypoint.lane_id);
700 // Fix situations where the next waypoint is in the opposite direction and
701 // this waypoint is its successor, so this function would end up in a loop
702 bool is_broken = false;
703 for (const auto &future_successor : GetSuccessors(successor)) {
704 if (future_successor.road_id == waypoint.road_id
705 && future_successor.lane_id == waypoint.lane_id
706 && future_successor.section_id == waypoint.section_id){
707 is_broken = true;
708 break;
709 }
710 }
711 if (!is_broken){
712 result = ConcatVectors(result, GetNext(successor, distance - remaining_lane_length));
713 }
714 }
715 return result;
716 }
717
718 std::vector<Waypoint> Map::GetPrevious(
719 const Waypoint waypoint,
720 const double distance) const {
722 if (distance <= EPSILON) {
723 return {waypoint};
724 }
725 const auto &lane = GetLane(waypoint);
726 const bool forward = !lane.IsPositiveDirection();
727 const double signed_distance = forward ? distance : -distance;
728 const double relative_s = waypoint.s - lane.GetDistance();
729 const double remaining_lane_length = forward ? lane.GetLength() - relative_s : relative_s;
730 DEBUG_ASSERT(remaining_lane_length >= 0.0);
731
732 // If after subtracting the distance we are still in the same lane, return
733 // same waypoint with the extra distance.
734 if (distance <= remaining_lane_length) {
735 Waypoint result = waypoint;
736 result.s += signed_distance;
737 result.s += forward ? -EPSILON : EPSILON;
738 RELEASE_ASSERT(result.s > 0.0);
739 return { result };
740 }
741
742 // If we run out of remaining_lane_length we have to go to the successors.
743 std::vector<Waypoint> result;
744 for (const auto &successor : GetPredecessors(waypoint)) {
746 successor.road_id != waypoint.road_id ||
747 successor.section_id != waypoint.section_id ||
748 successor.lane_id != waypoint.lane_id);
749 // Fix situations, when next waypoint is in the opposite direction and
750 // this waypoint is his predecessor, so this function would end up in a loop
751 bool is_broken = false;
752 for (const auto &future_predecessor : GetPredecessors(successor)) {
753 if (future_predecessor.road_id == waypoint.road_id
754 && future_predecessor.lane_id == waypoint.lane_id
755 && future_predecessor.section_id == waypoint.section_id){
756 is_broken = true;
757 break;
758 }
759 }
760 if (!is_broken){
761 result = ConcatVectors(result, GetPrevious(successor, distance - remaining_lane_length));
762 }
763 }
764 return result;
765 }
766
767 std::optional<Waypoint> Map::GetRight(Waypoint waypoint) const {
768 RELEASE_ASSERT(waypoint.lane_id != 0);
769 bool is_rht = GetLane(waypoint).GetRoad()->IsRHT();
770 if (is_rht){
771 if (waypoint.lane_id > 0) {
772 ++waypoint.lane_id;
773 } else {
774 --waypoint.lane_id;
775 }
776 return IsLanePresent(_data, waypoint) ? waypoint : std::optional<Waypoint>{};
777 } else {
778 if (std::abs(waypoint.lane_id) == 1) {
779 waypoint.lane_id *= -1;
780 } else if (waypoint.lane_id > 0) {
781 --waypoint.lane_id;
782 } else {
783 ++waypoint.lane_id;
784 }
785 return IsLanePresent(_data, waypoint) ? waypoint : std::optional<Waypoint>{};
786 }
787 }
788
789 std::optional<Waypoint> Map::GetLeft(Waypoint waypoint) const {
790 RELEASE_ASSERT(waypoint.lane_id != 0);
791 bool is_rht = GetLane(waypoint).GetRoad()->IsRHT();
792 if (is_rht){
793 if (std::abs(waypoint.lane_id) == 1) {
794 waypoint.lane_id *= -1;
795 } else if (waypoint.lane_id > 0) {
796 --waypoint.lane_id;
797 } else {
798 ++waypoint.lane_id;
799 }
800 return IsLanePresent(_data, waypoint) ? waypoint : std::optional<Waypoint>{};
801 } else {
802 if (waypoint.lane_id > 0) {
803 ++waypoint.lane_id;
804 } else {
805 --waypoint.lane_id;
806 }
807 return IsLanePresent(_data, waypoint) ? waypoint : std::optional<Waypoint>{};
808 }
809 }
810
811 std::vector<Waypoint> Map::GenerateWaypoints(const double distance) const {
813 std::vector<Waypoint> result;
814 for (const auto &pair : _data.GetRoads()) {
815 const auto &road = pair.second;
816 for (double s = EPSILON; s < (road.GetLength() - EPSILON); s += distance) {
817 ForEachDrivableLaneAt(road, s, [&](auto &&waypoint) {
818 result.emplace_back(waypoint);
819 });
820 }
821 }
822 return result;
823 }
824
825 std::vector<Waypoint> Map::GenerateWaypointsOnRoadEntries(Lane::LaneType lane_type) const {
826 std::vector<Waypoint> result;
827 for (const auto &pair : _data.GetRoads()) {
828 const auto &road = pair.second;
829 // right lanes start at s 0
830 for (const auto &lane_section : road.GetLaneSectionsAt(0.0)) {
831 for (const auto &lane : lane_section.GetLanes()) {
832 if (lane.second.IsPositiveDirection() &&
833 static_cast<int32_t>(lane.second.GetType()) & static_cast<int32_t>(lane_type)) {
834 result.emplace_back(Waypoint{ road.GetId(), lane_section.GetId(), lane.second.GetId(), 0.0 });
835 }
836 }
837 }
838 // left lanes start at s max
839 const auto road_len = road.GetLength();
840 for (const auto &lane_section : road.GetLaneSectionsAt(road_len)) {
841 for (const auto &lane : lane_section.GetLanes()) {
842 // LHT reversed. add the right (negative) lanes
843 if (!lane.second.IsPositiveDirection() &&
844 static_cast<int32_t>(lane.second.GetType()) & static_cast<int32_t>(lane_type)) {
845 result.emplace_back(
846 Waypoint{ road.GetId(), lane_section.GetId(), lane.second.GetId(), road_len });
847 }
848 }
849 }
850 }
851 return result;
852 }
853
854 std::vector<Waypoint> Map::GenerateWaypointsInRoad(
856 Lane::LaneType lane_type) const {
857 std::vector<Waypoint> result;
858 if(_data.GetRoads().count(road_id)) {
859 const auto &road = _data.GetRoads().at(road_id);
860 // right lanes start at s 0
861 for (const auto &lane_section : road.GetLaneSectionsAt(0.0)) {
862 for (const auto &lane : lane_section.GetLanes()) {
863 if (lane.second.IsPositiveDirection() &&
864 static_cast<int32_t>(lane.second.GetType()) & static_cast<int32_t>(lane_type)) {
865 result.emplace_back(Waypoint{ road.GetId(), lane_section.GetId(), lane.second.GetId(), 0.0 });
866 }
867 }
868 }
869 // left lanes start at s max
870 const auto road_len = road.GetLength();
871 for (const auto &lane_section : road.GetLaneSectionsAt(road_len)) {
872 for (const auto &lane : lane_section.GetLanes()) {
873 if (!lane.second.IsPositiveDirection() &&
874 static_cast<int32_t>(lane.second.GetType()) & static_cast<int32_t>(lane_type)) {
875 result.emplace_back(
876 Waypoint{ road.GetId(), lane_section.GetId(), lane.second.GetId(), road_len });
877 }
878 }
879 }
880 }
881 return result;
882 }
883
884 std::vector<std::pair<Waypoint, Waypoint>> Map::GenerateTopology() const {
885 std::vector<std::pair<Waypoint, Waypoint>> result;
886 for (const auto &pair : _data.GetRoads()) {
887 const auto &road = pair.second;
888 ForEachDrivableLane(road, [&](auto &&waypoint) {
889 auto successors = GetSuccessors(waypoint);
890 if (successors.size() == 0){
891 auto distance = static_cast<float>(GetDistanceAtEndOfLane(GetLane(waypoint)));
892 auto last_waypoint = GetWaypoint(waypoint.road_id, waypoint.lane_id, distance);
893 if (last_waypoint.has_value()){
894 result.push_back({waypoint, *last_waypoint});
895 }
896 }
897 else{
898 for (auto &&successor : GetSuccessors(waypoint)) {
899 result.push_back({waypoint, successor});
900 }
901 }
902 });
903 }
904 return result;
905 }
906
907 std::vector<std::pair<Waypoint, Waypoint>> Map::GetJunctionWaypoints(JuncId id, Lane::LaneType lane_type) const {
908 std::vector<std::pair<Waypoint, Waypoint>> result;
909 const Junction * junction = GetJunction(id);
910 for(auto &connections : junction->GetConnections()) {
911 const Road &road = _data.GetRoad(connections.second.connecting_road);
912 ForEachLane(road, lane_type, [&](auto &&waypoint) {
913 const auto& lane = GetLane(waypoint);
914 const double final_s = GetDistanceAtEndOfLane(lane);
915 Waypoint lane_end(waypoint);
916 lane_end.s = final_s;
917 result.push_back({waypoint, lane_end});
918 });
919 }
920 return result;
921 }
922
923 std::unordered_map<road::RoadId, std::unordered_set<road::RoadId>>
925
926 const float epsilon = 0.0001f; // small delta in the road (set to 0.1
927 // millimeters to prevent numeric errors)
928 const Junction *junction = GetJunction(id);
929 std::unordered_map<road::RoadId, std::unordered_set<road::RoadId>>
930 conflicts;
931
932 // 2d typedefs
933 typedef boost::geometry::model::point
934 <float, 2, boost::geometry::cs::cartesian> Point2d;
935 typedef boost::geometry::model::segment<Point2d> Segment2d;
936 typedef boost::geometry::model::box<Rtree::BPoint> Box;
937
938 // box range
939 auto bbox_pos = junction->GetBoundingBox().location;
940 auto bbox_ext = junction->GetBoundingBox().extent;
941 auto min_corner = geom::Vector3D(
942 bbox_pos.x - bbox_ext.x,
943 bbox_pos.y - bbox_ext.y,
944 bbox_pos.z - bbox_ext.z - epsilon);
945 auto max_corner = geom::Vector3D(
946 bbox_pos.x + bbox_ext.x,
947 bbox_pos.y + bbox_ext.y,
948 bbox_pos.z + bbox_ext.z + epsilon);
949 Box box({min_corner.x, min_corner.y, min_corner.z},
950 {max_corner.x, max_corner.y, max_corner.z});
951 auto segments = _rtree.GetIntersections(box);
952
953 for (size_t i = 0; i < segments.size(); ++i){
954 auto &segment1 = segments[i];
955 auto waypoint1 = segment1.second.first;
956 JuncId junc_id1 = _data.GetRoad(waypoint1.road_id).GetJunctionId();
957 // only segments in the junction
958 if(junc_id1 != id){
959 continue;
960 }
961 Segment2d seg1{{segment1.first.first.get<0>(), segment1.first.first.get<1>()},
962 {segment1.first.second.get<0>(), segment1.first.second.get<1>()}};
963 for (size_t j = i + 1; j < segments.size(); ++j){
964 auto &segment2 = segments[j];
965 auto waypoint2 = segment2.second.first;
966 JuncId junc_id2 = _data.GetRoad(waypoint2.road_id).GetJunctionId();
967 // only segments in the junction
968 if(junc_id2 != id){
969 continue;
970 }
971 // discard same road
972 if(waypoint1.road_id == waypoint2.road_id){
973 continue;
974 }
975 Segment2d seg2{{segment2.first.first.get<0>(), segment2.first.first.get<1>()},
976 {segment2.first.second.get<0>(), segment2.first.second.get<1>()}};
977
978 double distance = boost::geometry::distance(seg1, seg2);
979 // better to set distance to lanewidth
980 if(distance > 2.0){
981 continue;
982 }
983 if(conflicts[waypoint1.road_id].count(waypoint2.road_id) == 0){
984 conflicts[waypoint1.road_id].insert(waypoint2.road_id);
985 }
986 if(conflicts[waypoint2.road_id].count(waypoint1.road_id) == 0){
987 conflicts[waypoint2.road_id].insert(waypoint1.road_id);
988 }
989 }
990 }
991 return conflicts;
992 }
993
994 const Lane &Map::GetLane(Waypoint waypoint) const {
995 return _data.GetRoad(waypoint.road_id).GetLaneById(waypoint.section_id, waypoint.lane_id);
996 }
997
998 // ===========================================================================
999 // -- Map: Private functions -------------------------------------------------
1000 // ===========================================================================
1001
1002 // Adds a new element to the rtree element list using the position of the
1003 // waypoints both ends of the segment
1005 std::vector<Rtree::TreeElement> &rtree_elements,
1006 geom::Transform &current_transform,
1007 geom::Transform &next_transform,
1008 Waypoint &current_waypoint,
1009 Waypoint &next_waypoint) {
1010 Rtree::BPoint init =
1012 current_transform.location.x,
1013 current_transform.location.y,
1014 current_transform.location.z);
1015 Rtree::BPoint end =
1017 next_transform.location.x,
1018 next_transform.location.y,
1019 next_transform.location.z);
1020 rtree_elements.emplace_back(std::make_pair(Rtree::BSegment(init, end),
1021 std::make_pair(current_waypoint, next_waypoint)));
1022 }
1023 // Adds a new element to the rtree element list using the position of the
1024 // waypoints, both ends of the segment
1026 std::vector<Rtree::TreeElement> &rtree_elements,
1027 geom::Transform &current_transform,
1028 Waypoint &current_waypoint,
1029 Waypoint &next_waypoint) {
1030 geom::Transform next_transform = ComputeTransform(next_waypoint);
1031 AddElementToRtree(rtree_elements, current_transform, next_transform,
1032 current_waypoint, next_waypoint);
1033 current_waypoint = next_waypoint;
1034 current_transform = next_transform;
1035 }
1036
1037 // returns the remaining length of the geometry depending on the lane
1038 // direction
1039 double GetRemainingLength(const Lane &lane, double current_s) {
1040 if (lane.IsPositiveDirection()) {
1041 return (lane.GetDistance() + lane.GetLength() - current_s);
1042 } else {
1043 return (current_s - lane.GetDistance());
1044 }
1045 }
1046
1048 const double epsilon = 0.000001; // small delta in the road (set to 1
1049 // micrometer to prevent numeric errors)
1050 const double min_delta_s = 1; // segments of minimum 1m through the road
1051
1052 // 1.8 degrees, maximum angle in a curve to place a segment
1053 constexpr double angle_threshold = geom::Math::Pi<double>() / 100.0;
1054 // maximum distance of a segment
1055 constexpr double max_segment_length = 100.0;
1056
1057 // Generate waypoints at start of every lane
1058 std::vector<Waypoint> topology;
1059 for (const auto &pair : _data.GetRoads()) {
1060 const auto &road = pair.second;
1061 ForEachLane(road, Lane::LaneType::Any, [&](auto &&waypoint) {
1062 if(waypoint.lane_id != 0) {
1063 topology.push_back(waypoint);
1064 }
1065 });
1066 }
1067
1068 // Container of segments and waypoints
1069 std::vector<Rtree::TreeElement> rtree_elements;
1070 // Loop through all lanes
1071 for (auto &waypoint : topology) {
1072 auto &lane_start_waypoint = waypoint;
1073
1074 auto current_waypoint = lane_start_waypoint;
1075
1076 const Lane &lane = GetLane(current_waypoint);
1077
1078 geom::Transform current_transform = ComputeTransform(current_waypoint);
1079
1080 // Save computation time in straight lines
1081 if (lane.IsStraight()) {
1082 double delta_s = min_delta_s;
1083 double remaining_length =
1084 GetRemainingLength(lane, current_waypoint.s);
1085 remaining_length -= epsilon;
1086 delta_s = remaining_length;
1087 if (delta_s < epsilon) {
1088 continue;
1089 }
1090 auto next = GetNext(current_waypoint, delta_s);
1091
1092 RELEASE_ASSERT(next.size() == 1);
1093 RELEASE_ASSERT(next.front().road_id == current_waypoint.road_id);
1094 auto next_waypoint = next.front();
1095
1097 rtree_elements,
1098 current_transform,
1099 current_waypoint,
1100 next_waypoint);
1101 // end of lane
1102 } else {
1103 auto next_waypoint = current_waypoint;
1104
1105 // Loop until the end of the lane
1106 // Advance in small s-increments
1107 while (true) {
1108 double delta_s = min_delta_s;
1109 double remaining_length =
1110 GetRemainingLength(lane, next_waypoint.s);
1111 remaining_length -= epsilon;
1112 delta_s = std::min(delta_s, remaining_length);
1113
1114 if (delta_s < epsilon) {
1116 rtree_elements,
1117 current_transform,
1118 current_waypoint,
1119 next_waypoint);
1120 break;
1121 }
1122
1123 auto next = GetNext(next_waypoint, delta_s);
1124 if (next.size() != 1 ||
1125 current_waypoint.section_id != next.front().section_id) {
1127 rtree_elements,
1128 current_transform,
1129 current_waypoint,
1130 next_waypoint);
1131 break;
1132 }
1133
1134 next_waypoint = next.front();
1135 geom::Transform next_transform = ComputeTransform(next_waypoint);
1136 double angle = geom::Math::GetVectorAngle(
1137 current_transform.GetForwardVector(), next_transform.GetForwardVector());
1138
1139 if (std::abs(angle) > angle_threshold ||
1140 std::abs(current_waypoint.s - next_waypoint.s) > max_segment_length) {
1142 rtree_elements,
1143 current_transform,
1144 next_transform,
1145 current_waypoint,
1146 next_waypoint);
1147 current_waypoint = next_waypoint;
1148 current_transform = next_transform;
1149 }
1150 }
1151 }
1152 }
1153 // Add segments to Rtree
1154 _rtree.InsertElements(rtree_elements);
1155 }
1156
1158 return _data.GetJunction(id);
1159 }
1160
1162 return _data.GetJunction(id);
1163 }
1164
1166 const double distance,
1167 const float extra_width,
1168 const bool smooth_junctions) const {
1169 RELEASE_ASSERT(distance > 0.0);
1170 geom::MeshFactory mesh_factory;
1171 geom::Mesh out_mesh;
1172
1173 mesh_factory.road_param.resolution = static_cast<float>(distance);
1174 mesh_factory.road_param.extra_lane_width = extra_width;
1175
1176 // Generate roads outside junctions
1177 for (auto &&pair : _data.GetRoads()) {
1178 const auto &road = pair.second;
1179 if (road.IsJunction()) {
1180 continue;
1181 }
1182 out_mesh += *mesh_factory.Generate(road);
1183 }
1184
1185 // Generate roads within junctions and smooth them
1186 for (const auto &junc_pair : _data.GetJunctions()) {
1187 const auto &junction = junc_pair.second;
1188 std::vector<std::unique_ptr<geom::Mesh>> lane_meshes;
1189 for(const auto &connection_pair : junction.GetConnections()) {
1190 const auto &connection = connection_pair.second;
1191 const auto &road = _data.GetRoads().at(connection.connecting_road);
1192 for (auto &&lane_section : road.GetLaneSections()) {
1193 for (auto &&lane_pair : lane_section.GetLanes()) {
1194 lane_meshes.push_back(mesh_factory.Generate(lane_pair.second));
1195 }
1196 }
1197 }
1198 if(smooth_junctions) {
1199 out_mesh += *mesh_factory.MergeAndSmooth(lane_meshes);
1200 } else {
1201 geom::Mesh junction_mesh;
1202 for(auto& lane : lane_meshes) {
1203 junction_mesh += *lane;
1204 }
1205 out_mesh += junction_mesh;
1206 }
1207 }
1208
1209 return out_mesh;
1210 }
1211
1212
1213 // Some real-world OpenDRIVE exports (DeepMap/NuRec) contain sub-2m road
1214 // stubs with an empty <link> element: not connected to anything, not part
1215 // of any junction, and geometrically sitting ON TOP of the real road they
1216 // were traced from -- often centimetres above it. Rendering them produces
1217 // a slab of road floating over the actual carriageway, with their torn
1218 // marking fragments poking through the surface. They carry no routable
1219 // topology (nothing can drive onto a link-less road), so skipping their
1220 // meshes loses nothing. Longer link-less roads are genuine dead-end
1221 // streets and are kept.
1222 static bool IsOrphanSliverRoad(const road::Road &road) {
1223 constexpr double kMaxSliverLength = 2.0; // meters
1224 return !road.IsJunction() &&
1225 road.GetLength() < kMaxSliverLength &&
1226 road.GetNexts().empty() &&
1227 road.GetPrevs().empty();
1228 }
1229
1230 // The same exports also carry LONGER link-less roads (tens of meters).
1231 // Some are genuine dead-end streets; others are duplicate traces laid
1232 // over a road that already exists, floating centimetres above it (a
1233 // 30m slab of road on top of the real carriageway). Topology cannot
1234 // tell them apart -- both are unconnected -- so decide geometrically:
1235 // a link-less road most of whose driving-lane samples lie within a
1236 // lane-width of another (connected) road's lane centers at road level
1237 // is a duplicate trace. Support from fellow link-less candidates is
1238 // deliberately ignored so two orphans overlapping only each other are
1239 // both conservatively kept.
1240 static std::unordered_set<RoadId> ComputeDuplicateTraceRoads(
1241 const Map &map, const std::unordered_set<RoadId> &candidate_ids) {
1242 std::unordered_set<RoadId> duplicates;
1243 if (candidate_ids.empty()) {
1244 return duplicates;
1245 }
1246 constexpr double kSampleStep = 4.0; // m along lanes
1247 constexpr double kMaxXY = 2.0; // m to a supporting lane center
1248 constexpr double kMaxZ = 1.0; // m height difference
1249 constexpr double kMinOverlapFraction = 0.4;
1250 const auto waypoints = map.GenerateWaypoints(kSampleStep);
1251 const auto CellKey = [](double x, double y) {
1252 const auto ix = static_cast<int64_t>(std::floor(x / kMaxXY));
1253 const auto iy = static_cast<int64_t>(std::floor(y / kMaxXY));
1254 return (ix << 32) ^ (iy & 0xffffffff);
1255 };
1256 std::unordered_map<int64_t, std::vector<geom::Location>> support_grid;
1257 std::unordered_map<RoadId, std::vector<geom::Location>> candidate_points;
1258 for (const auto &wp : waypoints) {
1259 const geom::Location loc = map.ComputeTransform(wp).location;
1260 if (candidate_ids.count(wp.road_id) != 0u) {
1261 candidate_points[wp.road_id].push_back(loc);
1262 } else {
1263 support_grid[CellKey(loc.x, loc.y)].push_back(loc);
1264 }
1265 }
1266 for (const auto &entry : candidate_points) {
1267 size_t supported = 0u;
1268 for (const geom::Location &loc : entry.second) {
1269 bool found = false;
1270 for (int dx = -1; dx <= 1 && !found; ++dx) {
1271 for (int dy = -1; dy <= 1 && !found; ++dy) {
1272 const auto it = support_grid.find(
1273 CellKey(loc.x + dx * kMaxXY, loc.y + dy * kMaxXY));
1274 if (it == support_grid.end()) {
1275 continue;
1276 }
1277 for (const geom::Location &other : it->second) {
1278 const double dxy = std::hypot(loc.x - other.x, loc.y - other.y);
1279 if (dxy < kMaxXY && std::abs(loc.z - other.z) < kMaxZ) {
1280 found = true;
1281 break;
1282 }
1283 }
1284 }
1285 }
1286 if (found) {
1287 ++supported;
1288 }
1289 }
1290 if (!entry.second.empty() &&
1291 static_cast<double>(supported) / static_cast<double>(entry.second.size()) >=
1292 kMinOverlapFraction) {
1293 duplicates.insert(entry.first);
1294 }
1295 }
1296 return duplicates;
1297 }
1298
1299 std::unordered_set<RoadId> Map::ComputeSkippedGenerationRoads() const {
1300 std::unordered_set<RoadId> candidate_ids;
1301 std::unordered_set<RoadId> skipped;
1302 for (const auto &pair : _data.GetRoads()) {
1303 const auto &road = pair.second;
1304 if (road.IsJunction() || !road.GetNexts().empty() || !road.GetPrevs().empty()) {
1305 continue;
1306 }
1307 if (IsOrphanSliverRoad(road)) {
1308 skipped.insert(pair.first);
1309 } else {
1310 candidate_ids.insert(pair.first);
1311 }
1312 }
1313 for (RoadId id : ComputeDuplicateTraceRoads(*this, candidate_ids)) {
1314 skipped.insert(id);
1315 }
1316 return skipped;
1317 }
1318
1319 std::vector<std::unique_ptr<geom::Mesh>> Map::GenerateChunkedMesh(
1320 const rpc::OpendriveGenerationParameters& params) const {
1321 geom::MeshFactory mesh_factory(params);
1322 std::vector<std::unique_ptr<geom::Mesh>> out_mesh_list;
1323
1324 std::unordered_map<JuncId, geom::Mesh> junction_map;
1325 const auto skipped_roads = ComputeSkippedGenerationRoads();
1326 for (auto &&pair : _data.GetRoads()) {
1327 const auto &road = pair.second;
1328 if (!road.IsJunction() && skipped_roads.count(pair.first) == 0u) {
1329 std::vector<std::unique_ptr<geom::Mesh>> road_mesh_list =
1330 mesh_factory.GenerateAllWithMaxLen(road);
1331
1332 out_mesh_list.insert(
1333 out_mesh_list.end(),
1334 std::make_move_iterator(road_mesh_list.begin()),
1335 std::make_move_iterator(road_mesh_list.end()));
1336 }
1337 }
1338
1339 // Generate roads within junctions and smooth them
1340 for (const auto &junc_pair : _data.GetJunctions()) {
1341 const auto &junction = junc_pair.second;
1342 std::vector<std::unique_ptr<geom::Mesh>> lane_meshes;
1343 std::vector<std::unique_ptr<geom::Mesh>> sidewalk_lane_meshes;
1344 for(const auto &connection_pair : junction.GetConnections()) {
1345 const auto &connection = connection_pair.second;
1346 const auto &road = _data.GetRoads().at(connection.connecting_road);
1347 for (auto &&lane_section : road.GetLaneSections()) {
1348 for (auto &&lane_pair : lane_section.GetLanes()) {
1349 const auto &lane = lane_pair.second;
1350 if (lane.GetType() != road::Lane::LaneType::Sidewalk) {
1351 lane_meshes.push_back(mesh_factory.Generate(lane));
1352 } else {
1353 sidewalk_lane_meshes.push_back(mesh_factory.Generate(lane));
1354 }
1355 }
1356 }
1357 }
1358 if(params.smooth_junctions) {
1359 auto merged_mesh = mesh_factory.MergeAndSmooth(lane_meshes);
1360 // Pave the wedges left between the (smoothed) lane corridors before
1361 // the sidewalks are appended, so the fill follows the final z.
1362 if (auto fill = GenerateJunctionFill(junction, *merged_mesh)) {
1363 *merged_mesh += *fill;
1364 }
1365 for(auto& lane : sidewalk_lane_meshes) {
1366 *merged_mesh += *lane;
1367 }
1368 out_mesh_list.push_back(std::move(merged_mesh));
1369 } else {
1370 std::unique_ptr<geom::Mesh> junction_mesh = std::make_unique<geom::Mesh>();
1371 for(auto& lane : lane_meshes) {
1372 *junction_mesh += *lane;
1373 }
1374 if (auto fill = GenerateJunctionFill(junction, *junction_mesh)) {
1375 *junction_mesh += *fill;
1376 }
1377 for(auto& lane : sidewalk_lane_meshes) {
1378 *junction_mesh += *lane;
1379 }
1380 out_mesh_list.push_back(std::move(junction_mesh));
1381 }
1382 }
1383
1384 // Guard against a valid-but-geometry-free xodr (e.g. no roads/junctions
1385 // at all, or every generated mesh happening to have zero vertices):
1386 // out_mesh_list.front() and per-mesh GetVertices().front() below used to
1387 // be called unconditionally, which segfaulted the server instead of
1388 // returning an error. Bail out with an empty chunk list; the only
1389 // caller (AOpenDriveGenerator::GenerateRoadMesh) already skips
1390 // zero-vertex meshes and tolerates an empty result.
1391 if (out_mesh_list.empty()) {
1392 return {};
1393 }
1394
1395 bool found_seed = false;
1396 geom::Vector2D min_pos;
1397 for (auto & mesh : out_mesh_list) {
1398 if (!mesh->GetVertices().empty()) {
1399 const auto &seed_vertex = mesh->GetVertices().front();
1400 min_pos = geom::Vector2D(seed_vertex.x, seed_vertex.y);
1401 found_seed = true;
1402 break;
1403 }
1404 }
1405 if (!found_seed) {
1406 // Every generated mesh was empty; nothing to bin.
1407 return {};
1408 }
1409
1410 auto max_pos = min_pos;
1411 for (auto & mesh : out_mesh_list) {
1412 if (mesh->GetVertices().empty()) {
1413 continue;
1414 }
1415 auto vertex = mesh->GetVertices().front();
1416 min_pos.x = std::min(min_pos.x, vertex.x);
1417 min_pos.y = std::min(min_pos.y, vertex.y);
1418 max_pos.x = std::max(max_pos.x, vertex.x);
1419 max_pos.y = std::max(max_pos.y, vertex.y);
1420 }
1421 size_t mesh_amount_x = static_cast<size_t>((max_pos.x - min_pos.x)/params.max_road_length) + 1;
1422 size_t mesh_amount_y = static_cast<size_t>((max_pos.y - min_pos.y)/params.max_road_length) + 1;
1423 std::vector<std::unique_ptr<geom::Mesh>> result;
1424 result.reserve(mesh_amount_x*mesh_amount_y);
1425 for (size_t i = 0; i < mesh_amount_x*mesh_amount_y; ++i) {
1426 result.emplace_back(std::make_unique<geom::Mesh>());
1427 }
1428 for (auto & mesh : out_mesh_list) {
1429 if (mesh->GetVertices().empty()) {
1430 continue;
1431 }
1432 auto vertex = mesh->GetVertices().front();
1433 size_t x_pos = static_cast<size_t>((vertex.x - min_pos.x) / params.max_road_length);
1434 size_t y_pos = static_cast<size_t>((vertex.y - min_pos.y) / params.max_road_length);
1435 *(result[x_pos + mesh_amount_x*y_pos]) += *mesh;
1436 }
1437
1438 return result;
1439 }
1440
1441 std::map<road::Lane::LaneType , std::vector<std::unique_ptr<geom::Mesh>>>
1443 const geom::Vector3D& minpos,
1444 const geom::Vector3D& maxpos) const
1445 {
1446
1447 geom::MeshFactory mesh_factory(params);
1448 std::map<road::Lane::LaneType, std::vector<std::unique_ptr<geom::Mesh>>> road_out_mesh_list;
1449 std::map<road::Lane::LaneType, std::vector<std::unique_ptr<geom::Mesh>>> junction_out_mesh_list;
1450
1451 std::thread juntction_thread( &Map::GenerateJunctions, this, mesh_factory, params,
1452 minpos, maxpos, &junction_out_mesh_list);
1453
1454 std::vector<RoadId> RoadsIDToGenerate = FilterRoadsByPosition(minpos, maxpos);
1455 // Drop link-less sliver/duplicate-trace roads (floating slabs stacked
1456 // on the real carriageway in DeepMap/NuRec exports).
1457 const auto skipped_roads = ComputeSkippedGenerationRoads();
1458 RoadsIDToGenerate.erase(
1459 std::remove_if(RoadsIDToGenerate.begin(), RoadsIDToGenerate.end(),
1460 [&skipped_roads](RoadId id) { return skipped_roads.count(id) != 0u; }),
1461 RoadsIDToGenerate.end());
1462
1463 size_t num_roads = RoadsIDToGenerate.size();
1464 size_t num_roads_per_thread = 30;
1465 size_t num_threads = (num_roads / num_roads_per_thread) + 1;
1466 num_threads = num_threads > 1 ? num_threads : 1;
1467 std::vector<std::thread> workers;
1468 std::mutex write_mutex;
1469 std::cout << "Generating " << std::to_string(num_roads) << " roads" << std::endl;
1470
1471 for ( size_t i = 0; i < num_threads; ++i ) {
1472 std::thread neworker(
1473 [this, &write_mutex, &mesh_factory, &RoadsIDToGenerate, &road_out_mesh_list, i, num_roads_per_thread]() {
1474 std::map<road::Lane::LaneType, std::vector<std::unique_ptr<geom::Mesh>>> Current =
1475 GenerateRoadsMultithreaded(mesh_factory, RoadsIDToGenerate,i, num_roads_per_thread );
1476 std::scoped_lock<std::mutex> guard(write_mutex);
1477 for ( auto&& pair : Current ) {
1478 if (road_out_mesh_list.find(pair.first) != road_out_mesh_list.end()) {
1479 road_out_mesh_list[pair.first].insert(road_out_mesh_list[pair.first].end(),
1480 std::make_move_iterator(pair.second.begin()),
1481 std::make_move_iterator(pair.second.end()));
1482 } else {
1483 road_out_mesh_list[pair.first] = std::move(pair.second);
1484 }
1485 }
1486 });
1487 workers.push_back(std::move(neworker));
1488 }
1489
1490 for (size_t i = 0; i < workers.size(); ++i) {
1491 workers[i].join();
1492 }
1493 workers.clear();
1494 for (size_t i = 0; i < workers.size(); ++i) {
1495 if (workers[i].joinable()) {
1496 workers[i].join();
1497 }
1498 }
1499
1500 juntction_thread.join();
1501 for (auto&& pair : junction_out_mesh_list) {
1502 if (road_out_mesh_list.find(pair.first) != road_out_mesh_list.end())
1503 {
1504 road_out_mesh_list[pair.first].insert(road_out_mesh_list[pair.first].end(),
1505 std::make_move_iterator(pair.second.begin()),
1506 std::make_move_iterator(pair.second.end()));
1507 }
1508 else
1509 {
1510 road_out_mesh_list[pair.first] = std::move(pair.second);
1511 }
1512 }
1513 std::cout << "Generated " << std::to_string(num_roads) << " roads" << std::endl;
1514
1515 return road_out_mesh_list;
1516 }
1517
1518 std::vector<std::pair<geom::Transform, std::string>> Map::GetTreesTransform(
1519 const geom::Vector3D& minpos,
1520 const geom::Vector3D& maxpos,
1521 float distancebetweentrees,
1522 float distancefromdrivinglineborder,
1523 float s_offset,
1524 bool measure_from_curb,
1525 bool keep_on_sidewalk) const {
1526
1527 std::vector<std::pair<geom::Transform, std::string>> transforms;
1528
1529 // Walks outward from the outermost driving lane over every lane that is
1530 // still roadway (parking, biking, border, shoulder, ...) and returns the
1531 // last one -- the lane whose outer edge is the curb line -- plus the
1532 // sidewalk right past it, if any. Only the driving lane's own edge is
1533 // considered when measure_from_curb is off (legacy tree placement).
1534 auto FindCurbLane = [measure_from_curb](
1535 const road::LaneSection &section, const road::Lane *driving_lane,
1536 const road::Lane *&sidewalk_lane) -> const road::Lane * {
1537 sidewalk_lane = nullptr;
1538 const road::Lane *curb_lane = driving_lane;
1539 if (!measure_from_curb) {
1540 return curb_lane;
1541 }
1542 const auto &lanes = section.GetLanes();
1543 const LaneId step = driving_lane->GetId() < 0 ? -1 : 1;
1544 for (LaneId id = driving_lane->GetId() + step;; id += step) {
1545 const auto it = lanes.find(id);
1546 if (it == lanes.end()) {
1547 break;
1548 }
1549 const auto type = it->second.GetType();
1550 if (type == Lane::LaneType::Sidewalk) {
1551 sidewalk_lane = &it->second;
1552 break;
1553 }
1554 if (type == Lane::LaneType::None) {
1555 break;
1556 }
1557 curb_lane = &it->second;
1558 }
1559 return curb_lane;
1560 };
1561
1562 const std::vector<RoadId> RoadsIDToGenerate = FilterRoadsByPosition(minpos, maxpos);
1563 for ( RoadId id : RoadsIDToGenerate ) {
1564 const auto& road = _data.GetRoads().at(id);
1565 if (!road.IsJunction()) {
1566 for (auto &&lane_section : road.GetLaneSections()) {
1567 LaneId min_lane = 0; // most negative (outermost right-side) driving lane
1568 LaneId max_lane = 0; // most positive (outermost left-side) driving lane
1569 for (auto &pairlane : lane_section.GetLanes()) {
1570 if (pairlane.second.GetType() == Lane::LaneType::Driving) {
1571 if (pairlane.first < 0 && (min_lane == 0 || pairlane.first < min_lane)) {
1572 min_lane = pairlane.first;
1573 } else if (pairlane.first > 0 && pairlane.first > max_lane) {
1574 max_lane = pairlane.first;
1575 }
1576 }
1577 }
1578
1579 // Prefer the outermost right-side lane; fall back to the outermost
1580 // left-side lane for one-way roads that only have positive-ID lanes.
1581 // Skip if no driving lane is found on either side (avoids using the
1582 // reference lane whose near-zero width places trees at road centre).
1583 const LaneId outer_lane = (min_lane != 0) ? min_lane : max_lane;
1584 if (outer_lane == 0) continue;
1585
1586 const road::Lane* lane = lane_section.GetLane(outer_lane);
1587 if( lane ) {
1588 double s_current = lane_section.GetDistance() + s_offset;
1589 const double s_end = lane_section.GetDistance() + lane_section.GetLength();
1590 while(s_current < s_end){
1591 if(lane->GetWidth(s_current) != 0.0f){
1592 const auto edges = lane->GetCornerPositions(s_current, 0);
1593 geom::Vector3D director = edges.second - edges.first;
1594 // Use double precision for the length check to avoid false
1595 // negatives from float cancellation on near-equal corners.
1596 const double director_squared_length =
1597 static_cast<double>(director.x) * static_cast<double>(director.x) +
1598 static_cast<double>(director.y) * static_cast<double>(director.y) +
1599 static_cast<double>(director.z) * static_cast<double>(director.z);
1600 // Skip degenerate or near-degenerate lane widths; normalising a
1601 // near-zero vector produces unstable directions and can place
1602 // trees on or very close to the road surface.
1603 if (director_squared_length <= (TREE_PLACEMENT_EPSILON * TREE_PLACEMENT_EPSILON)) {
1604 s_current += distancebetweentrees;
1605 continue;
1606 }
1607 // GetCornerPositions returns the lane corners in (t_offset + width, t_offset - width) order.
1608 // The true outer edge therefore depends on the lane side: for positive lane IDs it is the second corner,
1609 // and for negative lane IDs it is the first. Offset farther outward so trees are always placed away from the driving surface.
1610 const bool is_positive_lane = (lane->GetId() > 0);
1611 const geom::Vector3D first_corner = edges.first;
1612 const geom::Vector3D second_corner = edges.second;
1613 geom::Vector3D outer_corner =
1614 is_positive_lane ? second_corner : first_corner;
1615 const geom::Vector3D inner_corner =
1616 is_positive_lane ? first_corner : second_corner;
1617 const geom::Vector3D outward_direction =
1618 (outer_corner - inner_corner).MakeUnitVector();
1619 // Street furniture is measured from the curb, not from the
1620 // driving lane: a parking or bike lane in between would
1621 // otherwise put a fixed 4 m offset in the middle of the
1622 // roadway, and where there is nothing in between the same
1623 // offset overshoots a 1.5 m sidewalk into the buildings.
1624 // With keep_on_sidewalk the anchor never goes past the
1625 // middle of the sidewalk lane that follows the curb.
1626 float offset = distancefromdrivinglineborder;
1627 const road::Lane *sidewalk_lane = nullptr;
1628 const road::Lane *curb_lane = FindCurbLane(lane_section, lane, sidewalk_lane);
1629 if (curb_lane != lane) {
1630 const auto curb_edges = curb_lane->GetCornerPositions(s_current, 0);
1631 // Pick the curb lane's corner farthest along the outward
1632 // direction: robust to the corner ordering convention.
1633 const float d_first = (curb_edges.first - inner_corner).x * outward_direction.x +
1634 (curb_edges.first - inner_corner).y * outward_direction.y;
1635 const float d_second = (curb_edges.second - inner_corner).x * outward_direction.x +
1636 (curb_edges.second - inner_corner).y * outward_direction.y;
1637 outer_corner = d_first > d_second ? curb_edges.first : curb_edges.second;
1638 }
1639 if (keep_on_sidewalk && sidewalk_lane != nullptr) {
1640 const float sidewalk_width = static_cast<float>(sidewalk_lane->GetWidth(s_current));
1641 if (sidewalk_width > 0.0f) {
1642 offset = std::min(offset, 0.5f * sidewalk_width);
1643 }
1644 }
1645 geom::Vector3D treeposition =
1646 outer_corner + outward_direction * offset;
1647 // Face the anchor toward the road instead of along it: the
1648 // street furniture spawned on these transforms (lamps, signage)
1649 // extends along its local +X, so the lane heading would leave a
1650 // street lamp's arm -- and its light cone -- running parallel
1651 // to the road over the shoulder. outward_direction is already
1652 // the road->anchor lateral direction in UE frame; its opposite
1653 // is the yaw the furniture should face.
1654 const geom::Vector3D inward_direction = outward_direction * (-1.0f);
1655 const geom::Rotation facing_rotation(
1656 0.0f,
1657 geom::Math::ToDegrees(std::atan2(inward_direction.y, inward_direction.x)),
1658 0.0f);
1659 geom::Transform treeTransform(treeposition, facing_rotation);
1661 // roadinfo is null for roads without an explicit maxspeed OSM tag
1662 // (common in urban areas that rely on default speed limits).
1663 if (roadinfo) {
1664 transforms.push_back(std::make_pair(treeTransform, roadinfo->GetType()));
1665 } else {
1666 transforms.push_back(std::make_pair(treeTransform, "Town"));
1667 }
1668 }
1669 s_current += distancebetweentrees;
1670 }
1671
1672 }
1673 }
1674 }
1675 }
1676 return transforms;
1677 }
1678
1680 geom::Mesh out_mesh;
1681
1682 // Get the crosswalk vertices for the current map
1683 const std::vector<geom::Location> crosswalk_vertex = GetAllCrosswalkZones();
1684 if (crosswalk_vertex.empty()) {
1685 return out_mesh;
1686 }
1687
1688 // One triangle fan per crosswalk polygon, with planar UVs laid out in
1689 // the polygon's own frame: U runs along its longest edge (across the
1690 // road, the direction the stripes repeat in), V spans the short side
1691 // (the crosswalk's depth along the road) exactly once, and U tiles
1692 // with the same metre-to-texel scale so the texture isn't stretched.
1693 // Without UVs every vertex sampled texel (0,0) and the slabs rendered
1694 // as one flat colour.
1695 auto AddCrosswalkPolygon = [&out_mesh](const std::vector<geom::Vector3D> &poly) {
1696 if (poly.size() < 3) {
1697 return;
1698 }
1699 size_t longest = 0;
1700 float longest_len2 = -1.0f;
1701 for (size_t k = 0; k < poly.size(); ++k) {
1702 const auto &p = poly[k];
1703 const auto &q = poly[(k + 1) % poly.size()];
1704 const float dx = q.x - p.x, dy = q.y - p.y;
1705 const float len2 = dx * dx + dy * dy;
1706 if (len2 > longest_len2) {
1707 longest_len2 = len2;
1708 longest = k;
1709 }
1710 }
1711 geom::Vector2D u_dir(1.0f, 0.0f);
1712 if (longest_len2 > 1e-6f) {
1713 const auto &p = poly[longest];
1714 const auto &q = poly[(longest + 1) % poly.size()];
1715 const float inv = 1.0f / std::sqrt(longest_len2);
1716 u_dir = geom::Vector2D((q.x - p.x) * inv, (q.y - p.y) * inv);
1717 }
1718 const geom::Vector2D v_dir(-u_dir.y, u_dir.x);
1719 float a_min = std::numeric_limits<float>::max(), b_min = a_min;
1720 float a_max = std::numeric_limits<float>::lowest(), b_max = a_max;
1721 std::vector<std::pair<float, float>> local;
1722 local.reserve(poly.size());
1723 for (const auto &p : poly) {
1724 const float a = p.x * u_dir.x + p.y * u_dir.y;
1725 const float b = p.x * v_dir.x + p.y * v_dir.y;
1726 local.emplace_back(a, b);
1727 a_min = std::min(a_min, a); a_max = std::max(a_max, a);
1728 b_min = std::min(b_min, b); b_max = std::max(b_max, b);
1729 }
1730 const float depth = std::max(b_max - b_min, 0.5f);
1731 const size_t first = out_mesh.GetVerticesNum() + 1; // 1-based
1732 for (size_t k = 0; k < poly.size(); ++k) {
1733 out_mesh.AddVertex(poly[k]);
1734 out_mesh.AddUV(geom::Vector2D(
1735 (local[k].first - a_min) / depth,
1736 (local[k].second - b_min) / depth));
1737 }
1738 for (size_t k = 1; k + 1 < poly.size(); ++k) {
1739 out_mesh.AddIndex(first);
1740 out_mesh.AddIndex(first + k);
1741 out_mesh.AddIndex(first + k + 1);
1742 }
1743 };
1744
1745 // Create a a list of triangle fans with material "crosswalk"
1746 out_mesh.AddMaterial("crosswalk");
1747 size_t start_vertex_index = 0;
1748 size_t i = 0;
1749 std::vector<geom::Vector3D> vertices;
1750 // Iterate the vertices until a repeated one is found, this indicates
1751 // the triangle fan is done and another one must start
1752 do {
1753 // Except for the first iteration && triangle fan done
1754 if (i != 0 && crosswalk_vertex[start_vertex_index] == crosswalk_vertex[i]) {
1755 // Create the actual fan
1756 AddCrosswalkPolygon(vertices);
1757 vertices.clear();
1758 // End the loop if i reached the end of the vertex list
1759 if (i >= crosswalk_vertex.size() - 1) {
1760 break;
1761 }
1762 start_vertex_index = ++i;
1763 }
1764 // Append a new Vector3D that will be added to the triangle fan
1765 vertices.push_back(crosswalk_vertex[i++]);
1766 } while (i < crosswalk_vertex.size());
1767
1768 out_mesh.EndMaterial();
1769 return out_mesh;
1770 }
1771
1772 /// Buids a list of meshes related with LineMarkings
1773 std::vector<std::unique_ptr<geom::Mesh>> Map::GenerateLineMarkings(
1775 const geom::Vector3D& minpos,
1776 const geom::Vector3D& maxpos,
1777 std::vector<std::string>& outinfo ) const
1778 {
1779 std::vector<std::unique_ptr<geom::Mesh>> LineMarks;
1780 geom::MeshFactory mesh_factory(params);
1781
1782 // A real junction fans one lane out into several connecting roads, so
1783 // sweeping markings per-lane the way ordinary roads do would be wrong
1784 // there. Some OpenDRIVE exports (e.g. DeepMap/NuRec real-world
1785 // reconstructions) instead wrap a junction record around what is really
1786 // just a single road-to-road splice at a curvature break -- one real
1787 // incoming->connecting link, occasionally padded with a placeholder
1788 // incomingRoad="-1" connection. Unconditionally skipping every junction
1789 // road left "clean seam" bald patches wherever a continuously-marked
1790 // street happened to run through one of these trivial junctions, even
1791 // though the connecting road carries its own real per-lane roadMark data.
1792 auto HasSingleConnectingRoad = [this](JuncId junction_id) {
1793 const Junction *junction = _data.GetJunction(junction_id);
1794 if (junction == nullptr) {
1795 return false;
1796 }
1797 std::unordered_set<RoadId> connecting_roads;
1798 for (const auto &connection_pair : junction->GetConnections()) {
1799 connecting_roads.insert(connection_pair.second.connecting_road);
1800 }
1801 return connecting_roads.size() == 1;
1802 };
1803
1804 const auto skipped_roads = ComputeSkippedGenerationRoads();
1805 const std::vector<RoadId> RoadsIDToGenerate = FilterRoadsByPosition(minpos, maxpos);
1806 for ( RoadId id : RoadsIDToGenerate ) {
1807 const auto& road = _data.GetRoads().at(id);
1808 if (skipped_roads.count(id) != 0u) {
1809 continue; // road mesh is skipped too -- see GenerateChunkedMesh
1810 }
1811 if (!road.IsJunction() || HasSingleConnectingRoad(road.GetJunctionId())) {
1812 mesh_factory.GenerateLaneMarkForRoad(road, LineMarks, outinfo);
1813 }
1814 }
1815
1816 return LineMarks;
1817 }
1818
1819 std::vector<carla::geom::BoundingBox> Map::GetJunctionsBoundingBoxes() const {
1820 std::vector<carla::geom::BoundingBox> returning;
1821 for ( const auto& junc_pair : _data.GetJunctions() ) {
1822 const auto& junction = junc_pair.second;
1823 float box_extraextension_factor = 1.5f;
1824 carla::geom::BoundingBox bb = junction.GetBoundingBox();
1825 bb.extent *= box_extraextension_factor;
1826 returning.push_back(bb);
1827 }
1828 return returning;
1829 }
1830
1831 inline float Map::GetZPosInDeformation(float posx, float posy) const {
1832 return geom::deformation::GetZPosInDeformation(posx, posy) +
1834 }
1835
1836 std::map<road::Lane::LaneType, std::vector<std::unique_ptr<geom::Mesh>>>
1838 const std::vector<RoadId>& RoadsId,
1839 const size_t index, const size_t number_of_roads_per_thread) const
1840 {
1841 std::map<road::Lane::LaneType, std::vector<std::unique_ptr<geom::Mesh>>> out;
1842
1843 size_t start = index * number_of_roads_per_thread;
1844 size_t endoffset = (index+1) * number_of_roads_per_thread;
1845 size_t end = RoadsId.size();
1846
1847 for (size_t i = start; i < endoffset && i < end; ++i) {
1848 const auto& road = _data.GetRoads().at(RoadsId[i]);
1849 if (!road.IsJunction()) {
1850 mesh_factory.GenerateAllOrderedWithMaxLen(road, out);
1851 }
1852 }
1853 std::cout << "Generated roads from " + std::to_string(index * number_of_roads_per_thread) + " to " + std::to_string((index+1) * number_of_roads_per_thread ) << std::endl;
1854 return out;
1855 }
1856
1858 const rpc::OpendriveGenerationParameters& /*params*/,
1859 const geom::Vector3D& minpos,
1860 const geom::Vector3D& maxpos,
1861 std::map<road::Lane::LaneType,
1862 std::vector<std::unique_ptr<geom::Mesh>>>* junction_out_mesh_list) const {
1863
1864 std::vector<JuncId> JunctionsToGenerate = FilterJunctionsByPosition(minpos, maxpos);
1865 size_t num_junctions = JunctionsToGenerate.size();
1866 std::cout << "Generating " << std::to_string(num_junctions) << " junctions" << std::endl;
1867 size_t num_junctions_per_thread = 5;
1868 size_t num_threads = (num_junctions / num_junctions_per_thread) + 1;
1869 num_threads = num_threads > 1 ? num_threads : 1;
1870 std::vector<std::thread> workers;
1871 std::mutex write_mutex;
1872
1873 for ( size_t i = 0; i < num_threads; ++i ) {
1874 std::thread neworker(
1875 [this, &write_mutex, &mesh_factory, &junction_out_mesh_list, JunctionsToGenerate, i, num_junctions_per_thread, num_junctions]() {
1876 std::map<road::Lane::LaneType,
1877 std::vector<std::unique_ptr<geom::Mesh>>> junctionsofthisthread;
1878
1879 size_t minimum = 0;
1880 if( (i + 1) * num_junctions_per_thread < num_junctions ){
1881 minimum = (i + 1) * num_junctions_per_thread;
1882 }else{
1883 minimum = num_junctions;
1884 }
1885 std::cout << "Generating Junctions between " << std::to_string(i * num_junctions_per_thread) << " and " << std::to_string(minimum) << std::endl;
1886
1887 for ( size_t junctionindex = i * num_junctions_per_thread;
1888 junctionindex < minimum;
1889 ++junctionindex )
1890 {
1891 GenerateSingleJunction(mesh_factory, JunctionsToGenerate[junctionindex], &junctionsofthisthread);
1892 }
1893 std::cout << "Generated Junctions between " << std::to_string(i * num_junctions_per_thread) << " and " << std::to_string(minimum) << std::endl;
1894 std::scoped_lock<std::mutex> guard(write_mutex);
1895 for ( auto&& pair : junctionsofthisthread ) {
1896 if ((*junction_out_mesh_list).find(pair.first) != (*junction_out_mesh_list).end()) {
1897 (*junction_out_mesh_list)[pair.first].insert((*junction_out_mesh_list)[pair.first].end(),
1898 std::make_move_iterator(pair.second.begin()),
1899 std::make_move_iterator(pair.second.end()));
1900 } else {
1901 (*junction_out_mesh_list)[pair.first] = std::move(pair.second);
1902 }
1903 }
1904 });
1905 workers.push_back(std::move(neworker));
1906 }
1907
1908 for (size_t i = 0; i < workers.size(); ++i) {
1909 workers[i].join();
1910 }
1911 workers.clear();
1912 for (size_t i = 0; i < workers.size(); ++i) {
1913 if (workers[i].joinable()) {
1914 workers[i].join();
1915 }
1916 }
1917 }
1918
1919 std::vector<JuncId> Map::FilterJunctionsByPosition( const geom::Vector3D& minpos,
1920 const geom::Vector3D& maxpos ) const {
1921
1922 std::cout << "Filtered from " + std::to_string(_data.GetJunctions().size() ) + " junctions " << std::endl;
1923 std::vector<JuncId> ToReturn;
1924 for( auto& junction : _data.GetJunctions() ){
1925 geom::Location junctionLocation = junction.second.GetBoundingBox().location;
1926 if( minpos.x < junctionLocation.x && junctionLocation.x < maxpos.x &&
1927 minpos.y > junctionLocation.y && junctionLocation.y > maxpos.y ) {
1928 ToReturn.push_back(junction.first);
1929 }
1930 }
1931 std::cout << "To " + std::to_string(ToReturn.size() ) + " junctions " << std::endl;
1932
1933 return ToReturn;
1934 }
1935
1936 std::vector<RoadId> Map::FilterRoadsByPosition( const geom::Vector3D& minpos,
1937 const geom::Vector3D& maxpos ) const {
1938
1939 std::vector<RoadId> ToReturn;
1940 std::cout << "Filtered from " + std::to_string(_data.GetRoads().size() ) + " roads " << std::endl;
1941 for( auto& road : _data.GetRoads() ){
1942 auto &&lane_section = (*road.second.GetLaneSections().begin());
1943 const road::Lane* lane = road.second.IsRHT() ? lane_section.GetLane(-1) : lane_section.GetLane(1);
1944 if (!lane) {
1945 // Fallback: the expected innermost lane (−1 for RHT, +1 for LHT) is
1946 // absent (common on one-way streets and complex urban junctions).
1947 // Pick the driving lane closest to the reference line (smallest abs id)
1948 // to minimise the position error used for bounding-box filtering.
1949 int best_abs_id = std::numeric_limits<int>::max();
1950 for (const auto& pairlane : lane_section.GetLanes()) {
1951 if (pairlane.first != 0 && pairlane.second.GetType() == Lane::LaneType::Driving) {
1952 const int abs_id = std::abs(pairlane.first);
1953 if (abs_id < best_abs_id) {
1954 best_abs_id = abs_id;
1955 lane = &pairlane.second;
1956 }
1957 }
1958 }
1959 }
1960 if( lane ) {
1961 const double s_check = lane_section.GetDistance() + lane_section.GetLength() * 0.5;
1962 geom::Location roadLocation = lane->ComputeTransform(s_check).location;
1963 if( minpos.x < roadLocation.x && roadLocation.x < maxpos.x &&
1964 minpos.y > roadLocation.y && roadLocation.y > maxpos.y ) {
1965 ToReturn.push_back(road.first);
1966 }
1967 }
1968 }
1969 std::cout << "To " + std::to_string(ToReturn.size() ) + " roads " << std::endl;
1970 return ToReturn;
1971 }
1972
1973 std::unique_ptr<geom::Mesh> Map::GenerateJunctionFill(
1974 const road::Junction &junction,
1975 const geom::Mesh &corridor_mesh) const {
1976 // The runtime junction mesh is the union of the connecting roads' lane
1977 // corridors. On anything bigger than a simple crossing that leaves the
1978 // ground showing through every region the corridors enclose but don't
1979 // cover (the wedges between diverging turns, the middle of wide
1980 // 4-arm/dual-carriageway crossings). This rasterizes the corridors plus
1981 // a short "cap" of every arm road at its contact end, floods the grid
1982 // from the outside, and paves every uncovered cell the flood can't
1983 // reach -- i.e. exactly the holes enclosed by junction roadway, and
1984 // nothing outside it (curb returns between adjacent arms, or the open
1985 // bays of an irregular junction, stay ground: they are not enclosed).
1986 // The fill follows the corridor surface (barycentric z where it
1987 // overlaps a corridor, inverse-distance blend of the corridor vertices
1988 // in the holes) and sits a hair below it so the corridor always wins
1989 // the depth test where the two coincide.
1990 using Vec3 = geom::Vector3D;
1991 struct Tri { Vec3 a, b, c; };
1992
1993 constexpr float kCell = 0.5f; // fill grid resolution (m)
1994 constexpr float kBin = 4.0f; // triangle/vertex spatial hash (m)
1995 constexpr float kCapLength = 5.0f; // arm cap extruded past the contact line (m)
1996 constexpr float kArmSnap = 3.0f; // arm end must lie this close to a corridor end (m)
1997 constexpr float kZBelow = 0.015f; // fill sits this far below the corridors (m)
1998 constexpr int kMargin = 2; // grid cells of padding around the footprint
1999 constexpr size_t kMaxVertices = 4000000; // sanity cap on the grid size
2000
2001 const auto &verts = corridor_mesh.GetVertices();
2002 const auto &idx = corridor_mesh.GetIndexes();
2003 if (verts.size() < 3 || idx.size() < 3) {
2004 return nullptr;
2005 }
2006
2007 std::vector<Tri> corridor_tris;
2008 corridor_tris.reserve(idx.size() / 3);
2009 for (size_t i = 0; i + 2 < idx.size(); i += 3) {
2010 // geom::Mesh indices are 1-based
2011 corridor_tris.push_back({verts[idx[i] - 1], verts[idx[i + 1] - 1], verts[idx[i + 2] - 1]});
2012 }
2013
2014 // -- arm caps -----------------------------------------------------------
2015 // Every road that touches the junction (incoming roads and the
2016 // predecessor/successor of each connecting road) contributes a quad
2017 // spanning its full roadway width at the contact end, extruded a few
2018 // metres away from the junction. They close the wedges between
2019 // corridors that diverge from / converge on the same arm, which are
2020 // otherwise open toward the arm and would read as "outside".
2021 std::vector<Vec3> corridor_ends;
2022 std::unordered_set<RoadId> arm_candidates;
2023 const auto &roads = _data.GetRoads();
2024 for (const auto &connection_pair : junction.GetConnections()) {
2025 const auto &connection = connection_pair.second;
2026 const auto it = roads.find(connection.connecting_road);
2027 if (it == roads.end()) {
2028 continue;
2029 }
2030 const auto &cr = it->second;
2031 corridor_ends.push_back(cr.GetDirectedPointIn(0.0).location);
2032 corridor_ends.push_back(cr.GetDirectedPointIn(cr.GetLength()).location);
2033 arm_candidates.insert(connection.incoming_road);
2034 arm_candidates.insert(cr.GetPredecessor());
2035 arm_candidates.insert(cr.GetSuccessor());
2036 }
2037
2038 auto NearestCorridorEnd = [&corridor_ends](const Vec3 &p) {
2039 float best = std::numeric_limits<float>::max();
2040 for (const auto &e : corridor_ends) {
2041 best = std::min(best, (e - p).Length2D());
2042 }
2043 return best;
2044 };
2045
2046 std::vector<Tri> cap_tris;
2047 for (RoadId arm_id : arm_candidates) {
2048 const auto it = roads.find(arm_id);
2049 if (it == roads.end() || it->second.IsJunction()) {
2050 continue;
2051 }
2052 const auto &arm = it->second;
2053 const double length = arm.GetLength();
2054 if (length < 1e-3) {
2055 continue;
2056 }
2057 const Vec3 p_start = arm.GetDirectedPointIn(0.0).location;
2058 const Vec3 p_end = arm.GetDirectedPointIn(length).location;
2059 const float d_start = NearestCorridorEnd(p_start);
2060 const float d_end = NearestCorridorEnd(p_end);
2061 if (std::min(d_start, d_end) > kArmSnap) {
2062 continue; // link id that isn't actually this junction's arm
2063 }
2064 const bool at_end = d_end < d_start;
2065 const double s_c = at_end ? length : 0.0;
2066 const double s_in = at_end ? std::max(0.0, length - 1.0) : std::min(length, 1.0);
2067 const Vec3 p_in = arm.GetDirectedPointIn(s_in).location;
2068 Vec3 outward = at_end ? Vec3(p_end.x - p_in.x, p_end.y - p_in.y, 0.0f)
2069 : Vec3(p_start.x - p_in.x, p_start.y - p_in.y, 0.0f);
2070 outward.z = 0.0f;
2071 if (outward.Length2D() < 1e-4f) {
2072 continue;
2073 }
2074 outward = outward.MakeUnitVector();
2075 const Vec3 normal(-outward.y, outward.x, 0.0f);
2076
2077 bool have_corner = false;
2078 float t_min = 0.0f, t_max = 0.0f;
2079 Vec3 c_min, c_max;
2080 for (const road::Lane *lane : arm.GetLanesByDistance(s_c)) {
2081 if (lane == nullptr || lane->GetId() == 0 ||
2082 lane->GetType() == road::Lane::LaneType::Sidewalk ||
2083 lane->GetType() == road::Lane::LaneType::None) {
2084 continue;
2085 }
2086 const auto corners = lane->GetCornerPositions(s_c, 0.0f);
2087 for (const Vec3 &c : {corners.first, corners.second}) {
2088 const float t = (c.x - p_start.x) * normal.x + (c.y - p_start.y) * normal.y;
2089 if (!have_corner || t < t_min) { t_min = t; c_min = c; }
2090 if (!have_corner || t > t_max) { t_max = t; c_max = c; }
2091 have_corner = true;
2092 }
2093 }
2094 if (!have_corner || (t_max - t_min) < 0.5f) {
2095 continue;
2096 }
2097 const Vec3 o = outward * kCapLength;
2098 cap_tris.push_back({c_min, c_max, c_max + o});
2099 cap_tris.push_back({c_min, c_max + o, c_min + o});
2100 }
2101
2102 // -- grid ---------------------------------------------------------------
2103 float min_x = std::numeric_limits<float>::max(), min_y = min_x;
2104 float max_x = std::numeric_limits<float>::lowest(), max_y = max_x;
2105 auto Extend = [&](const Vec3 &v) {
2106 min_x = std::min(min_x, v.x); max_x = std::max(max_x, v.x);
2107 min_y = std::min(min_y, v.y); max_y = std::max(max_y, v.y);
2108 };
2109 for (const auto &v : verts) { Extend(v); }
2110 for (const auto &t : cap_tris) { Extend(t.a); Extend(t.b); Extend(t.c); }
2111
2112 const float origin_x = min_x - kMargin * kCell;
2113 const float origin_y = min_y - kMargin * kCell;
2114 const int nvx = static_cast<int>(std::ceil((max_x - min_x) / kCell)) + 2 * kMargin + 1;
2115 const int nvy = static_cast<int>(std::ceil((max_y - min_y) / kCell)) + 2 * kMargin + 1;
2116 if (nvx < 3 || nvy < 3 || static_cast<size_t>(nvx) * static_cast<size_t>(nvy) > kMaxVertices) {
2117#ifdef LIBCARLA_JFILL_DEBUG
2118 std::cout << "[jfill] junction " << junction.GetId() << " BAIL grid " << nvx << "x" << nvy << std::endl;
2119#endif
2120 return nullptr;
2121 }
2122 const int ncx = nvx - 1, ncy = nvy - 1;
2123
2124 // spatial hash of triangles and corridor vertices
2125 const int nbx = static_cast<int>((nvx * kCell) / kBin) + 1;
2126 const int nby = static_cast<int>((nvy * kCell) / kBin) + 1;
2127 auto BinX = [&](float x) { return std::clamp(static_cast<int>((x - origin_x) / kBin), 0, nbx - 1); };
2128 auto BinY = [&](float y) { return std::clamp(static_cast<int>((y - origin_y) / kBin), 0, nby - 1); };
2129 std::vector<std::vector<int>> corridor_bins(static_cast<size_t>(nbx) * nby);
2130 std::vector<std::vector<int>> cap_bins(static_cast<size_t>(nbx) * nby);
2131 std::vector<std::vector<int>> vertex_bins(static_cast<size_t>(nbx) * nby);
2132 auto BinTris = [&](const std::vector<Tri> &tris, std::vector<std::vector<int>> &bins) {
2133 for (size_t i = 0; i < tris.size(); ++i) {
2134 const Tri &t = tris[i];
2135 const int bx0 = BinX(std::min({t.a.x, t.b.x, t.c.x}));
2136 const int bx1 = BinX(std::max({t.a.x, t.b.x, t.c.x}));
2137 const int by0 = BinY(std::min({t.a.y, t.b.y, t.c.y}));
2138 const int by1 = BinY(std::max({t.a.y, t.b.y, t.c.y}));
2139 for (int by = by0; by <= by1; ++by) {
2140 for (int bx = bx0; bx <= bx1; ++bx) {
2141 bins[static_cast<size_t>(by) * nbx + bx].push_back(static_cast<int>(i));
2142 }
2143 }
2144 }
2145 };
2146 BinTris(corridor_tris, corridor_bins);
2147 BinTris(cap_tris, cap_bins);
2148 for (size_t i = 0; i < verts.size(); ++i) {
2149 vertex_bins[static_cast<size_t>(BinY(verts[i].y)) * nbx + BinX(verts[i].x)].push_back(static_cast<int>(i));
2150 }
2151
2152 // Barycentric point-in-triangle (2D); returns the surface z on a hit.
2153 auto HitTriangle = [](const Tri &t, float px, float py, float &z_out) {
2154 const float denom = (t.b.y - t.c.y) * (t.a.x - t.c.x) + (t.c.x - t.b.x) * (t.a.y - t.c.y);
2155 if (std::abs(denom) < 1e-9f) {
2156 return false;
2157 }
2158 const float w0 = ((t.b.y - t.c.y) * (px - t.c.x) + (t.c.x - t.b.x) * (py - t.c.y)) / denom;
2159 const float w1 = ((t.c.y - t.a.y) * (px - t.c.x) + (t.a.x - t.c.x) * (py - t.c.y)) / denom;
2160 const float w2 = 1.0f - w0 - w1;
2161 constexpr float eps = -1e-3f;
2162 if (w0 < eps || w1 < eps || w2 < eps) {
2163 return false;
2164 }
2165 z_out = w0 * t.a.z + w1 * t.b.z + w2 * t.c.z;
2166 return true;
2167 };
2168
2169 // per grid vertex: covered by a corridor (with z) / by an arm cap
2170 std::vector<uint8_t> v_corridor(static_cast<size_t>(nvx) * nvy, 0);
2171 std::vector<uint8_t> v_cap(static_cast<size_t>(nvx) * nvy, 0);
2172 std::vector<float> v_z(static_cast<size_t>(nvx) * nvy, 0.0f);
2173 for (int vy = 0; vy < nvy; ++vy) {
2174 for (int vx = 0; vx < nvx; ++vx) {
2175 const float px = origin_x + vx * kCell;
2176 const float py = origin_y + vy * kCell;
2177 const size_t vid = static_cast<size_t>(vy) * nvx + vx;
2178 const size_t bin = static_cast<size_t>(BinY(py)) * nbx + BinX(px);
2179 float z = 0.0f;
2180 for (int ti : corridor_bins[bin]) {
2181 if (HitTriangle(corridor_tris[ti], px, py, z)) {
2182 v_corridor[vid] = 1;
2183 v_z[vid] = z;
2184 break;
2185 }
2186 }
2187 for (int ti : cap_bins[bin]) {
2188 if (HitTriangle(cap_tris[ti], px, py, z)) {
2189 v_cap[vid] = 1;
2190 break;
2191 }
2192 }
2193 }
2194 }
2195
2196 // per cell: blocked (fully paved, or an arm cap) vs. open
2197 auto CellId = [&](int cx, int cy) { return static_cast<size_t>(cy) * ncx + cx; };
2198 std::vector<uint8_t> blocked(static_cast<size_t>(ncx) * ncy, 0);
2199 std::vector<uint8_t> full(static_cast<size_t>(ncx) * ncy, 0);
2200 for (int cy = 0; cy < ncy; ++cy) {
2201 for (int cx = 0; cx < ncx; ++cx) {
2202 const size_t v00 = static_cast<size_t>(cy) * nvx + cx;
2203 const size_t v10 = v00 + 1, v01 = v00 + nvx, v11 = v01 + 1;
2204 const int n_corr = v_corridor[v00] + v_corridor[v10] + v_corridor[v01] + v_corridor[v11];
2205 const bool any_cap = v_cap[v00] || v_cap[v10] || v_cap[v01] || v_cap[v11];
2206 full[CellId(cx, cy)] = (n_corr == 4);
2207 blocked[CellId(cx, cy)] = (n_corr == 4 || any_cap);
2208 }
2209 }
2210
2211 // Morphological closing of the corridor coverage: concave pockets
2212 // narrower than ~2*kCloseRadius get paved even when they are open to
2213 // the outside -- the wedges between corridors of *different* arms and
2214 // the curb-return mouths between adjacent arms, which a pure
2215 // enclosed-hole fill leaves green on big irregular junctions (Sunnyvale
2216 // j59: most wedges connect to the perimeter somewhere). Chamfer
2217 // distance out from the coverage (dilation), then chamfer back in from
2218 // the dilation's complement (erosion): closing = cells deeper than the
2219 // radius inside the dilated set. A straight corridor edge is invariant
2220 // under closing, so this never pads the junction's convex outline.
2221 // Radius scaled to the junction: a residential crossing needs only a
2222 // small curb-return fillet, while a sprawling multi-arm junction
2223 // (Sunnyvale j59 spans ~150 m) has paved voids tens of metres wide
2224 // between its corridors that a fixed small radius can never span.
2225 // Genuinely open bays wider than ~2R stay ground either way.
2226 const float bbox_extent = std::max(max_x - min_x, max_y - min_y);
2227 const float close_radius = std::clamp(0.12f * bbox_extent, 8.0f, 20.0f); // m
2228 {
2229 const float r_cells = close_radius / kCell;
2230 const float kInf = 1e9f;
2231 std::vector<float> dist(static_cast<size_t>(ncx) * ncy, kInf);
2232 auto Chamfer = [&](std::vector<float> &d) {
2233 for (int cy = 0; cy < ncy; ++cy) {
2234 for (int cx = 0; cx < ncx; ++cx) {
2235 float v = d[CellId(cx, cy)];
2236 if (cx > 0) v = std::min(v, d[CellId(cx - 1, cy)] + 1.0f);
2237 if (cy > 0) v = std::min(v, d[CellId(cx, cy - 1)] + 1.0f);
2238 if (cx > 0 && cy > 0) v = std::min(v, d[CellId(cx - 1, cy - 1)] + 1.4142f);
2239 if (cx + 1 < ncx && cy > 0) v = std::min(v, d[CellId(cx + 1, cy - 1)] + 1.4142f);
2240 d[CellId(cx, cy)] = v;
2241 }
2242 }
2243 for (int cy = ncy - 1; cy >= 0; --cy) {
2244 for (int cx = ncx - 1; cx >= 0; --cx) {
2245 float v = d[CellId(cx, cy)];
2246 if (cx + 1 < ncx) v = std::min(v, d[CellId(cx + 1, cy)] + 1.0f);
2247 if (cy + 1 < ncy) v = std::min(v, d[CellId(cx, cy + 1)] + 1.0f);
2248 if (cx + 1 < ncx && cy + 1 < ncy) v = std::min(v, d[CellId(cx + 1, cy + 1)] + 1.4142f);
2249 if (cx > 0 && cy + 1 < ncy) v = std::min(v, d[CellId(cx - 1, cy + 1)] + 1.4142f);
2250 d[CellId(cx, cy)] = v;
2251 }
2252 }
2253 };
2254 for (size_t i = 0; i < full.size(); ++i) {
2255 if (full[i]) dist[i] = 0.0f;
2256 }
2257 Chamfer(dist);
2258 std::vector<float> dist2(static_cast<size_t>(ncx) * ncy, kInf);
2259 for (size_t i = 0; i < dist.size(); ++i) {
2260 if (dist[i] > r_cells) dist2[i] = 0.0f; // complement of the dilation
2261 }
2262 Chamfer(dist2);
2263 for (size_t i = 0; i < dist2.size(); ++i) {
2264 // Inside the closing and not already coverage or an arm cap: pave.
2265 // Marking it blocked keeps the outside flood from running through
2266 // it, so enclosed-hole detection composes with the closing.
2267 if (dist2[i] > r_cells && !blocked[i]) {
2268 blocked[i] = 2; // 2 = closing fill (any non-zero blocks the flood)
2269 }
2270 }
2271 }
2272
2273 // flood the open cells from the grid border
2274 std::vector<uint8_t> outside(static_cast<size_t>(ncx) * ncy, 0);
2275 std::vector<std::pair<int, int>> stack;
2276 auto Seed = [&](int cx, int cy) {
2277 const size_t id = CellId(cx, cy);
2278 if (!blocked[id] && !outside[id]) {
2279 outside[id] = 1;
2280 stack.emplace_back(cx, cy);
2281 }
2282 };
2283 for (int cx = 0; cx < ncx; ++cx) { Seed(cx, 0); Seed(cx, ncy - 1); }
2284 for (int cy = 0; cy < ncy; ++cy) { Seed(0, cy); Seed(ncx - 1, cy); }
2285 while (!stack.empty()) {
2286 const auto [cx, cy] = stack.back();
2287 stack.pop_back();
2288 if (cx > 0) Seed(cx - 1, cy);
2289 if (cx + 1 < ncx) Seed(cx + 1, cy);
2290 if (cy > 0) Seed(cx, cy - 1);
2291 if (cy + 1 < ncy) Seed(cx, cy + 1);
2292 }
2293
2294 // z of a hole vertex: inverse-distance blend of the nearest corridor
2295 // vertices (ring search over the spatial hash)
2296 auto BlendZ = [&](float px, float py, bool &ok) {
2297 const int bx = BinX(px), by = BinY(py);
2298 double sum_w = 0.0, sum_z = 0.0;
2299 int found = 0;
2300 for (int r = 0; r <= 12 && (found < 4 || r == 0); ++r) {
2301 for (int y = by - r; y <= by + r; ++y) {
2302 if (y < 0 || y >= nby) continue;
2303 for (int x = bx - r; x <= bx + r; ++x) {
2304 if (x < 0 || x >= nbx) continue;
2305 if (std::abs(x - bx) != r && std::abs(y - by) != r) continue; // ring only
2306 for (int vi : vertex_bins[static_cast<size_t>(y) * nbx + x]) {
2307 const Vec3 &v = verts[vi];
2308 const double dx = v.x - px, dy = v.y - py;
2309 const double w = 1.0 / (dx * dx + dy * dy + 1e-2);
2310 sum_w += w;
2311 sum_z += w * v.z;
2312 ++found;
2313 }
2314 }
2315 }
2316 }
2317 ok = found > 0;
2318 return ok ? static_cast<float>(sum_z / sum_w) : 0.0f;
2319 };
2320
2321 // -- emit ---------------------------------------------------------------
2322 geom::Mesh out_mesh;
2323 std::vector<size_t> mesh_index(static_cast<size_t>(nvx) * nvy, 0); // 1-based, 0 = unset
2324 auto VertexIndex = [&](int vx, int vy, bool &ok) -> size_t {
2325 const size_t vid = static_cast<size_t>(vy) * nvx + vx;
2326 if (mesh_index[vid] != 0) {
2327 ok = true;
2328 return mesh_index[vid];
2329 }
2330 const float px = origin_x + vx * kCell;
2331 const float py = origin_y + vy * kCell;
2332 float z;
2333 if (v_corridor[vid]) {
2334 z = v_z[vid];
2335 ok = true;
2336 } else {
2337 z = BlendZ(px, py, ok);
2338 }
2339 if (!ok) {
2340 return 0;
2341 }
2342 out_mesh.AddVertex(Vec3(px, py, z - kZBelow));
2343 mesh_index[vid] = out_mesh.GetVerticesNum();
2344 return mesh_index[vid];
2345 };
2346
2347 size_t filled_cells = 0;
2348 out_mesh.AddMaterial("road");
2349 for (int cy = 0; cy < ncy; ++cy) {
2350 for (int cx = 0; cx < ncx; ++cx) {
2351 const size_t id = CellId(cx, cy);
2352 // Fill: closing cells (blocked == 2) and enclosed holes (open cells
2353 // the outside flood never reached). Never corridor-covered cells or
2354 // arm caps.
2355 const bool is_closing_fill = (blocked[id] == 2);
2356 const bool is_hole = (!blocked[id] && !outside[id]);
2357 if (!is_closing_fill && !is_hole) {
2358 continue;
2359 }
2360 bool ok0, ok1, ok2, ok3;
2361 const size_t i00 = VertexIndex(cx, cy, ok0);
2362 const size_t i10 = VertexIndex(cx + 1, cy, ok1);
2363 const size_t i11 = VertexIndex(cx + 1, cy + 1, ok2);
2364 const size_t i01 = VertexIndex(cx, cy + 1, ok3);
2365 if (!(ok0 && ok1 && ok2 && ok3)) {
2366 continue;
2367 }
2368 // Same winding as MeshFactory's lane strips (clockwise in the
2369 // stored x/y frame) so the UE side culls consistently.
2370 out_mesh.AddIndex(i00); out_mesh.AddIndex(i01); out_mesh.AddIndex(i11);
2371 out_mesh.AddIndex(i00); out_mesh.AddIndex(i11); out_mesh.AddIndex(i10);
2372 ++filled_cells;
2373 }
2374 }
2375 out_mesh.EndMaterial();
2376
2377#ifdef LIBCARLA_JFILL_DEBUG
2378 std::cout << "[jfill] junction " << junction.GetId() << " grid " << nvx << "x" << nvy
2379 << " corridors " << corridor_tris.size() << " caps " << cap_tris.size()
2380 << " filled " << filled_cells << std::endl;
2381#endif
2382 if (filled_cells == 0) {
2383 return nullptr;
2384 }
2385 return std::make_unique<geom::Mesh>(std::move(out_mesh));
2386 }
2387
2388 std::unique_ptr<geom::Mesh> Map::SDFToMesh(const road::Junction& jinput,
2389 const std::vector<geom::Vector3D>& /*sdfinput*/,
2390 int /*grid_cells_per_dim*/) const {
2391
2392 float box_extraextension_factor = 1.2f;
2393 const double CubeSize = 0.5;
2395 carla::geom::Vector3D MinOffset = bb.location - geom::Location(bb.extent * box_extraextension_factor);
2396 auto junctionsdf = [this, CubeSize](MeshReconstruction::Vec3 const& pos)
2397 {
2398 geom::Vector3D worldloc(static_cast<float>(pos.x), static_cast<float>(pos.y), static_cast<float>(pos.z));
2399 std::optional<element::Waypoint> CheckingWaypoint = GetWaypoint(geom::Location(worldloc), 0x1 << 1);
2400 if (CheckingWaypoint) {
2401 if ( pos.z < 0.2) {
2402 return 0.0;
2403 } else {
2404 return -abs(pos.z);
2405 }
2406 }
2407 std::optional<element::Waypoint> InRoadWaypoint = GetClosestWaypointOnRoad(geom::Location(worldloc), 0x1 << 1);
2408 geom::Transform InRoadWPTransform = ComputeTransform(*InRoadWaypoint);
2409
2410 geom::Vector3D director = geom::Location(worldloc) - (InRoadWPTransform.location);
2411 geom::Vector3D laneborder = InRoadWPTransform.location + geom::Location(director.MakeUnitVector() * static_cast<float>(GetLaneWidth(*InRoadWaypoint) * 0.5));
2412
2413 geom::Vector3D Distance = laneborder - worldloc;
2414 if (Distance.Length2D() < CubeSize * 1.1 && pos.z < 0.2) {
2415 return 0.0;
2416 }
2417 return Distance.Length() * -1.0;
2418 };
2419
2421 domain.min = { MinOffset.x, MinOffset.y, MinOffset.z };
2422 domain.size = { bb.extent.x * box_extraextension_factor * 2, bb.extent.y * box_extraextension_factor * 2, 0.4 };
2423
2424 MeshReconstruction::Vec3 cubeSize{ CubeSize, CubeSize, 0.2 };
2425 auto mesh = MeshReconstruction::MarchCube(junctionsdf, domain, cubeSize );
2426 geom::Mesh out_mesh;
2427
2428 for (auto& cv : mesh.vertices) {
2429 geom::Vector3D newvertex;
2430 newvertex.x = static_cast<float>(cv.x);
2431 newvertex.y = static_cast<float>(cv.y);
2432 newvertex.z = static_cast<float>(cv.z);
2433 out_mesh.AddVertex(newvertex);
2434 }
2435
2436 auto finalvertices = out_mesh.GetVertices();
2437 for (auto ct : mesh.triangles) {
2438 out_mesh.AddIndex(static_cast<size_t>(ct[1] + 1));
2439 out_mesh.AddIndex(static_cast<size_t>(ct[0] + 1));
2440 out_mesh.AddIndex(static_cast<size_t>(ct[2] + 1));
2441 }
2442
2443 for (auto& cv : out_mesh.GetVertices() ) {
2444 std::optional<element::Waypoint> CheckingWaypoint = GetWaypoint(geom::Location(cv), 0x1 << 1);
2445 if (!CheckingWaypoint)
2446 {
2447 std::optional<element::Waypoint> InRoadWaypoint = GetClosestWaypointOnRoad(geom::Location(cv), 0x1 << 1);
2448 geom::Transform InRoadWPTransform = ComputeTransform(*InRoadWaypoint);
2449
2450 geom::Vector3D director = geom::Location(cv) - (InRoadWPTransform.location);
2451 geom::Vector3D laneborder = InRoadWPTransform.location + geom::Location(director.MakeUnitVector() * static_cast<float>(GetLaneWidth(*InRoadWaypoint) * 0.5));
2452 cv = laneborder;
2453 }
2454 }
2455 return std::make_unique<geom::Mesh>(out_mesh);
2456 }
2457
2459 const JuncId Id,
2460 std::map<road::Lane::LaneType, std::vector<std::unique_ptr<geom::Mesh>>>*
2461 junction_out_mesh_list) const {
2462
2463 const auto& junction = _data.GetJunctions().at(Id);
2464 if (junction.GetConnections().size() > 2) {
2465 std::vector<std::unique_ptr<geom::Mesh>> lane_meshes;
2466 std::vector<std::unique_ptr<geom::Mesh>> sidewalk_lane_meshes;
2467 std::vector<carla::geom::Vector3D> perimeterpoints;
2468
2469 auto pmesh = SDFToMesh(junction, perimeterpoints, 75);
2470 (*junction_out_mesh_list)[road::Lane::LaneType::Driving].push_back(std::move(pmesh));
2471
2472 for (const auto& connection_pair : junction.GetConnections()) {
2473 const auto& connection = connection_pair.second;
2474 const auto& road = _data.GetRoads().at(connection.connecting_road);
2475 for (auto&& lane_section : road.GetLaneSections()) {
2476 for (auto&& lane_pair : lane_section.GetLanes()) {
2477 const auto& lane = lane_pair.second;
2478 if ( lane.GetType() == road::Lane::LaneType::Sidewalk ) {
2479 std::optional<element::Waypoint> sw =
2480 GetWaypoint(road.GetId(), lane_pair.first, static_cast<float>(lane.GetDistance() + (lane.GetLength() * 0.5)));
2481 if (!GetWaypoint(ComputeTransform(*sw).location).has_value()){
2482 sidewalk_lane_meshes.push_back(mesh_factory.GenerateSidewalk(lane));
2483 }
2484 }
2485 }
2486 }
2487 }
2488 std::unique_ptr<geom::Mesh> sidewalk_mesh = std::make_unique<geom::Mesh>();
2489 for (auto& lane : sidewalk_lane_meshes) {
2490 *sidewalk_mesh += *lane;
2491 }
2492 (*junction_out_mesh_list)[road::Lane::LaneType::Sidewalk].push_back(std::move(sidewalk_mesh));
2493 } else {
2494 std::vector<std::unique_ptr<geom::Mesh>> lane_meshes;
2495 std::vector<std::unique_ptr<geom::Mesh>> sidewalk_lane_meshes;
2496 for (const auto& connection_pair : junction.GetConnections()) {
2497 const auto& connection = connection_pair.second;
2498 const auto& road = _data.GetRoads().at(connection.connecting_road);
2499 for (auto&& lane_section : road.GetLaneSections()) {
2500 for (auto&& lane_pair : lane_section.GetLanes()) {
2501 const auto& lane = lane_pair.second;
2502 if (lane.GetType() != road::Lane::LaneType::Sidewalk) {
2503 lane_meshes.push_back(mesh_factory.GenerateTesselated(lane));
2504 }
2505 else {
2506 sidewalk_lane_meshes.push_back(mesh_factory.GenerateSidewalk(lane));
2507 }
2508 }
2509 }
2510 }
2511 std::unique_ptr<geom::Mesh> merged_mesh = std::make_unique<geom::Mesh>();
2512 for (auto& lane : lane_meshes) {
2513 *merged_mesh += *lane;
2514 }
2515 std::unique_ptr<geom::Mesh> sidewalk_mesh = std::make_unique<geom::Mesh>();
2516 for (auto& lane : sidewalk_lane_meshes) {
2517 *sidewalk_mesh += *lane;
2518 }
2519
2520 (*junction_out_mesh_list)[road::Lane::LaneType::Driving].push_back(std::move(merged_mesh));
2521 (*junction_out_mesh_list)[road::Lane::LaneType::Sidewalk].push_back(std::move(sidewalk_mesh));
2522 }
2523 }
2524
2525} // namespace road
2526} // namespace carla
float distance
float y
float x
float z
#define DEBUG_ASSERT(predicate)
Definition Debug.h:66
#define RELEASE_ASSERT(pred)
Definition Debug.h:84
long long first
Definition LaneGraph.cpp:34
std::string road_id
Definition LaneGraph.cpp:39
std::map< std::string, unsigned int > road
std::map< std::string, unsigned int > junction
Location location
Center of the BoundingBox in local space.
Vector3D extent
Half the size of the BoundingBox in local space.
value_type Evaluate(const value_type &x) const
Evaluates f(x) = a + bx + cx^2 + dx^3.
static double GetVectorAngle(const Vector3D &a, const Vector3D &b)
Returns the angle between 2 vectors in radians.
Definition Math.cpp:14
static auto Distance2D(const Vector3D &a, const Vector3D &b)
Definition Math.h:82
static std::pair< float, float > DistanceSegmentToPoint(const Vector3D &p, const Vector3D &v, const Vector3D &w)
Returns a pair containing:
Definition Math.cpp:18
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
Mesh helper generator.
Definition MeshFactory.h:22
std::unique_ptr< Mesh > GenerateSidewalk(const road::LaneSection &lane_section) const
std::unique_ptr< Mesh > MergeAndSmooth(std::vector< std::unique_ptr< Mesh > > &lane_meshes) const
void GenerateLaneMarkForRoad(const road::Road &road, std::vector< std::unique_ptr< Mesh > > &inout, std::vector< std::string > &outinfo) const
RoadParameters road_param
std::unique_ptr< Mesh > Generate(const road::Road &road) const
Generates a mesh that defines a road.
std::unique_ptr< Mesh > GenerateTesselated(const road::Lane &lane, const double s_start, const double s_end) const
Generates a mesh that defines a lane from a given s start and end with bigger tesselation.
std::vector< std::unique_ptr< Mesh > > GenerateAllWithMaxLen(const road::Road &road) const
Generates a chunked road with all the features needed for simulation.
void GenerateAllOrderedWithMaxLen(const road::Road &road, std::map< road::Lane::LaneType, std::vector< std::unique_ptr< Mesh > > > &roads) const
Mesh data container, validator and exporter.
Definition Mesh.h:44
void AddIndex(index_type index)
Appends a index to the indexes list.
Definition Mesh.cpp:101
void AddVertex(vertex_type vertex)
Appends a vertex to the vertices list.
Definition Mesh.cpp:89
void AddUV(uv_type uv)
Appends a vertex to the vertices list, they will be read 3 in 3.
Definition Mesh.cpp:105
const std::vector< index_type > & GetIndexes() const
Definition Mesh.cpp:323
size_t GetVerticesNum() const
Definition Mesh.cpp:315
void AddMaterial(const std::string &material_name)
Starts applying a new material to the new added triangles.
Definition Mesh.cpp:113
void EndMaterial()
Stops applying the material to the new added triangles.
Definition Mesh.cpp:129
const std::vector< vertex_type > & GetVertices() const
Definition Mesh.cpp:307
Euler angles of a rotation in CARLA's / Unreal's left-handed frame: x forward, y right,...
Definition Rotation.h:45
Vector3D GetForwardVector() const
Definition Rotation.h:75
boost::geometry::model::point< float, 3, boost::geometry::cs::cartesian > BPoint
Definition Rtree.h:89
boost::geometry::model::segment< BPoint > BSegment
Definition Rtree.h:90
std::vector< TreeElement > GetNearestNeighboursWithFilter(const Geometry &geometry, Filter filter, size_t number_neighbours=1) const
Return nearest neighbors with a user defined filter.
Definition Rtree.h:111
std::pair< BSegment, std::pair< Waypoint, Waypoint > > TreeElement
Definition Rtree.h:91
std::vector< TreeElement > GetIntersections(const Geometry &geometry) const
Returns segments that intersec the specified geometry Warning: intersection between 3D segments is no...
Definition Rtree.h:135
void InsertElements(const std::vector< TreeElement > &elements)
Definition Rtree.h:101
A rigid transform in CARLA's / Unreal's left-handed frame: x forward, y right, z up,...
void TransformPoint(Vector3D &in_point) const
Applies this transformation to in_point (first translation then rotation).
Vector3D GetForwardVector() const
carla::geom::BoundingBox GetBoundingBox() const
bool ContainsLane(LaneId id) const
std::map< LaneId, Lane > & GetLanes()
bool IsStraight() const
Checks whether the geometry is straight or not.
Definition road/Lane.cpp:67
Road * GetRoad() const
Definition road/Lane.cpp:29
const std::vector< Lane * > & GetNextLanes() const
Definition road/Lane.h:90
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 T * GetInfo(const double s) const
Definition road/Lane.h:79
LaneType
Can be used as flags.
Definition road/Lane.h:29
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
const std::vector< Lane * > & GetPreviousLanes() const
Definition road/Lane.h:94
bool IsPositiveDirection() const
double GetLength() const
Definition road/Lane.cpp:51
std::unordered_map< RoadId, Road > & GetRoads()
Definition MapData.cpp:13
Road & GetRoad(const RoadId id)
Definition MapData.cpp:21
bool ContainsRoad(RoadId id) const
Definition MapData.h:54
std::unordered_map< JuncId, Junction > & GetJunctions()
Definition MapData.cpp:17
Junction * GetJunction(JuncId id)
Definition MapData.cpp:29
std::vector< std::pair< Waypoint, Waypoint > > GenerateTopology() const
Generate the minimum set of waypoints that define the topology of map.
Definition road/Map.cpp:884
std::vector< SignalSearchData > GetSignalsInDistance(Waypoint waypoint, double distance, bool stop_at_junction=false) const
Searches signals from an initial waypoint until the defined distance.
Definition road/Map.cpp:335
JuncId GetJunctionId(RoadId road_id) const
Definition road/Map.cpp:303
std::vector< Waypoint > GetPrevious(Waypoint waypoint, double distance) const
Return the list of waypoints at distance in the reversed direction that a vehicle at waypoint could d...
Definition road/Map.cpp:718
bool IsJunction(RoadId road_id) const
Definition road/Map.cpp:307
std::optional< Waypoint > GetLeft(Waypoint waypoint) const
Return a waypoint at the lane of waypoint's left lane.
Definition road/Map.cpp:789
Lane::LaneType GetLaneType(Waypoint waypoint) const
Definition road/Map.cpp:286
std::unique_ptr< geom::Mesh > SDFToMesh(const road::Junction &jinput, const std::vector< geom::Vector3D > &sdfinput, int grid_cells_per_dim) const
double GetLaneWidth(Waypoint waypoint) const
Definition road/Map.cpp:290
std::vector< std::pair< geom::Transform, std::string > > GetTreesTransform(const geom::Vector3D &minpos, const geom::Vector3D &maxpos, float distancebetweentrees, float distancefromdrivinglineborder, float s_offset=0, bool measure_from_curb=false, bool keep_on_sidewalk=false) const
std::vector< RoadId > FilterRoadsByPosition(const geom::Vector3D &minpos, const geom::Vector3D &maxpos) const
void AddElementToRtreeAndUpdateTransforms(std::vector< Rtree::TreeElement > &rtree_elements, geom::Transform &current_transform, Waypoint &current_waypoint, Waypoint &next_waypoint)
std::vector< Waypoint > GenerateWaypointsOnRoadEntries(Lane::LaneType lane_type=Lane::LaneType::Driving) const
Generate waypoints on each lane at the start of each road.
Definition road/Map.cpp:825
std::pair< const element::RoadInfoMarkRecord *, const element::RoadInfoMarkRecord * > GetMarkRecord(Waypoint waypoint) const
Definition road/Map.cpp:312
std::vector< element::LaneMarking > CalculateCrossedLanes(const geom::Location &origin, const geom::Location &destination) const
Definition road/Map.cpp:522
std::vector< const element::RoadInfoStencil * > GetAllStencilReferences() const
Return all RoadInfoStencil in the map.
Definition road/Map.cpp:456
std::unordered_set< road::RoadId > ComputeSkippedGenerationRoads() const
Roads excluded from mesh/marking generation: link-less sub-2m slivers and link-less duplicate traces ...
std::vector< carla::geom::BoundingBox > GetJunctionsBoundingBoxes() const
std::vector< JuncId > FilterJunctionsByPosition(const geom::Vector3D &minpos, const geom::Vector3D &maxpos) const
std::map< road::Lane::LaneType, std::vector< std::unique_ptr< geom::Mesh > > > GenerateRoadsMultithreaded(const carla::geom::MeshFactory &mesh_factory, const std::vector< RoadId > &RoadsID, const size_t index, const size_t number_of_roads_per_thread) const
std::optional< element::Waypoint > GetWaypoint(const geom::Location &location, int32_t lane_type=static_cast< int32_t >(Lane::LaneType::Driving)) const
Definition road/Map.cpp:217
geom::Mesh GetAllCrosswalkMesh() const
Buids a mesh of all crosswalks based on the OpenDRIVE.
std::vector< Waypoint > GetNext(Waypoint waypoint, double distance) const
Return the list of waypoints at distance such that a vehicle at waypoint could drive to.
Definition road/Map.cpp:669
std::optional< Waypoint > GetRight(Waypoint waypoint) const
Return a waypoint at the lane of waypoint's right lane.
Definition road/Map.cpp:767
Junction * GetJunction(JuncId id)
std::optional< element::Waypoint > GetClosestWaypointOnRoad(const geom::Location &location, int32_t lane_type=static_cast< int32_t >(Lane::LaneType::Driving)) const
========================================================================
Definition road/Map.cpp:170
void GenerateJunctions(const carla::geom::MeshFactory &mesh_factory, const rpc::OpendriveGenerationParameters &params, const geom::Vector3D &minpos, const geom::Vector3D &maxpos, std::map< road::Lane::LaneType, std::vector< std::unique_ptr< geom::Mesh > > > *juntion_out_mesh_list) const
std::vector< Waypoint > GenerateWaypointsInRoad(RoadId road_id, Lane::LaneType lane_type=Lane::LaneType::Driving) const
Generate waypoints at the entry of each lane of the specified road.
Definition road/Map.cpp:854
std::vector< std::unique_ptr< geom::Mesh > > GenerateChunkedMesh(const rpc::OpendriveGenerationParameters &params) const
geom::Transform ComputeTransform(Waypoint waypoint) const
Definition road/Map.cpp:278
std::vector< StencilSearchData > GetStencilsInDistance(Waypoint waypoint, double distance, bool stop_at_junction=false) const
Searches stencils from an initial waypoint until the defined distance.
Definition road/Map.cpp:468
std::vector< Waypoint > GenerateWaypoints(double approx_distance) const
Generate all the waypoints in map separated by approx_distance.
Definition road/Map.cpp:811
std::map< road::Lane::LaneType, std::vector< std::unique_ptr< geom::Mesh > > > GenerateOrderedChunkedMeshInLocations(const rpc::OpendriveGenerationParameters &params, const geom::Vector3D &minpos, const geom::Vector3D &maxpos) const
void GenerateSingleJunction(const carla::geom::MeshFactory &mesh_factory, const JuncId Id, std::map< road::Lane::LaneType, std::vector< std::unique_ptr< geom::Mesh > > > *junction_out_mesh_list) const
std::vector< Waypoint > GetPredecessors(Waypoint waypoint) const
Definition road/Map.cpp:643
std::vector< Waypoint > GetSuccessors(Waypoint waypoint) const
========================================================================
Definition road/Map.cpp:617
std::vector< geom::Location > GetAllCrosswalkZones() const
Returns a list of locations defining 2d areas, when a location is repeated an area is finished.
Definition road/Map.cpp:528
std::vector< std::unique_ptr< geom::Mesh > > GenerateLineMarkings(const rpc::OpendriveGenerationParameters &params, const geom::Vector3D &minpos, const geom::Vector3D &maxpos, std::vector< std::string > &outinfo) const
Buids a list of meshes related with LineMarkings.
float GetZPosInDeformation(float posx, float posy) const
void AddElementToRtree(std::vector< Rtree::TreeElement > &rtree_elements, geom::Transform &current_transform, geom::Transform &next_transform, Waypoint &current_waypoint, Waypoint &next_waypoint)
Helper Functions for constructing the rtree element list.
std::vector< const element::RoadInfoSignal * > GetAllSignalReferences() const
Return all RoadInfoSignal in the map.
Definition road/Map.cpp:443
std::vector< std::pair< Waypoint, Waypoint > > GetJunctionWaypoints(JuncId id, Lane::LaneType lane_type) const
Generate waypoints of the junction.
Definition road/Map.cpp:907
std::unordered_map< road::RoadId, std::unordered_set< road::RoadId > > ComputeJunctionConflicts(JuncId id) const
Definition road/Map.cpp:924
std::unique_ptr< geom::Mesh > GenerateJunctionFill(const road::Junction &junction, const geom::Mesh &corridor_mesh) const
Paves the regions a junction's lane corridors enclose but don't cover (see GenerateChunkedMesh).
const Lane & GetLane(Waypoint waypoint) const
========================================================================
Definition road/Map.cpp:994
geom::Mesh GenerateMesh(const double distance, const float extra_width=0.6f, const bool smooth_junctions=true) const
Buids a mesh based on the OpenDRIVE.
Lane & GetLaneById(SectionId section_id, LaneId lane_id)
bool IsRHT() const
Definition road/Road.cpp:52
JuncId GetJunctionId() const
Definition road/Road.cpp:48
bool IsJunction() const
Definition road/Road.cpp:44
LaneSection & GetLaneSectionById(SectionId id)
Definition road/Road.h:172
static std::vector< LaneMarking > Calculate(const Map &map, const geom::Location &origin, const geom::Location &destination)
Lane Width RecordEach lane within a road’scross section can be provided with severalwidth entries.
const geom::CubicPolynomial & GetPolynomial() const
Each lane within a road cross section can be provided with several road markentries.
double GetDistance() const
Distance from road's start location.
Definition RoadInfo.h:28
Mesh MarchCube(Fun3s const &sdf, Rect3 const &domain)
Reconstructs a triangle mesh from a given signed distance function using Marching Cubes.
float GetBumpDeformation(float posx, float posy)
Definition Deformation.h:39
float GetZPosInDeformation(float posx, float posy)
Definition Deformation.h:21
double GetRemainingLength(const Lane &lane, double current_s)
static bool IsOrphanSliverRoad(const road::Road &road)
static void ForEachDrivableLane(const Road &road, FuncT &&func)
Return a waypoint for each drivable lane on each lane section of road.
Definition road/Map.cpp:125
int32_t JuncId
Definition RoadTypes.h:17
static constexpr double EPSILON
We use this epsilon to shift the waypoints away from the edges of the lane sections to avoid floating...
Definition road/Map.cpp:44
static std::unordered_set< RoadId > ComputeDuplicateTraceRoads(const Map &map, const std::unordered_set< RoadId > &candidate_ids)
static bool IsLanePresent(const MapData &data, Waypoint waypoint)
Assumes road_id and section_id are valid.
Definition road/Map.cpp:161
static void ForEachDrivableLaneAt(const Road &road, double distance, FuncT &&func)
Return a waypoint for each drivable lane at distance on road.
Definition road/Map.cpp:150
static void ForEachDrivableLaneImpl(RoadId road_id, const LaneSection &lane_section, double distance, FuncT &&func)
Return a waypoint for each drivable lane on lane_section.
Definition road/Map.cpp:81
static void ForEachLane(const Road &road, Lane::LaneType lane_type, FuncT &&func)
Return a waypoint for each lane of the specified type on each lane section of road.
Definition road/Map.cpp:137
uint32_t RoadId
Definition RoadTypes.h:15
static void ForEachLaneImpl(RoadId road_id, const LaneSection &lane_section, double distance, Lane::LaneType lane_type, FuncT &&func)
Definition road/Map.cpp:102
static double GetDistanceAtStartOfLane(const Lane &lane)
Definition road/Map.cpp:63
int32_t LaneId
Definition RoadTypes.h:19
static std::vector< T > ConcatVectors(std::vector< T > dst, std::vector< T > src)
Definition road/Map.cpp:52
static double GetDistanceAtEndOfLane(const Lane &lane)
Definition road/Map.cpp:71
static constexpr double TREE_PLACEMENT_EPSILON
Definition road/Map.cpp:45
This file contains definitions of common data structures used in traffic manager.
Vector3D MakeUnitVector(const float epsilon=2.0f *std::numeric_limits< float >::epsilon()) const
Return a unit-length copy of this vector.
float Length2D() const
Data structure for the signal search.
Definition road/Map.h:99
Data structure for the stencil search.
Definition road/Map.h:212
const element::RoadInfoStencil * stencil
Definition road/Map.h:213
Seting for map generation from opendrive without additional geometry.