Carma-platform v4.2.0
CARMA Platform is built on robot operating system (ROS) and utilizes open source software (OSS) that enables Cooperative Driving Automation (CDA) features to allow Automated Driving Systems to interact and cooperate with infrastructure and other vehicles through communication.
CARMAWorldModel.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2022 LEIDOS.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17#include <tuple>
18#include <algorithm>
19#include <assert.h>
21#include <lanelet2_routing/RoutingGraph.h>
22#include <lanelet2_traffic_rules/TrafficRulesFactory.h>
23#include <lanelet2_core/Attribute.h>
24#include <lanelet2_core/geometry/LineString.h>
25#include <lanelet2_core/primitives/Traits.h>
26#include <Eigen/Core>
27#include <Eigen/LU>
28#include <cmath>
29#include <lanelet2_core/geometry/Polygon.h>
30#include <boost/geometry.hpp>
31#include <boost/geometry/geometries/polygon.hpp>
32#include "carma_wm/Geometry.hpp"
33#include <queue>
34#include <boost/math/special_functions/sign.hpp>
35#include <boost/date_time/posix_time/conversion.hpp>
36
37namespace carma_wm
38{
39
40 std::pair<TrackPos, TrackPos> CARMAWorldModel::routeTrackPos(const lanelet::ConstArea& area) const
41 {
42 // Check if the route was loaded yet
43 if (!route_)
44 {
45 throw std::invalid_argument("Route has not yet been loaded");
46 }
47
48 lanelet::ConstLineStrings3d outer_bound = area.outerBound();
49
50 if (outer_bound.empty())
51 {
52 throw std::invalid_argument("Provided area outer bound is invalid as it contains no points");
53 }
54
55 TrackPos minPos(0, 0);
56 TrackPos maxPos(0, 0);
57 bool first = true;
58 for (lanelet::ConstLineString3d sub_bound3d : outer_bound)
59 {
60 auto sub_bound = lanelet::utils::to2D(sub_bound3d);
61 for (lanelet::ConstPoint2d point : sub_bound)
62 {
64 if (first)
65 {
66 minPos = maxPos = tp;
67 first = false;
68 }
69 else if (tp.downtrack < minPos.downtrack)
70 {
71 minPos.downtrack = tp.downtrack;
72 minPos.crosstrack = tp.crosstrack;
73 }
74 else if (tp.downtrack > maxPos.downtrack)
75 {
76 maxPos.downtrack = tp.downtrack;
77 maxPos.crosstrack = tp.crosstrack;
78 }
79 }
80 }
81 return std::make_pair(minPos, maxPos);
82 }
83
84 TrackPos CARMAWorldModel::routeTrackPos(const lanelet::ConstLanelet& lanelet) const
85 {
86 // Check if the route was loaded yet
87 if (!route_)
88 {
89 throw std::invalid_argument("Route has not yet been loaded");
90 }
91
92 lanelet::ConstLineString2d centerline = lanelet::utils::to2D(lanelet.centerline());
93 if (centerline.empty())
94 {
95 throw std::invalid_argument("Provided lanelet has invalid centerline containing no points");
96 }
97 auto front = centerline.front();
98 return routeTrackPos(front);
99 }
100
101 std::vector<lanelet::BusStopRulePtr> CARMAWorldModel::getBusStopsAlongRoute(const lanelet::BasicPoint2d& loc) const
102 {
103 // Check if the map is loaded yet
104 if (!semantic_map_ || semantic_map_->laneletLayer.empty())
105 {
106 RCLCPP_ERROR_STREAM(rclcpp::get_logger("carma_wm"), "Map is not set or does not contain lanelets");
107 return {};
108 }
109 // Check if the route was loaded yet
110 if (!route_)
111 {
112 RCLCPP_ERROR_STREAM(rclcpp::get_logger("carma_wm"), "Route has not yet been loaded");
113 return {};
114 }
115 std::vector<lanelet::BusStopRulePtr> bus_stop_list;
116 auto curr_downtrack = routeTrackPos(loc).downtrack;
117 // shortpath is already sorted by distance
118
119 for (const auto &ll : route_->shortestPath())
120 {
121 auto bus_stops = semantic_map_->laneletLayer.get(ll.id()).regulatoryElementsAs<lanelet::BusStopRule>();
122 if (bus_stops.empty())
123 {
124 continue;
125 }
126
127 for (auto bus_stop : bus_stops)
128 {
129 auto stop_line = bus_stop->stopAndWaitLine();
130 if (stop_line.empty())
131 {
132 RCLCPP_ERROR_STREAM(rclcpp::get_logger("carma_wm"), "No stop line");
133 continue;
134 }
135 else
136 {
137 double bus_stop_downtrack = routeTrackPos(stop_line.front().front().basicPoint2d()).downtrack;
138 double distance_remaining_to_bus_stop = bus_stop_downtrack - curr_downtrack;
139
140 if (distance_remaining_to_bus_stop < 0)
141 {
142 continue;
143 }
144 bus_stop_list.push_back(bus_stop);
145 }
146 }
147 }
148 return bus_stop_list;
149 }
150
151 TrackPos CARMAWorldModel::routeTrackPos(const lanelet::BasicPoint2d& point) const
152 {
153 // Check if the route was loaded yet
154 if (!route_)
155 {
156 throw std::invalid_argument("Route has not yet been loaded");
157 }
158
159 // Find the nearest continuos shortest path centerline segment using fast map nearest search
160 lanelet::Points3d near_points =
161 shortest_path_filtered_centerline_view_->pointLayer.nearest(point, 1); // Find the nearest points
162
163 // Match point with linestring using fast map lookup
164 auto lineString_1 =
165 lanelet::utils::to2D(shortest_path_filtered_centerline_view_->lineStringLayer.findUsages(near_points[0])
166 .front()); // Only need the first index due to nature of route centerline
167
168 if (lineString_1.size() == 0)
169 {
170 throw std::invalid_argument("Invalid route loaded. Shortest path does not have proper references");
171 }
172
173 // New approach
174 // 1. Find nearest point
175 // 2. Find linestring associated with nearest point
176 // 3. If the nearest point is the first point on the linestring then we need to check the downtrack value
177 // 4. -- If donwtrack is negative then iterate over route segments to find preceeding segment
178 // 5. -- If downtrack is positive then we are on the correct segment
179 // 6. If the nearest point is the last point on the linestring then we need to check downtrack value
180 // 7. -- If downtrack is less then seg length then we are on the correct segment
181 // 8. -- If downtrack is greater then seg length then iterate over route segments to find succeeding segment
182 // 9. With correct segment identified compute segment downtrack distance
183 // 10. Accumulate previos segment distances if needed.
184
185 // Find best route segment
186 lanelet::Id bestRouteSegId;
187 TrackPos tp(0, 0);
188 // Check for end cases
189
190 auto indexes = shortest_path_distance_map_.getIndexFromId(near_points[0].id());
191 size_t ls_i = indexes.first;
192 size_t p_i = indexes.second;
193
194 if (near_points[0].id() == lineString_1.front().id())
195 { // Nearest point is at the start of a line string
196 // Get start point of cur segment and add 1
197 auto next_point = lineString_1[1];
198 TrackPos tp_next = geometry::trackPos(point, lineString_1.front().basicPoint(), next_point.basicPoint());
199
200 if (tp_next.downtrack >= 0 || ls_i == 0)
201 {
202 bestRouteSegId = lineString_1.id();
203 tp = tp_next;
204 // If downtrack is positive then we are on the correct segment
205 }
206 else
207 {
208 // If downtrack is negative then iterate over route segments to find preceeding segment
209 const size_t prev_ls_i = ls_i - 1;
210
211 auto prev_centerline = lanelet::utils::to2D(shortest_path_centerlines_[prev_ls_i]); // Get prev centerline
212 tp = geometry::trackPos(point, prev_centerline[prev_centerline.size() - 2].basicPoint(),
213 prev_centerline[prev_centerline.size() - 1].basicPoint());
214 tp.downtrack += shortest_path_distance_map_.distanceToPointAlongElement(prev_ls_i, prev_centerline.size() - 2);
215 bestRouteSegId = prev_centerline.id();
216 }
217 }
218 else if (near_points[0].id() == lineString_1.back().id())
219 { // Nearest point is the end of a line string
220
221 // Get end point of cur segment and subtract 1
222 auto prev_prev_point = lineString_1[lineString_1.size() - 2];
223 auto prev_point = lineString_1[lineString_1.size() - 1];
224
225 TrackPos tp_prev = geometry::trackPos(point, prev_prev_point.basicPoint(), prev_point.basicPoint());
226
227 double last_seg_length =
228 shortest_path_distance_map_.distanceBetween(ls_i, lineString_1.size() - 2, lineString_1.size() - 1);
229
230 if (tp_prev.downtrack < last_seg_length || ls_i == shortest_path_centerlines_.size() - 1)
231 {
232 // If downtrack is less then seg length then we are on the correct segment
233 bestRouteSegId = lineString_1.id();
234 tp = tp_prev;
235 tp.downtrack += shortest_path_distance_map_.distanceToPointAlongElement(ls_i, lineString_1.size() - 2);
236 }
237 else
238 {
239 // If downtrack is greater then seg length then we need to find the succeeding segment
240 auto next_centerline = lanelet::utils::to2D(shortest_path_centerlines_[ls_i + 1]); // Get prev centerline
241 tp = geometry::trackPos(point, next_centerline[0].basicPoint(), next_centerline[1].basicPoint());
242 bestRouteSegId = next_centerline.id();
243 }
244 }
245 else
246 { // The nearest point is in the middle of a line string
247 // Graph the two bounding points on the line string and call matchSegment using a 3 element segment
248 // There is a guarantee from the earlier if statements that near_points[0] will always be located at an index within
249 // the exclusive range (0,lineString_1.size() - 1) so no need for range checks
250
251 lanelet::BasicLineString2d subSegment = lanelet::BasicLineString2d(
252 { lineString_1[p_i - 1].basicPoint(), lineString_1[p_i].basicPoint(), lineString_1[p_i + 1].basicPoint() });
253
254 tp = std::get<0>(geometry::matchSegment(point, subSegment)); // Get track pos along centerline
255
256 tp.downtrack += shortest_path_distance_map_.distanceToPointAlongElement(ls_i, p_i - 1);
257
258 bestRouteSegId = lineString_1.id();
259 }
260
261 // Accumulate distance
262 auto bestRouteSegIndex = shortest_path_distance_map_.getIndexFromId(bestRouteSegId);
263 tp.downtrack += shortest_path_distance_map_.distanceToElement(bestRouteSegIndex.first);
264
265 return tp;
266 }
267
269 {
270 public:
271 lanelet::ConstLanelet lanelet_;
272 double downtrack_ = 0;
273
274 LaneletDowntrackPair(lanelet::ConstLanelet lanelet, double downtrack) : lanelet_(lanelet), downtrack_(downtrack)
275 {
276 }
277 bool operator<(const LaneletDowntrackPair& pair) const
278 {
279 return this->downtrack_ < pair.downtrack_;
280 }
281 bool operator>(const LaneletDowntrackPair& pair) const
282 {
283 return this->downtrack_ > pair.downtrack_;
284 }
285 };
286
287 std::vector<lanelet::ConstLanelet> CARMAWorldModel::getLaneletsBetween(double start, double end, bool shortest_path_only,
288 bool bounds_inclusive) const
289 {
290 // Check if the route was loaded yet
291 if (!route_)
292 {
293 throw std::invalid_argument("Route has not yet been loaded");
294 }
295 if (start > end)
296 {
297 throw std::invalid_argument("Start distance is greater than end distance");
298 }
299
300 std::vector<lanelet::ConstLanelet> output;
301 std::priority_queue<LaneletDowntrackPair, std::vector<LaneletDowntrackPair>, std::greater<LaneletDowntrackPair>>
302 prioritized_lanelets;
303
304 auto lanelet_map = route_->laneletMap();
305 for (lanelet::ConstLanelet lanelet : lanelet_map->laneletLayer)
306 {
307 if (shortest_path_only && !shortest_path_view_->laneletLayer.exists(lanelet.id()))
308 {
309 continue; // Continue if we are only evaluating the shortest path and this lanelet is not part of it
310 }
311 lanelet::ConstLineString2d centerline = lanelet::utils::to2D(lanelet.centerline());
312
313 auto front = centerline.front();
314 auto back = centerline.back();
315 TrackPos min = routeTrackPos(front);
316 TrackPos max = routeTrackPos(back);
317
318 if (!bounds_inclusive) // reduce bounds slightly to avoid including exact bounds
319 {
320 if (std::max(min.downtrack, start + 0.00001) > std::min(max.downtrack, end - 0.00001)
321 || (start == end && (min.downtrack >= start || max.downtrack <= end)))
322 { // Check for 1d intersection
323 // No intersection so continue
324 continue;
325 }
326 }
327 else
328 {
329 if (std::max(min.downtrack, start) > std::min(max.downtrack, end)
330 || (start == end && (min.downtrack > start || max.downtrack < end)))
331 { // Check for 1d intersection
332 // No intersection so continue
333 continue;
334 }
335 }
336 // Intersection has occurred so add lanelet to list
338 prioritized_lanelets.push(pair);
339 }
340
341 output.reserve(prioritized_lanelets.size());
342 while (!prioritized_lanelets.empty())
343 {
344 auto pair = prioritized_lanelets.top();
345 prioritized_lanelets.pop();
346 output.push_back(pair.lanelet_);
347 }
348
349 if (!shortest_path_only)
350 {
351 return output;
352 }
353
354 //Sort lanelets according to shortest path if using shortest path
355 std::vector<lanelet::ConstLanelet> sorted_output;
356 for (auto llt : route_->shortestPath())
357 {
358 for (size_t i = 0; i < output.size(); i++)
359 {
360 if (llt.id() == output[i].id())
361 {
362 sorted_output.push_back(llt);
363 break;
364 }
365 }
366 }
367
368 return sorted_output;
369 }
370
371 std::vector<lanelet::BasicPoint2d> CARMAWorldModel::sampleRoutePoints(double start_downtrack, double end_downtrack,
372 double step_size) const
373 {
374 std::vector<lanelet::BasicPoint2d> output;
375 if (!route_)
376 {
377 RCLCPP_WARN_STREAM(rclcpp::get_logger("carma_wm"), "Route has not yet been loaded");
378 return output;
379 }
380
381 double route_end = getRouteEndTrackPos().downtrack;
382
383 if (start_downtrack < 0 || start_downtrack > route_end || end_downtrack < 0 || end_downtrack > route_end ||
384 start_downtrack > end_downtrack)
385 {
386 RCLCPP_WARN_STREAM(rclcpp::get_logger("carma_wm"), "Invalid input downtracks");
387 return output;
388 }
389
390 TrackPos route_pos(start_downtrack, 0);
391
392 if (end_downtrack == start_downtrack)
393 {
394 output.emplace_back(*(pointFromRouteTrackPos(route_pos))); // If a single point was provided return that point
395 return output;
396 }
397
398 output.reserve(2 + (end_downtrack - start_downtrack) / step_size);
399 double downtrack = start_downtrack;
400 while (downtrack < end_downtrack)
401 {
402 route_pos.downtrack = downtrack;
403 output.emplace_back(*(pointFromRouteTrackPos(route_pos)));
404 downtrack += step_size;
405 }
406
407 route_pos.downtrack = end_downtrack;
408 output.emplace_back(*(pointFromRouteTrackPos(route_pos)));
409 return output;
410 }
411
412 boost::optional<lanelet::BasicPoint2d> CARMAWorldModel::pointFromRouteTrackPos(const TrackPos& route_pos) const
413 {
414 double downtrack = route_pos.downtrack;
415 double crosstrack = route_pos.crosstrack;
416
417 if (!route_)
418 {
419 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm"), "Route has not yet been loaded");
420 return boost::none;
421 }
422
423 if (downtrack < 0 || downtrack > getRouteEndTrackPos().downtrack)
424 {
425 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm"), "Tried to convert a downtrack of: " << downtrack
426 << " to map point, but it did not fit in route bounds of "
427 << getRouteEndTrackPos().downtrack);
428 return boost::none;
429 }
430
431 // Use fast lookup to identify the points before and after the provided downtrack on the route
432 auto indices = shortest_path_distance_map_.getElementIndexByDistance(downtrack, true); // Get the linestring matching the provided downtrack
433 size_t ls_i = std::get<0>(indices);
434 size_t pt_i = std::get<1>(indices);
435
436 auto linestring = shortest_path_centerlines_[ls_i];
437
438 if (pt_i >= linestring.size())
439 {
440 throw std::invalid_argument("Impossible index: pt: " + std::to_string(pt_i) + " linestring: " + std::to_string(ls_i));
441 }
442
443 double ls_downtrack = shortest_path_distance_map_.distanceToElement(ls_i);
444
445 double relative_downtrack = downtrack - ls_downtrack;
446
447 size_t centerline_size = linestring.size();
448
449 size_t prior_idx = std::min(pt_i, centerline_size - 1);
450
451 size_t next_idx = std::min(pt_i + 1, centerline_size - 1);
452
453 // This if block handles the edge case where the downtrack distance has landed exactly on an existing point
454 if (prior_idx == next_idx)
455 { // If both indexes are the same we are on the point
456
457 if (crosstrack == 0)
458 { // Crosstrack not provided so we can return the point directly
459 auto prior_point = linestring[prior_idx];
460
461 return lanelet::BasicPoint2d(prior_point.x(), prior_point.y());
462 }
463
464 lanelet::BasicPoint2d prior_point, next_point;
465 double x, y;
466 if (prior_idx < centerline_size - 1)
467 { // If this is not the end point compute the crosstrack based on the next point
468 prior_point = linestring[prior_idx].basicPoint2d();
469 next_point = linestring[prior_idx + 1].basicPoint2d(); // No need to check bounds as this class will reject routes with fewer than 2 points in a centerline
470 x = prior_point.x();
471 y = prior_point.y();
472 }
473 else
474 { // If this is the end point compute the crosstrack based on previous point
475 prior_point = linestring[prior_idx - 1].basicPoint2d();
476 next_point = linestring[prior_idx].basicPoint2d();
477 x = next_point.x();
478 y = next_point.y();
479 }
480
481 // Compute the crosstrack
482 double sigma = geometry::point_to_point_yaw(prior_point, next_point); // Angle between route segment and x-axis
483 double theta = sigma + M_PI_2; // M_PI_2 is 90 deg. Theta is the angle to the vector from the route projected point
484 // to the target point
485 double delta_x = cos(theta) * -crosstrack;
486 double delta_y = sin(theta) * -crosstrack;
487
488 return lanelet::BasicPoint2d(x + delta_x, y + delta_y);
489 }
490
491 double prior_downtrack = shortest_path_distance_map_.distanceToPointAlongElement(ls_i, prior_idx);
492 double next_downtrack = shortest_path_distance_map_.distanceToPointAlongElement(ls_i, next_idx);
493
494 double prior_to_next_dist = next_downtrack - prior_downtrack;
495 double prior_to_target_dist = relative_downtrack - prior_downtrack;
496 double interpolation_percentage = 0;
497
498 if (prior_to_next_dist < 0.000001)
499 {
500 interpolation_percentage = 0;
501 }
502 else
503 {
504 interpolation_percentage = prior_to_target_dist / prior_to_next_dist; // Use the percentage progress between both points to compute the downtrack point
505 }
506
507 auto prior_point = linestring[prior_idx].basicPoint2d();
508 auto next_point = linestring[next_idx].basicPoint2d();
509 auto delta_vec = next_point - prior_point;
510
511 double x = prior_point.x() + interpolation_percentage * delta_vec.x();
512 double y = prior_point.y() + interpolation_percentage * delta_vec.y();
513
514 if (crosstrack != 0) // If the crosstrack is not set no need to do the costly computation.
515 {
516 double sigma = geometry::point_to_point_yaw(prior_point, next_point); // Angle between route segment and x-axis
517 double theta = sigma + M_PI_2; // M_PI_2 is 90 deg. Theta is the angle to the vector from the route projected point
518 // to the target point
519 double delta_x = cos(theta) * -crosstrack;
520 double delta_y = sin(theta) * -crosstrack;
521
522 x += delta_x; // Adjust x and y of target point to account for crosstrack
523 y += delta_y;
524 }
525
526 return lanelet::BasicPoint2d(x, y);
527 }
528
529 lanelet::LaneletMapConstPtr CARMAWorldModel::getMap() const
530 {
531 return std::static_pointer_cast<lanelet::LaneletMap const>(semantic_map_); // Cast pointer to const variant
532 }
533
535 {
536 return std::static_pointer_cast<const lanelet::routing::Route>(route_); // Cast pointer to const variant
537 }
538
540 {
542 return p;
543 }
544
545 void CARMAWorldModel::setMap(lanelet::LaneletMapPtr map, size_t map_version, bool recompute_routing_graph)
546 {
547 // If this is the first time the map has been set, then recompute the routing graph
548 if (!semantic_map_)
549 {
550
551 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm"), "First time map is set in carma_wm. Routing graph will be recomputed reguardless of method inputs.");
552
553 recompute_routing_graph = true;
554 }
555
556 semantic_map_ = map;
557 map_version_ = map_version;
558
559 // If the routing graph should be updated then recompute it
560 if (recompute_routing_graph)
561 {
562
563 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm"), "Building routing graph");
564
566
567 if (!tr)
568 {
569 throw std::invalid_argument("Could not construct traffic rules for participant");
570 }
571
572 TrafficRulesConstPtr traffic_rules = *tr;
573
574 lanelet::routing::RoutingGraphUPtr map_graph = lanelet::routing::RoutingGraph::build(*semantic_map_, *traffic_rules);
575 map_routing_graph_ = std::move(map_graph);
576
577 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm"), "Done building routing graph");
578 }
579 }
580
582
583 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm"), "Setting the routing graph with user or listener provided graph");
584
585 map_routing_graph_ = graph;
586 }
587
589 {
590 return map_version_;
591 }
592
593 lanelet::LaneletMapPtr CARMAWorldModel::getMutableMap() const
594 {
595 return semantic_map_;
596 }
597
598 void CARMAWorldModel::setRos1Clock(const rclcpp::Time& time_now)
599 {
600 sim_.ros1_clock_ = time_now;
601 }
602
603 void CARMAWorldModel::setSimulationClock(const rclcpp::Time& time_now)
604 {
605 sim_.simulation_clock_ = time_now;
606 }
607
609 {
610 route_ = route;
611 lanelet::ConstLanelets path_lanelets(route_->shortestPath().begin(), route_->shortestPath().end());
612 shortest_path_view_ = lanelet::utils::createConstSubmap(path_lanelets, {});
614 // NOTE: Setting the route_length_ field here will likely result in the final lanelets final point being used. Call setRouteEndPoint to use the destination point value
615 route_length_ = routeTrackPos(route_->getEndPoint().basicPoint2d()).downtrack; // Cache the route length with
616 // consideration for endpoint
617 }
618
619 void CARMAWorldModel::setRouteEndPoint(const lanelet::BasicPoint3d& end_point)
620 {
621 if (!route_)
622 {
623 throw std::invalid_argument("Route endpoint set before route was available.");
624 }
625
626 lanelet::ConstPoint3d const_end_point{ lanelet::utils::getId(), end_point.x(), end_point.y(), end_point.z() };
627
628 route_->setEndPoint(const_end_point);
629
630 route_length_ = routeTrackPos(route_->getEndPoint().basicPoint2d()).downtrack; // Cache the route length with
631 // consideration for endpoint
632 }
633
634 void CARMAWorldModel::setRouteName(const std::string& route_name)
635 {
636 route_name_ = route_name;
637 }
638
640 {
641 return route_name_;
642 }
643
644 lanelet::LineString3d CARMAWorldModel::copyConstructLineString(const lanelet::ConstLineString3d& line) const
645 {
646 std::vector<lanelet::Point3d> coppied_points;
647 coppied_points.reserve(line.size());
648
649 for (auto basic_p : line.basicLineString())
650 {
651 coppied_points.push_back(lanelet::Point3d(lanelet::utils::getId(), basic_p));
652 }
653
654 return lanelet::LineString3d(lanelet::utils::getId(), coppied_points);
655 }
656
658 {
659 IndexedDistanceMap distance_map;
660
661 lanelet::routing::LaneletPath shortest_path = route_->shortestPath();
662 // Build shortest path routing graph
664
665 lanelet::routing::RoutingGraphUPtr shortest_path_graph =
666 lanelet::routing::RoutingGraph::build(*shortest_path_view_, *traffic_rules);
667
668 std::vector<lanelet::LineString3d> lineStrings; // List of continuos line strings representing segments of the route
669 // reference line
670
671 bool first = true;
672 size_t next_index = 0;
673 // Iterate over each lanelet in the shortest path this loop works by looking one lanelet ahead to detect lane changes
674 for (lanelet::ConstLanelet ll : shortest_path)
675 {
676 next_index++;
677 if (first)
678 { // For the first lanelet store its centerline and length
679 lineStrings.push_back(copyConstructLineString(ll.centerline()));
680 first = false;
681 }
682 if (next_index < shortest_path.size())
683 { // Check for remaining lanelets
684 auto nextLanelet = shortest_path[next_index];
685 lanelet::LineString3d nextCenterline = copyConstructLineString(nextLanelet.centerline());
686 size_t connectionCount = shortest_path_graph->possiblePaths(ll, (uint32_t)2, false).size();
687
688 if (connectionCount == 1)
689 { // Get list of connected lanelets without lanechanges. On the shortest path this should only return 1 or 0
690 // No lane change
691 // Append distance to current centerline
692 size_t offset =
693 lineStrings.size() == 0 || lineStrings.back().size() == 0 ?
694 0 :
695 1; // Prevent duplicate points when concatenating. Not clear if causes an issue at lane changes
696 if ((nextCenterline.size() <= 1) || (nextCenterline.size() <= 2 && offset == 1))
697 {
698 throw std::invalid_argument("Cannot process route with lanelet containing very short centerline");
699 }
700 lineStrings.back().insert(lineStrings.back().end(), nextCenterline.begin() + offset, nextCenterline.end());
701 }
702 else if (connectionCount == 0)
703 {
704 // Lane change required
705 // Break the point chain when a lanechange occurs
706 if (lineStrings.back().size() == 0)
707 continue; // we don't have to create empty_linestring if we already have one
708 // occurs when route is changing lanes multiple times in sequence
709 lanelet::LineString3d empty_linestring;
710 empty_linestring.setId(lanelet::utils::getId());
711 distance_map.pushBack(lanelet::utils::to2D(lineStrings.back()));
712 lineStrings.push_back(empty_linestring);
713 }
714 else
715 {
716 assert(false); // It should not be possable to reach this point. Doing so demonstrates a bug
717 }
718 }
719 }
720 // Copy values to member variables
721 while (lineStrings.back().size() == 0)
722 lineStrings.pop_back(); // clear empty linestrings that was never used in the end
723 shortest_path_centerlines_ = lineStrings;
724 shortest_path_distance_map_ = distance_map;
725
726 // Add length of final sections
728 {
729 shortest_path_distance_map_.pushBack(lanelet::utils::to2D(lineStrings.back())); // Record length of last continuous
730 // segment
731 }
732
733 // Since our copy constructed linestrings do not contain references to lanelets they can be added to a full map
734 // instead of a submap
736 }
737
739 {
740 return std::static_pointer_cast<const lanelet::routing::RoutingGraph>(map_routing_graph_); // Cast pointer to const
741 // variant
742 }
743
744 lanelet::Optional<TrafficRulesConstPtr> CARMAWorldModel::getTrafficRules(const std::string& participant) const
745 {
746 lanelet::Optional<TrafficRulesConstPtr> optional_ptr;
747 // Create carma traffic rules object
748 try
749 {
750 lanelet::traffic_rules::TrafficRulesUPtr traffic_rules = lanelet::traffic_rules::TrafficRulesFactory::create(
751 lanelet::traffic_rules::CarmaUSTrafficRules::Location, participant);
752
753 auto carma_traffic_rules = std::make_shared<lanelet::traffic_rules::CarmaUSTrafficRules>();
754
755 carma_traffic_rules = std::static_pointer_cast<lanelet::traffic_rules::CarmaUSTrafficRules>(
756 lanelet::traffic_rules::TrafficRulesPtr(std::move(traffic_rules)));
757 carma_traffic_rules->setConfigSpeedLimit(config_speed_limit_);
758
759 optional_ptr = std::static_pointer_cast<const lanelet::traffic_rules::CarmaUSTrafficRules>(carma_traffic_rules);
760 }
761 catch (const lanelet::InvalidInputError& e)
762 {
763 return optional_ptr;
764 }
765
766 return optional_ptr;
767 }
768
769 lanelet::Optional<TrafficRulesConstPtr> CARMAWorldModel::getTrafficRules() const
770 {
771
773 }
774
775 lanelet::Optional<carma_perception_msgs::msg::RoadwayObstacle>
776 CARMAWorldModel::toRoadwayObstacle(const carma_perception_msgs::msg::ExternalObject& object) const
777 {
778 if (!semantic_map_ || semantic_map_->laneletLayer.size() == 0)
779 {
780 throw std::invalid_argument("Map is not set or does not contain lanelets");
781 }
782
783 lanelet::BasicPoint2d object_center(object.pose.pose.position.x, object.pose.pose.position.y);
784
785 auto nearestLaneletBoost = getIntersectingLanelet(object);
786
787 if (!nearestLaneletBoost)
788 return boost::none;
789
790 lanelet::Lanelet nearestLanelet = nearestLaneletBoost.get();
791
792 carma_perception_msgs::msg::RoadwayObstacle obs;
793 obs.object = object;
794 obs.connected_vehicle_type.type =
795 carma_perception_msgs::msg::ConnectedVehicleType::NOT_CONNECTED; // TODO No clear way to determine automation state at this time
796 obs.lanelet_id = nearestLanelet.id();
797
798 carma_wm::TrackPos obj_track_pos = geometry::trackPos(nearestLanelet, object_center);
799 obs.down_track = obj_track_pos.downtrack;
800 obs.cross_track = obj_track_pos.crosstrack;
801
802 for (auto prediction : object.predictions)
803 {
804 // Compute prediction polygon
805 lanelet::BasicPolygon2d prediction_polygon =
806 geometry::objectToMapPolygon(prediction.predicted_position, object.size);
807 lanelet::BasicPoint2d prediction_center(prediction.predicted_position.position.x,
808 prediction.predicted_position.position.y);
809
810 auto predNearestLanelet = semantic_map_->laneletLayer.nearest(prediction_center, 1)[0];
811
812 carma_wm::TrackPos pred_track_pos = geometry::trackPos(predNearestLanelet, prediction_center);
813
814 obs.predicted_lanelet_ids.emplace_back(predNearestLanelet.id());
815 obs.predicted_cross_tracks.emplace_back(pred_track_pos.crosstrack);
816 obs.predicted_down_tracks.emplace_back(pred_track_pos.downtrack);
817
818 // Since the predictions are having their lanelet ids matched based on the nearest nounding box search rather than
819 // checking for intersection The id confidence will be set to 90% of the position confidence
820 obs.predicted_lanelet_id_confidences.emplace_back(0.9 * prediction.predicted_position_confidence);
821 obs.predicted_cross_track_confidences.emplace_back(0.9 * prediction.predicted_position_confidence);
822 obs.predicted_down_track_confidences.emplace_back(0.9 * prediction.predicted_position_confidence);
823 }
824
825 return obs;
826 }
827
828 void CARMAWorldModel::setRoadwayObjects(const std::vector<carma_perception_msgs::msg::RoadwayObstacle>& rw_objs)
829 {
830 roadway_objects_ = rw_objs;
831 }
832
833 std::vector<carma_perception_msgs::msg::RoadwayObstacle> CARMAWorldModel::getRoadwayObjects() const
834 {
835 return roadway_objects_;
836 }
837
838 std::vector<carma_perception_msgs::msg::RoadwayObstacle> CARMAWorldModel::getInLaneObjects(const lanelet::ConstLanelet& lanelet,
839 const LaneSection& section) const
840 {
841 // Get all lanelets on current lane section
842 std::vector<lanelet::ConstLanelet> lane = getLane(lanelet, section);
843
844 // Check if any roadway object is registered
845 if (roadway_objects_.size() == 0)
846 {
847 return std::vector<carma_perception_msgs::msg::RoadwayObstacle>{};
848 }
849
850 // Initialize useful variables
851 std::vector<carma_perception_msgs::msg::RoadwayObstacle> lane_objects, roadway_objects_copy = roadway_objects_;
852
853 /*
854 * Get all in lane objects
855 * For each lane, we check if each object is on it by matching lanelet_id
856 * Complexity N*K, where N: num of lanelets, K: num of objects
857 */
858 int curr_idx;
859 std::queue<int> obj_idxs_queue;
860
861 // Create an index queue for roadway objects to quickly pop the idx if associated
862 // lanelet is found. This is to reduce number of objects to check as we check new lanelets
863 for (size_t i = 0; i < roadway_objects_copy.size(); i++)
864 {
865 obj_idxs_queue.push((int)i);
866 }
867
868 // check each lanelets
869 for (auto llt : lane)
870 {
871 int checked_queue_items = 0, to_check = obj_idxs_queue.size();
872 // check each objects
873 while (checked_queue_items < to_check)
874 {
875 curr_idx = obj_idxs_queue.front();
876 obj_idxs_queue.pop();
877 checked_queue_items++;
878
879 // Check if the object is in the lanelet
880 auto curr_obj = roadway_objects_copy[curr_idx];
881 if (curr_obj.lanelet_id == llt.id())
882 {
883 // found intersecting lanelet for this object
884 lane_objects.push_back(curr_obj);
885 }
886 // handle a case where an object might be lane-changing, so check adjacent ids
887 // a bit faster than checking intersection solely as && is left-to-right evaluation
888 else if (((map_routing_graph_->left(llt) && curr_obj.lanelet_id == map_routing_graph_->left(llt).get().id()) ||
889 (map_routing_graph_->right(llt) && curr_obj.lanelet_id == map_routing_graph_->right(llt).get().id())) &&
890 boost::geometry::intersects(
891 llt.polygon2d().basicPolygon(),
892 geometry::objectToMapPolygon(curr_obj.object.pose.pose, curr_obj.object.size)))
893 {
894 // found intersecting lanelet for this object
895 lane_objects.push_back(curr_obj);
896 }
897 else
898 {
899 // did not find suitable lanelet, so it will be processed again for next lanelet
900 obj_idxs_queue.push(curr_idx);
901 }
902 }
903 }
904
905 return lane_objects;
906 }
907
908 lanelet::Optional<lanelet::Lanelet>
909 CARMAWorldModel::getIntersectingLanelet(const carma_perception_msgs::msg::ExternalObject& object) const
910 {
911 // Check if the map is loaded yet
912 if (!semantic_map_ || semantic_map_->laneletLayer.size() == 0)
913 {
914 throw std::invalid_argument("Map is not set or does not contain lanelets");
915 }
916
917 lanelet::BasicPoint2d object_center(object.pose.pose.position.x, object.pose.pose.position.y);
918 lanelet::BasicPolygon2d object_polygon = geometry::objectToMapPolygon(object.pose.pose, object.size);
919
920 auto nearestLanelet = semantic_map_->laneletLayer.nearest(
921 object_center, 1)[0]; // Since the map contains lanelets there should always be at least 1 element
922
923 // Check if the object is inside or intersecting this lanelet
924 // If no intersection then the object can be considered off the road and does not need to processed
925 if (!boost::geometry::intersects(nearestLanelet.polygon2d().basicPolygon(), object_polygon))
926 {
927 return boost::none;
928 }
929 return nearestLanelet;
930 }
931
932 lanelet::Optional<double> CARMAWorldModel::distToNearestObjInLane(const lanelet::BasicPoint2d& object_center) const
933 {
934 // Check if the map is loaded yet
935 if (!semantic_map_ || semantic_map_->laneletLayer.size() == 0)
936 {
937 throw std::invalid_argument("Map is not set or does not contain lanelets");
938 }
939
940 // return empty if there is no object nearby
941 if (roadway_objects_.size() == 0)
942 return boost::none;
943
944 // Get the lanelet of this point
945 auto curr_lanelet = semantic_map_->laneletLayer.nearest(object_center, 1)[0];
946
947 // Check if this point at least is actually within this lanelet; otherwise, it wouldn't be "in-lane"
948 if (!boost::geometry::within(object_center, curr_lanelet.polygon2d().basicPolygon()))
949 throw std::invalid_argument("Given point is not within any lanelet");
950
951 std::vector<carma_perception_msgs::msg::RoadwayObstacle> lane_objects = getInLaneObjects(curr_lanelet);
952
953 // return empty if there is no object in the lane
954 if (lane_objects.size() == 0)
955 return boost::none;
956
957 // Record the closest distance out of all polygons, 4 points each
958 double min_dist = INFINITY;
959 for (auto obj : roadway_objects_)
960 {
961 lanelet::BasicPolygon2d object_polygon = geometry::objectToMapPolygon(obj.object.pose.pose, obj.object.size);
962
963 // Point to closest edge on polygon distance by boost library
964 double curr_dist = lanelet::geometry::distance(object_center, object_polygon);
965 if (min_dist > curr_dist)
966 min_dist = curr_dist;
967 }
968
969 // Return the closest distance out of all polygons
970 return min_dist;
971 }
972
973 lanelet::Optional<std::tuple<TrackPos, carma_perception_msgs::msg::RoadwayObstacle>>
974 CARMAWorldModel::getNearestObjInLane(const lanelet::BasicPoint2d& object_center, const LaneSection& section) const
975 {
976 // Check if the map is loaded yet
977 if (!semantic_map_ || semantic_map_->laneletLayer.size() == 0)
978 {
979 throw std::invalid_argument("Map is not set or does not contain lanelets");
980 }
981
982 // return empty if there is no object nearby
983 if (roadway_objects_.size() == 0)
984 return boost::none;
985
986 // Get the lanelet of this point
987 auto curr_lanelet = semantic_map_->laneletLayer.nearest(object_center, 1)[0];
988
989 // Check if this point at least is actually within this lanelet; otherwise, it wouldn't be "in-lane"
990 if (!boost::geometry::within(object_center, curr_lanelet.polygon2d().basicPolygon()))
991 throw std::invalid_argument("Given point is not within any lanelet");
992
993 // Get objects that are in the lane
994 std::vector<carma_perception_msgs::msg::RoadwayObstacle> lane_objects = getInLaneObjects(curr_lanelet, section);
995
996 // return empty if there is no object in the lane
997 if (lane_objects.size() == 0)
998 return boost::none;
999
1000 // Get the lane that is including this lanelet
1001 std::vector<lanelet::ConstLanelet> lane_section = getLane(curr_lanelet, section);
1002
1003 std::vector<double> object_downtracks, object_crosstracks;
1004 std::vector<int> object_idxs;
1005 std::queue<int> obj_idxs_queue;
1006 double base_downtrack = 0;
1007 double input_obj_downtrack = 0;
1008 int curr_idx = 0;
1009
1010 // Create an index queue for in lane objects to quickly pop the idx if associated
1011 // lanelet is found. This is to reduce number of objects to check as we check new lanelets
1012 for (size_t i = 0; i < lane_objects.size(); i++)
1013 {
1014 obj_idxs_queue.push((int)i);
1015 }
1016
1017 // For each lanelet, check if each object is inside it. if so, calculate downtrack
1018 for (auto llt : lane_section)
1019 {
1020 int checked_queue_items = 0, to_check = obj_idxs_queue.size();
1021
1022 // check each remaining objects
1023 while (checked_queue_items < to_check)
1024 {
1025 curr_idx = obj_idxs_queue.front();
1026 obj_idxs_queue.pop();
1027 checked_queue_items++;
1028
1029 // if the object is on it, store its total downtrack distance
1030 if (lane_objects[curr_idx].lanelet_id == llt.id())
1031 {
1032 object_downtracks.push_back(base_downtrack + lane_objects[curr_idx].down_track);
1033 object_crosstracks.push_back(lane_objects[curr_idx].cross_track);
1034 object_idxs.push_back(curr_idx);
1035 }
1036 // if it's not on it, try adjacent lanelets because the object could be lane changing
1037 else if ((map_routing_graph_->left(llt) &&
1038 lane_objects[curr_idx].lanelet_id == map_routing_graph_->left(llt).get().id()) ||
1039 (map_routing_graph_->right(llt) &&
1040 lane_objects[curr_idx].lanelet_id == map_routing_graph_->right(llt).get().id()))
1041 {
1042 // no need to check intersection as the objects are guaranteed to be intersecting this lane
1043 lanelet::BasicPoint2d obj_center(lane_objects[curr_idx].object.pose.pose.position.x,
1044 lane_objects[curr_idx].object.pose.pose.position.y);
1045 TrackPos new_tp = geometry::trackPos(llt, obj_center);
1046 object_downtracks.push_back(base_downtrack + new_tp.downtrack);
1047 object_crosstracks.push_back(lane_objects[curr_idx].cross_track);
1048 object_idxs.push_back(curr_idx);
1049 }
1050 else
1051 {
1052 // the object is not in the lanelet if above conditions do not meet
1053 obj_idxs_queue.push(curr_idx);
1054 }
1055 }
1056 // try to update object_center's downtrack
1057 if (curr_lanelet.id() == llt.id())
1058 input_obj_downtrack = base_downtrack + geometry::trackPos(llt, object_center).downtrack;
1059
1060 // this lanelet's entire centerline as downtrack
1061 base_downtrack +=
1062 geometry::trackPos(llt.centerline().back().basicPoint2d(), llt.centerline().front().basicPoint2d(),
1063 llt.centerline().back().basicPoint2d())
1064 .downtrack;
1065 }
1066
1067 // compare with input's downtrack and return the min_dist
1068 size_t min_idx = 0;
1069 double min_dist = INFINITY;
1070 for (size_t idx = 0; idx < object_downtracks.size(); idx++)
1071 {
1072 if (min_dist > std::fabs(object_downtracks[idx] - input_obj_downtrack))
1073 {
1074 min_dist = std::fabs(object_downtracks[idx] - input_obj_downtrack);
1075 min_idx = idx;
1076 }
1077 }
1078
1079 // if before the parallel line with the start of the llt that crosses given object_center, neg downtrack.
1080 // if left to the parallel line with the centerline of the llt that crosses given object_center, pos crosstrack
1081 return std::tuple<TrackPos, carma_perception_msgs::msg::RoadwayObstacle>(
1082 TrackPos(object_downtracks[min_idx] - input_obj_downtrack,
1083 object_crosstracks[min_idx] - geometry::trackPos(curr_lanelet, object_center).crosstrack),
1084 lane_objects[object_idxs[min_idx]]);
1085 }
1086
1087 lanelet::Optional<std::tuple<TrackPos, carma_perception_msgs::msg::RoadwayObstacle>>
1088 CARMAWorldModel::nearestObjectAheadInLane(const lanelet::BasicPoint2d& object_center) const
1089 {
1090 return getNearestObjInLane(object_center, LANE_AHEAD);
1091 }
1092
1093 lanelet::Optional<std::tuple<TrackPos, carma_perception_msgs::msg::RoadwayObstacle>>
1094 CARMAWorldModel::nearestObjectBehindInLane(const lanelet::BasicPoint2d& object_center) const
1095 {
1096 return getNearestObjInLane(object_center, LANE_BEHIND);
1097 }
1098 std::vector<lanelet::ConstLanelet> CARMAWorldModel::getLane(const lanelet::ConstLanelet& lanelet,
1099 const LaneSection& section) const
1100 {
1101 // Check if the map is loaded yet
1102 if (!semantic_map_ || semantic_map_->laneletLayer.size() == 0)
1103 {
1104 throw std::invalid_argument("Map is not set or does not contain lanelets");
1105 }
1106
1107 // Check if the lanelet is in map
1108 if (semantic_map_->laneletLayer.find(lanelet.id()) == semantic_map_->laneletLayer.end())
1109 {
1110 throw std::invalid_argument("Lanelet is not on the map");
1111 }
1112
1113 // Check if the lane section input is correct
1114 if (section != LANE_FULL && section != LANE_BEHIND && section != LANE_AHEAD)
1115 {
1116 throw std::invalid_argument("Undefined lane section is requested");
1117 }
1118
1119 std::vector<lanelet::ConstLanelet> following_lane = {lanelet};
1120 std::stack<lanelet::ConstLanelet> prev_lane_helper;
1121 std::vector<lanelet::ConstLanelet> prev_lane;
1122 std::vector<lanelet::ConstLanelet> connecting_lanelet = map_routing_graph_->following(lanelet, false);
1123
1124 // if only interested in following lanelets, as it is the most case
1125 while (connecting_lanelet.size() != 0)
1126 {
1127 following_lane.push_back(connecting_lanelet[0]);
1128 connecting_lanelet = map_routing_graph_->following(connecting_lanelet[0], false);
1129 }
1130 if (section == LANE_AHEAD)
1131 return following_lane;
1132
1133 // if interested in lanelets behind
1134 connecting_lanelet = map_routing_graph_->previous(lanelet, false);
1135 while (connecting_lanelet.size() != 0)
1136 {
1137 prev_lane_helper.push(connecting_lanelet[0]);
1138 connecting_lanelet = map_routing_graph_->previous(connecting_lanelet[0], false);
1139 }
1140
1141 // gather all lanelets with correct start order
1142 while (prev_lane_helper.size() != 0)
1143 {
1144 prev_lane.push_back(prev_lane_helper.top());
1145 prev_lane_helper.pop();
1146 }
1147
1148 // if only interested in lane behind
1149 if (section == LANE_BEHIND)
1150 {
1151 prev_lane.push_back(lanelet);
1152 return prev_lane;
1153 }
1154
1155 // if interested in full lane
1156 prev_lane.insert(prev_lane.end(), following_lane.begin(), following_lane.end());
1157 return prev_lane;
1158 }
1159
1160 void CARMAWorldModel::setTrafficLightIds(uint32_t id, lanelet::Id lanelet_id)
1161 {
1162 traffic_light_ids_[id] = lanelet_id;
1163 }
1164
1166 {
1167 config_speed_limit_ = config_lim;
1168 }
1169
1170 void CARMAWorldModel::setVehicleParticipationType(const std::string& participant)
1171 {
1172 participant_type_ = participant;
1173 }
1174
1176 {
1177 return participant_type_;
1178 }
1179
1180 std::vector<lanelet::Lanelet> CARMAWorldModel::getLaneletsFromPoint(const lanelet::BasicPoint2d& point, const unsigned int n)
1181 {
1183 }
1184
1185 std::vector<lanelet::ConstLanelet> CARMAWorldModel::getLaneletsFromPoint(const lanelet::BasicPoint2d& point, const unsigned int n) const
1186 {
1188 }
1189
1190 std::vector<lanelet::Lanelet> CARMAWorldModel::nonConnectedAdjacentLeft(const lanelet::BasicPoint2d& input_point, const unsigned int n)
1191 {
1193 }
1194
1195 std::vector<lanelet::ConstLanelet> CARMAWorldModel::nonConnectedAdjacentLeft(const lanelet::BasicPoint2d& input_point, const unsigned int n) const
1196 {
1197 return carma_wm::query::getLaneletsFromPoint(getMap(), input_point, n);
1198 }
1199
1200 std::vector<lanelet::CarmaTrafficSignalPtr> CARMAWorldModel::getSignalsAlongRoute(const lanelet::BasicPoint2d& loc) const
1201 {
1202 // Check if the map is loaded yet
1203 if (!semantic_map_ || semantic_map_->laneletLayer.empty())
1204 {
1205 RCLCPP_ERROR_STREAM(rclcpp::get_logger("carma_wm"), "Map is not set or does not contain lanelets");
1206 return {};
1207 }
1208 // Check if the route was loaded yet
1209 if (!route_)
1210 {
1211 RCLCPP_ERROR_STREAM(rclcpp::get_logger("carma_wm"), "Route has not yet been loaded");
1212 return {};
1213 }
1214 std::vector<lanelet::CarmaTrafficSignalPtr> light_list;
1215 auto curr_downtrack = routeTrackPos(loc).downtrack;
1216 // shortpath is already sorted by distance
1217
1218 for (const auto &ll : route_->shortestPath())
1219 {
1220 auto lights = semantic_map_->laneletLayer.get(ll.id()).regulatoryElementsAs<lanelet::CarmaTrafficSignal>();
1221 if (lights.empty())
1222 {
1223 continue;
1224 }
1225
1226 for (auto light : lights)
1227 {
1228 auto stop_line = light->getStopLine(ll);
1229 if (!stop_line)
1230 {
1231 RCLCPP_ERROR_STREAM(rclcpp::get_logger("carma_wm"), "No stop line");
1232 continue;
1233 }
1234 else
1235 {
1236 double light_downtrack = routeTrackPos(stop_line.get().front().basicPoint2d()).downtrack;
1237 double distance_remaining_to_traffic_light = light_downtrack - curr_downtrack;
1238
1239 if (distance_remaining_to_traffic_light < 0)
1240 {
1241 continue;
1242 }
1243 light_list.push_back(light);
1244 }
1245 }
1246 }
1247 return light_list;
1248 }
1249
1250 boost::optional<std::pair<lanelet::ConstLanelet, lanelet::ConstLanelet>> CARMAWorldModel::getEntryExitOfSignalAlongRoute(const lanelet::CarmaTrafficSignalPtr& traffic_signal) const
1251 {
1252 if (!traffic_signal)
1253 {
1254 throw std::invalid_argument("Empty traffic signal pointer has been passed!");
1255 }
1256
1257 std::pair<lanelet::ConstLanelet, lanelet::ConstLanelet> entry_exit;
1258 bool found_entry = false;
1259 bool found_exit = false;
1260 auto entry_lanelets = traffic_signal->getControlStartLanelets();
1261 auto exit_lanelets = traffic_signal->getControlEndLanelets();
1262
1263 // get entry and exit lane along route for the nearest given signal
1264 for (const auto& ll: route_->shortestPath())
1265 {
1266 if (!found_entry)
1267 {
1268 for (const auto& entry: entry_lanelets)
1269 {
1270 if (ll.id() == entry.id())
1271 {
1272 entry_exit.first = entry;
1273 found_entry = true;
1274 break;
1275 }
1276 }
1277 }
1278
1279 if (!found_exit)
1280 {
1281 for (const auto& exit: exit_lanelets)
1282 {
1283 if (ll.id() == exit.id())
1284 {
1285 entry_exit.second = exit;
1286 found_exit = true;
1287 break;
1288 }
1289 }
1290 }
1291
1292 if (found_entry && found_exit)
1293 return entry_exit;
1294 }
1295
1296 // was not able to find entry and exit for this signal along route
1297 return boost::none;
1298 }
1299
1300 std::vector<std::shared_ptr<lanelet::AllWayStop>> CARMAWorldModel::getIntersectionsAlongRoute(const lanelet::BasicPoint2d& loc) const
1301 {
1302 // Check if the map is loaded yet
1303 if (!semantic_map_ || semantic_map_->laneletLayer.empty())
1304 {
1305 RCLCPP_ERROR_STREAM(rclcpp::get_logger("carma_wm"), "Map is not set or does not contain lanelets");
1306 return {};
1307 }
1308 // Check if the route was loaded yet
1309 if (!route_)
1310 {
1311 RCLCPP_ERROR_STREAM(rclcpp::get_logger("carma_wm"), "Route has not yet been loaded");
1312 return {};
1313 }
1314 std::vector<std::shared_ptr<lanelet::AllWayStop>> intersection_list;
1315 auto curr_downtrack = routeTrackPos(loc).downtrack;
1316 // shortpath is already sorted by distance
1317 for(const auto& ll : route_->shortestPath())
1318 {
1319 auto intersections = semantic_map_->laneletLayer.get(ll.id()).regulatoryElementsAs<lanelet::AllWayStop>();
1320 if (intersections.empty())
1321 {
1322 continue;
1323 }
1324 for (auto intersection : intersections)
1325 {
1326 double intersection_downtrack = routeTrackPos(intersection->stopLines().front().front().basicPoint2d()).downtrack;
1327 if (intersection_downtrack < curr_downtrack)
1328 {
1329 continue;
1330 }
1331 intersection_list.push_back(intersection);
1332 }
1333 }
1334 return intersection_list;
1335 }
1336
1337 std::vector<lanelet::SignalizedIntersectionPtr> CARMAWorldModel::getSignalizedIntersectionsAlongRoute(const lanelet::BasicPoint2d &loc) const
1338 {
1339 // Check if the map is loaded yet
1340 if (!semantic_map_ || semantic_map_->laneletLayer.empty())
1341 {
1342 RCLCPP_ERROR_STREAM(rclcpp::get_logger("carma_wm"), "Map is not set or does not contain lanelets");
1343 return {};
1344 }
1345 // Check if the route was loaded yet
1346 if (!route_)
1347 {
1348 RCLCPP_ERROR_STREAM(rclcpp::get_logger("carma_wm"), "Route has not yet been loaded");
1349 return {};
1350 }
1351 std::vector<lanelet::SignalizedIntersectionPtr> intersection_list;
1352 auto curr_downtrack = routeTrackPos(loc).downtrack;
1353 // shortpath is already sorted by distance
1354 for (const auto &ll : route_->shortestPath())
1355 {
1356 auto intersections = semantic_map_->laneletLayer.get(ll.id()).regulatoryElementsAs<lanelet::SignalizedIntersection>();
1357 if (intersections.empty())
1358 {
1359 continue;
1360 }
1361 for (auto intersection : intersections)
1362 {
1363 double intersection_downtrack = routeTrackPos(ll.centerline().back().basicPoint2d()).downtrack;
1364 if (intersection_downtrack < curr_downtrack)
1365 {
1366 continue;
1367 }
1368 intersection_list.push_back(intersection);
1369 }
1370 }
1371 return intersection_list;
1372 }
1373
1374 std::optional<lanelet::ConstLanelet> CARMAWorldModel::getFirstLaneletOnShortestPath(const std::vector<lanelet::ConstLanelet>& lanelets_to_filter) const
1375 {
1376 if (!route_ || lanelets_to_filter.empty())
1377 {
1378 return std::nullopt;
1379 }
1380
1381 // pick a lanelet on the shortest path
1382 for (const auto& llt : lanelets_to_filter)
1383 {
1384 auto shortest_path = route_->shortestPath();
1385 if (std::find(shortest_path.begin(), shortest_path.end(), llt) != shortest_path.end())
1386 {
1387 return llt;
1388 }
1389 }
1390
1391 return std::nullopt;
1392 }
1393
1394} // namespace carma_wm
IndexedDistanceMap shortest_path_distance_map_
std::vector< carma_perception_msgs::msg::RoadwayObstacle > roadway_objects_
void setTrafficLightIds(uint32_t id, lanelet::Id lanelet_id)
Sets the id mapping between intersection/signal group and lanelet::Id for traffic lights in the map.
lanelet::Optional< double > distToNearestObjInLane(const lanelet::BasicPoint2d &object_center) const override
Gets Cartesian distance to the closest object on the same lane as the given point.
std::pair< TrackPos, TrackPos > routeTrackPos(const lanelet::ConstArea &area) const override
Returns a pair of TrackPos, computed in 2d, of the provided area relative to the current route....
std::vector< carma_perception_msgs::msg::RoadwayObstacle > getInLaneObjects(const lanelet::ConstLanelet &lanelet, const LaneSection &section=LANE_AHEAD) const override
Gets all roadway objects currently in the same lane as the given lanelet.
boost::optional< lanelet::BasicPoint2d > pointFromRouteTrackPos(const TrackPos &route_pos) const override
Converts a route track position into a map frame cartesian point.
lanelet::LineString3d copyConstructLineString(const lanelet::ConstLineString3d &line) const
Helper function to perform a deep copy of a LineString and assign new ids to all the elements....
boost::optional< std::pair< lanelet::ConstLanelet, lanelet::ConstLanelet > > getEntryExitOfSignalAlongRoute(const lanelet::CarmaTrafficSignalPtr &traffic_signal) const override
Returns the entry and exit lanelet of the signal along the SHORTEST PATH of route....
std::vector< lanelet::Lanelet > getLaneletsFromPoint(const lanelet::BasicPoint2d &point, const unsigned int n)
(non-const version) Gets the underlying lanelet, given the cartesian point on the map
lanelet::Optional< lanelet::Lanelet > getIntersectingLanelet(const carma_perception_msgs::msg::ExternalObject &object) const override
Gets the a lanelet the object is currently on determined by its position on the semantic map....
std::vector< lanelet::BusStopRulePtr > getBusStopsAlongRoute(const lanelet::BasicPoint2d &loc) const override
Return a list of bus stop along the current route. The bus stop along a route and the next bus stop a...
void setVehicleParticipationType(const std::string &participant)
Set vehicle participation type.
void setConfigSpeedLimit(double config_lim)
std::optional< lanelet::ConstLanelet > getFirstLaneletOnShortestPath(const std::vector< lanelet::ConstLanelet > &lanelets_to_filter) const
Given the vector of lanelets, returns the lanelet that's on the shortest path. Returns earliest lanel...
lanelet::Optional< std::tuple< TrackPos, carma_perception_msgs::msg::RoadwayObstacle > > getNearestObjInLane(const lanelet::BasicPoint2d &object_center, const LaneSection &section=LANE_AHEAD) const
This function is called by distanceToObjectBehindInLane or distanceToObjectAheadInLane....
lanelet::LaneletMapConstPtr getMap() const override
Get a pointer to the current map. If the underlying map has changed the pointer will also need to be ...
void setMap(lanelet::LaneletMapPtr map, size_t map_version=0, bool recompute_routing_graph=true)
Set the current map.
lanelet::LaneletSubmapConstUPtr shortest_path_view_
LaneletRouteConstPtr getRoute() const override
Get a pointer to the current route. If the underlying route has changed the pointer will also need to...
void setRoutingGraph(LaneletRoutingGraphPtr graph)
Set the routing graph for the participant type. This function may serve as an optimization to recompu...
lanelet::Optional< TrafficRulesConstPtr > getTrafficRules() const override
Get the Traffic Rules object.
void computeDowntrackReferenceLine()
Helper function to compute the geometry of the route downtrack/crosstrack reference line This functio...
LaneletRoutingGraphPtr map_routing_graph_
std::unordered_map< uint32_t, lanelet::Id > traffic_light_ids_
void setRouteName(const std::string &route_name)
Set the name of the route.
void setSimulationClock(const rclcpp::Time &time_now)
Set simulation clock clock (only used in simulation runs)
void setRouteEndPoint(const lanelet::BasicPoint3d &end_point)
Set endpoint of the route.
std::vector< lanelet::ConstLanelet > getLane(const lanelet::ConstLanelet &lanelet, const LaneSection &section=LANE_AHEAD) const override
Gets the specified lane section achievable without lane change, sorted from the start,...
std::string getVehicleParticipationType()
Get vehicle participation type.
std::vector< lanelet::ConstLanelet > getLaneletsBetween(double start, double end, bool shortest_path_only=false, bool bounds_inclusive=true) const override
Returns a list of lanelets which are part of the route and whose downtrack bounds exist within the pr...
void setRos1Clock(const rclcpp::Time &time_now)
Set current Ros1 clock (only used in simulation runs)
lanelet::Optional< std::tuple< TrackPos, carma_perception_msgs::msg::RoadwayObstacle > > nearestObjectBehindInLane(const lanelet::BasicPoint2d &object_center) const override
Gets Downtrack distance to AND copy of the closest object BEHIND on the same lane as the given point....
lanelet::LaneletMapPtr getMutableMap() const
Get a mutable version of the current map.
carma_wm::SignalizedIntersectionManager sim_
TrackPos getRouteEndTrackPos() const override
Get trackpos of the end of route point relative to the route.
std::vector< lanelet::Lanelet > nonConnectedAdjacentLeft(const lanelet::BasicPoint2d &input_point, const unsigned int n=10)
(non-const version) Given the cartesian point on the map, tries to get the opposite direction lanelet...
lanelet::LaneletMapUPtr shortest_path_filtered_centerline_view_
std::vector< std::shared_ptr< lanelet::AllWayStop > > getIntersectionsAlongRoute(const lanelet::BasicPoint2d &loc) const override
Return a list of all way stop intersections along the current route. The tall way stop intersections ...
std::shared_ptr< lanelet::LaneletMap > semantic_map_
void setRoute(LaneletRoutePtr route)
Set the current route. This route must match the current map for this class to function properly.
LaneletRoutingGraphConstPtr getMapRoutingGraph() const override
Get a pointer to the routing graph for the current map. If the underlying map has changed the pointer...
size_t getMapVersion() const override
Returns a monotonically increasing version number which represents the version stamp of the map geome...
void setRoadwayObjects(const std::vector< carma_perception_msgs::msg::RoadwayObstacle > &rw_objs)
Update internal records of roadway objects. These objects MUST be guaranteed to be on the road.
std::vector< lanelet::LineString3d > shortest_path_centerlines_
std::vector< lanelet::CarmaTrafficSignalPtr > getSignalsAlongRoute(const lanelet::BasicPoint2d &loc) const override
Return a list of traffic lights/intersections along the current route. The traffic lights along a rou...
std::vector< carma_perception_msgs::msg::RoadwayObstacle > getRoadwayObjects() const override
Get most recent roadway objects - all objects on the road detected by perception stack.
lanelet::Optional< carma_perception_msgs::msg::RoadwayObstacle > toRoadwayObstacle(const carma_perception_msgs::msg::ExternalObject &object) const override
Converts an ExternalObject in a RoadwayObstacle by mapping its position onto the semantic map....
std::vector< lanelet::BasicPoint2d > sampleRoutePoints(double start_downtrack, double end_downtrack, double step_size) const override
Samples the route centerline between the provided downtracks with the provided step size....
std::vector< lanelet::SignalizedIntersectionPtr > getSignalizedIntersectionsAlongRoute(const lanelet::BasicPoint2d &loc) const
Return a list of signalized intersections along the current route. The signalized intersections along...
lanelet::Optional< std::tuple< TrackPos, carma_perception_msgs::msg::RoadwayObstacle > > nearestObjectAheadInLane(const lanelet::BasicPoint2d &object_center) const override
Gets Downtrack distance to AND copy of the closest object AHEAD on the same lane as the given point....
std::string getRouteName() const override
Get the current route name.
O(1) distance lookup structure for quickly accessing route distance information. NOTE: This structure...
std::pair< size_t, size_t > getIndexFromId(const lanelet::Id &id) const
Returns the indexes of the element identified by the provided Id NOTE: It is up to the user to know i...
void pushBack(const lanelet::LineString2d &ls)
Add a linestring to this structure. This function will iterate over the line string to compute distan...
std::pair< size_t, size_t > getElementIndexByDistance(double distance, bool get_point=true) const
Returns index of the linestring which the provided distance is within. NOTE: Unlike the rest of this ...
double distanceToPointAlongElement(size_t index, size_t point_index) const
Get the along-line distance to the point on the provided linestring.
double distanceToElement(size_t index) const
Get the distance to the start of the linestring at the specified index.
size_t size() const
Returns number of linestrings in this structure.
double distanceBetween(size_t index, size_t p1_index, size_t p2_index) const
Get the distance between two points on the same linestring.
bool operator>(const LaneletDowntrackPair &pair) const
lanelet::ConstLanelet lanelet_
LaneletDowntrackPair(lanelet::ConstLanelet lanelet, double downtrack)
bool operator<(const LaneletDowntrackPair &pair) const
Position in a track based coordinate system where the axis are downtrack and crosstrack....
Definition: TrackPos.hpp:35
auto to_string(const UtmZone &zone) -> std::string
Definition: utm_zone.cpp:21
lanelet::BasicPolygon2d objectToMapPolygon(const geometry_msgs::msg::Pose &pose, const geometry_msgs::msg::Vector3 &size)
Uses the provided pose and size vector of an ExternalObject to compute what the polygon would be of t...
Definition: Geometry.cpp:289
double point_to_point_yaw(const lanelet::BasicPoint2d &cur_point, const lanelet::BasicPoint2d &next_point)
Computes the 2d orientation of the vector from the provided start point to end point.
Definition: Geometry.cpp:588
std::tuple< TrackPos, lanelet::BasicSegment2d > matchSegment(const lanelet::BasicPoint2d &p, const lanelet::BasicLineString2d &line_string)
Get the TrackPos of the provided point along the linestring and the segment of the line string which ...
Definition: Geometry.cpp:187
TrackPos trackPos(const lanelet::ConstLanelet &lanelet, const lanelet::BasicPoint2d &point)
Returns the TrackPos, computed in 2d, of the provided point relative to the centerline of the provide...
Definition: Geometry.cpp:120
std::vector< lanelet::ConstLanelet > getLaneletsFromPoint(const lanelet::LaneletMapConstPtr &semantic_map, const lanelet::BasicPoint2d &point, const unsigned int n=10)
carma_wm::query namespace contains implementations for query functions (input and output read or writ...
std::shared_ptr< lanelet::routing::RoutingGraph > LaneletRoutingGraphPtr
Definition: WorldModel.hpp:52
std::shared_ptr< const lanelet::traffic_rules::TrafficRules > TrafficRulesConstPtr
Definition: WorldModel.hpp:57
std::shared_ptr< const lanelet::routing::Route > LaneletRouteConstPtr
Definition: WorldModel.hpp:48
std::shared_ptr< const lanelet::routing::RoutingGraph > LaneletRoutingGraphConstPtr
Definition: WorldModel.hpp:53
std::shared_ptr< lanelet::routing::Route > LaneletRoutePtr
Definition: WorldModel.hpp:47