CARLA
test_opendrive.cpp
Go to the documentation of this file.
1 // Copyright (c) 2017 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 "test.h"
8 #include "OpenDrive.h"
9 #include "Random.h"
10 
11 #include <carla/StopWatch.h>
12 #include <carla/ThreadPool.h>
13 #include <carla/geom/Location.h>
14 #include <carla/geom/Math.h>
16 #include <carla/road/MapBuilder.h>
21 
22 #include <pugixml/pugixml.hpp>
23 
24 #include <fstream>
25 #include <string>
26 
27 using namespace carla::road;
28 using namespace carla::road::element;
29 using namespace carla::geom;
30 using namespace carla::opendrive;
31 using namespace util;
32 
33 const std::string BASE_PATH = LIBCARLA_TEST_CONTENT_FOLDER "/OpenDrive/";
34 
35 // Road Elevation
36 static void test_road_elevation(const pugi::xml_document &xml, boost::optional<Map>& map) {
37  pugi::xml_node open_drive_node = xml.child("OpenDRIVE");
38 
39  for (pugi::xml_node road_node : open_drive_node.children("road")) {
40  RoadId road_id = road_node.attribute("id").as_uint();
41  auto elevation_profile_nodes = road_node.children("elevationProfile");
42 
43  for (pugi::xml_node elevation_profile_node : elevation_profile_nodes) {
44  auto total_elevations = 0;
45  auto elevation_nodes = elevation_profile_node.children("elevation");
46  auto total_elevation_parser = std::distance(elevation_nodes.begin(), elevation_nodes.end());
47 
48  for (pugi::xml_node elevation_node : elevation_nodes) {
49  float s = elevation_node.attribute("s").as_float();
50  const auto elevation = map->GetMap().GetRoad(road_id).GetInfo<RoadInfoElevation>(s);
51  if (elevation != nullptr)
52  ++total_elevations;
53  }
54  ASSERT_EQ(total_elevations, total_elevation_parser);
55  }
56  }
57 }
58 
59 // Geometry
60 static void test_geometry(const pugi::xml_document &xml, boost::optional<Map>& map) {
61  pugi::xml_node open_drive_node = xml.child("OpenDRIVE");
62 
63  for (pugi::xml_node road_node : open_drive_node.children("road")) {
64  RoadId road_id = road_node.attribute("id").as_uint();
65 
66  for (pugi::xml_node plan_view_nodes : road_node.children("planView")) {
67  auto geometries_parser = plan_view_nodes.children("geometry");
68  auto total_geometries_parser = std::distance(geometries_parser.begin(), geometries_parser.end());
69  auto total_geometries = 0;
70  for (pugi::xml_node geometry_node : plan_view_nodes.children("geometry")){
71  float s = geometry_node.attribute("s").as_float();
72  auto geometry = map->GetMap().GetRoad(road_id).GetInfo<RoadInfoGeometry>(s);
73  if (geometry != nullptr)
74  ++total_geometries;
75  }
76  ASSERT_EQ(total_geometries, total_geometries_parser);
77  }
78  }
79 }
80 
81 // Road test
84  LaneSection& lane_section) {
85  constexpr auto error = 1e-5;
86  auto total_road_mark = 0;
87  auto total_road_mark_parser = 0;
88  for (pugi::xml_node lane_node : lane_nodes) {
89  // Check Road Mark
90  auto road_mark_nodes = lane_node.children("roadMark");
91  total_road_mark_parser += std::distance(road_mark_nodes.begin(), road_mark_nodes.end());
92 
93  const int lane_id = lane_node.attribute("id").as_int();
94  Lane* lane = nullptr;
95  lane = lane_section.GetLane(lane_id);
96  EXPECT_NE(lane, nullptr);
97 
98  // <roadMark sOffset="0.0000000000000000e+0" type="none" material="standard" color="white" laneChange="none"/>
99  for (pugi::xml_node road_mark_node : road_mark_nodes) {
100  const auto s_offset = road_mark_node.attribute("sOffset").as_double();
101  const auto type = road_mark_node.attribute("type").as_string();
102  const auto material = road_mark_node.attribute("material").as_string();
103  const auto color = road_mark_node.attribute("color").as_string();
104  const auto road_mark = lane->GetInfo<RoadInfoMarkRecord>(lane->GetDistance() + s_offset);
105  if (road_mark != nullptr) {
106  EXPECT_NEAR(lane->GetDistance() + s_offset, road_mark->GetDistance(), error);
107  EXPECT_EQ(type, road_mark->GetType());
108  EXPECT_EQ(material, road_mark->GetMaterial());
109  EXPECT_EQ(color, road_mark->GetColor());
110  ++total_road_mark;
111  }
112  }
113  }
114  return std::make_pair(total_road_mark, total_road_mark_parser);
115 }
116 
117 static void test_roads(const pugi::xml_document &xml, boost::optional<Map>& map) {
118  pugi::xml_node open_drive_node = xml.child("OpenDRIVE");
119 
120  // Check total Roads
121  auto roads_parser = open_drive_node.children("road");
122  auto total_roads_parser = std::distance(roads_parser.begin(), roads_parser.end());
123  auto total_roads = map->GetMap().GetRoads().size();
124  ASSERT_EQ(total_roads, total_roads_parser);
125 
126  for (pugi::xml_node road_node : roads_parser) {
127  RoadId road_id = road_node.attribute("id").as_uint();
128 
129  for (pugi::xml_node lanes_node : road_node.children("lanes")) {
130 
131  // Check total Lane Sections
132  auto lane_sections_parser = lanes_node.children("laneSection");
133  auto total_lane_sections_parser = std::distance(lane_sections_parser.begin(), lane_sections_parser.end());
134  auto total_lane_sections = map->GetMap().GetRoad(road_id).GetLaneSections().size();
135  ASSERT_EQ(total_lane_sections, total_lane_sections_parser);
136 
137  for (pugi::xml_node lane_section_node : lane_sections_parser) {
138 
139  // Check total Lanes
140  const double s = lane_section_node.attribute("s").as_double();
141  auto lane_section = map->GetMap().GetRoad(road_id).GetLaneSectionsAt(s);
142  size_t total_lanes = 0u;
143  for (auto it = lane_section.begin(); it != lane_section.end(); ++it) {
144  total_lanes = it->GetLanes().size();
145  }
146  auto left_nodes = lane_section_node.child("left").children("lane");
147  auto center_nodes = lane_section_node.child("center").children("lane");
148  auto right_nodes = lane_section_node.child("right").children("lane");
149  auto total_lanes_parser = std::distance(left_nodes.begin(), left_nodes.end());
150  total_lanes_parser += std::distance(right_nodes.begin(), right_nodes.end());
151  total_lanes_parser += std::distance(center_nodes.begin(), center_nodes.end());
152 
153  ASSERT_EQ(total_lanes, total_lanes_parser);
154 
155 
156  auto total_road_mark = 0;
157  auto total_road_mark_parser = 0;
158  for (auto it = lane_section.begin(); it != lane_section.end(); ++it) {
159  auto total_left = get_total_road_marks(left_nodes, *it);
160  auto total_center = get_total_road_marks(center_nodes, *it);
161  auto total_right = get_total_road_marks(right_nodes, *it);
162  total_road_mark = total_left.first + total_center.first + total_right.first;
163  total_road_mark_parser = total_left.first + total_center.first + total_right.first;
164  }
165  ASSERT_EQ(total_road_mark, total_road_mark_parser);
166  }
167  }
168  }
169 }
170 
171 // Junctions
172 static void test_junctions(const pugi::xml_document &xml, boost::optional<Map>& map) {
173  pugi::xml_node open_drive_node = xml.child("OpenDRIVE");
174 
175  // Check total number of junctions
176  auto& junctions = map->GetMap().GetJunctions();
177  auto total_junctions_parser = std::distance(open_drive_node.children("junction").begin(), open_drive_node.children("junction").end());
178 
179  ASSERT_EQ(junctions.size(), total_junctions_parser);
180 
181  for (pugi::xml_node junction_node : open_drive_node.children("junction")) {
182  // Check total number of connections
183  auto total_connections_parser = std::distance(junction_node.children("connection").begin(), junction_node.children("connection").end());
184 
185  JuncId junction_id = junction_node.attribute("id").as_int();
186  auto& junction = junctions.find(junction_id)->second;
187 
188  auto& connections = junction.GetConnections();
189 
190  ASSERT_EQ(connections.size(), total_connections_parser);
191 
192  for (pugi::xml_node connection_node : junction_node.children("connection")) {
193  auto total_lane_links_parser = std::distance(connection_node.children("laneLink").begin(), connection_node.children("laneLink").end());
194 
195  ConId connection_id = connection_node.attribute("id").as_uint();
196  auto& connection = connections.find(connection_id)->second;
197 
198  auto& lane_links = connection.lane_links;
199 
200  ASSERT_EQ(lane_links.size(), total_lane_links_parser);
201 
202  }
203  }
204 }
205 
206 static void test_road_links(boost::optional<Map>& map) {
207 
208  // process all roads, sections and lanes
209  for (auto &road : map->GetMap().GetRoads()) {
210  for (auto &section : road.second.GetLaneSections()) {
211  for (auto &lane : section.GetLanes()) {
212  // check all nexts
213  for (auto link : lane.second.GetNextLanes()) {
214  ASSERT_TRUE(link != nullptr);
215  }
216  // check all prevs
217  for (auto link : lane.second.GetPreviousLanes()) {
218  ASSERT_TRUE(link != nullptr);
219  }
220  }
221  }
222  }
223 }
224 
225 TEST(road, parse_files) {
226  for (const auto &file : util::OpenDrive::GetAvailableFiles()) {
227  // std::cerr << file << std::endl;
229  ASSERT_TRUE(map);
230  // print_roads(map, file);
231  }
232 }
233 
234 TEST(road, parse_road_links) {
235  for (const auto &file : util::OpenDrive::GetAvailableFiles()) {
236  // std::cerr << file << std::endl;
238  ASSERT_TRUE(map);
239  test_road_links(map);
240  }
241 }
242 
243 TEST(road, parse_junctions) {
244  for (const auto& file : util::OpenDrive::GetAvailableFiles()) {
246  ASSERT_TRUE(map.has_value());
247 
248  const std::string full_path = BASE_PATH + file;
249  pugi::xml_document xml;
250 
251  pugi::xml_parse_result result = xml.load_file( full_path.c_str());
252  ASSERT_TRUE(result);
253 
254  test_junctions(xml, map);
255  }
256 
257 }
258 
259 TEST(road, parse_road) {
260  for (const auto& file : util::OpenDrive::GetAvailableFiles()) {
262  ASSERT_TRUE(map.has_value());
263 
264  const std::string full_path = BASE_PATH + file;
265  pugi::xml_document xml;
266  pugi::xml_parse_result result = xml.load_file( full_path.c_str());
267  ASSERT_TRUE(result);
268 
269  test_roads(xml, map);
270  }
271 
272 }
273 
274 TEST(road, parse_road_elevation) {
275  for (const auto& file : util::OpenDrive::GetAvailableFiles()) {
277  ASSERT_TRUE(map.has_value());
278 
279  const std::string full_path = BASE_PATH + file;
280  pugi::xml_document xml;
281  pugi::xml_parse_result result = xml.load_file( full_path.c_str());
282  ASSERT_TRUE(result);
283 
284  test_road_elevation(xml, map);
285  }
286 
287 }
288 
289 TEST(road, parse_geometry) {
290  for (const auto& file : util::OpenDrive::GetAvailableFiles()) {
292  ASSERT_TRUE(map.has_value());
293 
294  const std::string full_path = BASE_PATH + file;
295  pugi::xml_document xml;
296  pugi::xml_parse_result result = xml.load_file( full_path.c_str());
297  ASSERT_TRUE(result);
298 
299  test_geometry(xml, map);
300  }
301 
302 }
303 
304 TEST(road, iterate_waypoints) {
305  carla::ThreadPool pool;
306  pool.AsyncRun();
307  std::vector<std::future<void>> results;
308  for (const auto& file : util::OpenDrive::GetAvailableFiles()) {
309  carla::logging::log("Parsing", file);
310  results.push_back(pool.Post([file]() {
311  carla::StopWatch stop_watch;
312  auto m = OpenDriveParser::Load(util::OpenDrive::Load(file));
313  ASSERT_TRUE(m.has_value());
314  auto &map = *m;
315  const auto topology = map.GenerateTopology();
316  ASSERT_FALSE(topology.empty());
317  auto count = 0u;
318  auto waypoints = map.GenerateWaypoints(0.5);
319  ASSERT_FALSE(waypoints.empty());
320  Random::Shuffle(waypoints);
321  const auto number_of_waypoints_to_explore =
322  std::min<size_t>(2000u, waypoints.size());
323  for (auto i = 0u; i < number_of_waypoints_to_explore; ++i) {
324  auto wp = waypoints[i];
325  map.ComputeTransform(wp);
326  if (i != 0u) {
327  ASSERT_NE(wp, waypoints[0u]);
328  }
329  for (auto &&successor : map.GetSuccessors(wp)) {
330  ASSERT_TRUE(
331  successor.road_id != wp.road_id ||
332  successor.section_id != wp.section_id ||
333  successor.lane_id != wp.lane_id ||
334  successor.s != wp.s);
335  }
336  auto origin = wp;
337  for (auto j = 0u; j < 200u; ++j) {
338  auto next_wps = map.GetNext(origin, Random::Uniform(0.0001, 150.0));
339  if (next_wps.empty()) {
340  break;
341  }
342  const auto number_of_next_wps_to_explore =
343  std::min<size_t>(10u, next_wps.size());
344  Random::Shuffle(next_wps);
345  for (auto k = 0u; k < number_of_next_wps_to_explore; ++k) {
346  auto next = next_wps[k];
347  ++count;
348  ASSERT_TRUE(
349  next.road_id != wp.road_id ||
350  next.section_id != wp.section_id ||
351  next.lane_id != wp.lane_id ||
352  next.s != wp.s);
353  auto right = map.GetRight(next);
354  if (right.has_value()) {
355  ASSERT_EQ(right->road_id, next.road_id);
356  ASSERT_EQ(right->section_id, next.section_id);
357  ASSERT_NE(right->lane_id, next.lane_id);
358  ASSERT_EQ(right->s, next.s);
359  }
360  auto left = map.GetLeft(next);
361  if (left.has_value()) {
362  ASSERT_EQ(left->road_id, next.road_id);
363  ASSERT_EQ(left->section_id, next.section_id);
364  ASSERT_NE(left->lane_id, next.lane_id);
365  ASSERT_EQ(left->s, next.s);
366  }
367  }
368  origin = next_wps[0u];
369  }
370  }
371  ASSERT_GT(count, 0u);
372  float seconds = 1e-3f * stop_watch.GetElapsedTime();
373  carla::logging::log(file, "done in", seconds, "seconds.");
374  }));
375  }
376  for (auto &result : results) {
377  result.get();
378  }
379 }
380 
381 TEST(road, get_waypoint) {
382  carla::ThreadPool pool;
383  pool.AsyncRun();
384  std::vector<std::future<void>> results;
385  for (const auto& file : util::OpenDrive::GetAvailableFiles()) {
386  carla::logging::log("Parsing", file);
387  results.push_back(pool.Post([file]() {
388  carla::StopWatch stop_watch;
389  auto m = OpenDriveParser::Load(util::OpenDrive::Load(file));
390  ASSERT_TRUE(m.has_value());
391  auto &map = *m;
392  for (auto i = 0u; i < 10'000u; ++i) {
393  const auto location = Random::Location(-500.0f, 500.0f);
394  auto owp = map.GetClosestWaypointOnRoad(location);
395  ASSERT_TRUE(owp.has_value());
396  auto &wp = *owp;
397  for (auto &next : map.GetNext(wp, 0.5)) {
398  ASSERT_TRUE(
399  next.road_id != wp.road_id ||
400  next.section_id != wp.section_id ||
401  next.lane_id != wp.lane_id ||
402  next.s != wp.s);
403  }
404  auto left = map.GetLeft(wp);
405  if (left.has_value()) {
406  ASSERT_EQ(left->road_id, wp.road_id);
407  ASSERT_EQ(left->section_id, wp.section_id);
408  ASSERT_NE(left->lane_id, wp.lane_id);
409  ASSERT_EQ(left->s, wp.s);
410  }
411  auto right = map.GetRight(wp);
412  if (right.has_value()) {
413  ASSERT_EQ(right->road_id, wp.road_id);
414  ASSERT_EQ(right->section_id, wp.section_id);
415  ASSERT_NE(right->lane_id, wp.lane_id);
416  ASSERT_EQ(right->s, wp.s);
417  }
418  }
419  float seconds = 1e-3f * stop_watch.GetElapsedTime();
420  carla::logging::log(file, "done in", seconds, "seconds.");
421  }));
422  }
423  for (auto &result : results) {
424  result.get();
425  }
426 }
static void test_road_links(boost::optional< Map > &map)
static std::string Load(const std::string &filename)
uint32_t RoadId
Definition: RoadTypes.h:15
unsigned int as_uint(unsigned int def=0) const
Definition: pugixml.cpp:5173
TEST(road, parse_files)
static void log(Args &&... args)
Definition: Logging.h:59
static std::vector< std::string > GetAvailableFiles()
xml_parse_result load_file(const char *path, unsigned int options=parse_default, xml_encoding encoding=encoding_auto)
Definition: pugixml.cpp:7108
Each lane within a road cross section can be provided with several road markentries.
std::future< ResultT > Post(FunctorT &&functor)
Post a task to the pool.
Definition: ThreadPool.h:41
double GetDistance() const
Definition: Lane.cpp:46
xml_attribute attribute(const char_t *name) const
Definition: pugixml.cpp:5500
int32_t JuncId
Definition: RoadTypes.h:17
static void test_roads(const pugi::xml_document &xml, boost::optional< Map > &map)
static auto get_total_road_marks(pugi::xml_object_range< pugi::xml_named_node_iterator > &lane_nodes, LaneSection &lane_section)
static EpisodeProxyPointerType::Shared Load(EpisodeProxyPointerType::Strong ptr)
const std::string BASE_PATH
int as_int(int def=0) const
Definition: pugixml.cpp:5168
xml_node child(const char_t *name) const
Definition: pugixml.cpp:5490
static void test_junctions(const pugi::xml_document &xml, boost::optional< Map > &map)
static void test_road_elevation(const pugi::xml_document &xml, boost::optional< Map > &map)
A thread pool based on Boost.Asio&#39;s io context.
Definition: ThreadPool.h:24
const T * GetInfo(const double s) const
Definition: Lane.h:79
uint32_t ConId
Definition: RoadTypes.h:27
void AsyncRun(size_t worker_threads)
Launch threads to run tasks asynchronously.
Definition: ThreadPool.h:51
Lane * GetLane(const LaneId id)
Definition: LaneSection.cpp:31
static void test_geometry(const pugi::xml_document &xml, boost::optional< Map > &map)
xml_object_range< xml_node_iterator > children() const
Definition: pugixml.cpp:5425