From d355b2e8a92c6d4696474efdb6141f5ea1ecbb5e Mon Sep 17 00:00:00 2001 From: taikitanaka3 <65527974+taikitanaka3@users.noreply.github.com> Date: Thu, 2 Dec 2021 11:26:55 +0900 Subject: [PATCH 01/36] feat: add map packages (#8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * release v0.4.0 * add resolution param in lanelet2_extension (#760) * Fix/extend drivable area beyond goal (#781) * update llt2 extention query func Signed-off-by: Kosuke Murakami * extend drivable area over goal point Signed-off-by: Kosuke Murakami * apply clang Signed-off-by: Kosuke Murakami * update get preeceeding func Signed-off-by: Kosuke Murakami * update preceeding func in lanechange Signed-off-by: Kosuke Murakami * update comment Signed-off-by: Kosuke Murakami * Fix intersection preceeding lane query (#807) * modified interseciton module to add lanelets in intersection to objective lanelets due to change in getPreceedingLaneletSequences() Signed-off-by: mitsudome-r * update comment Signed-off-by: mitsudome-r * Install executables in lanelet2_map_preprocessor (#834) Signed-off-by: mitsudome-r * remove ROS1 packages temporarily Signed-off-by: mitsudome-r * Revert "remove ROS1 packages temporarily" This reverts commit 3290a8b9e92c9eae05d9159c8b9fd56ca8935c01. Signed-off-by: mitsudome-r * add COLCON_IGNORE to ros1 packages Signed-off-by: mitsudome-r * Rename launch files to launch.xml (#28) * port map_tf_generator (#32) * port map_tf_generator Signed-off-by: Takamasa Horibe * add missing dependency Signed-off-by: Takamasa Horibe * fix pointor, tf_broadcaster, add compile option Signed-off-by: Takamasa Horibe * use ament_auto Signed-off-by: Takamasa Horibe * Port lanelet2 extension (#36) * remove COLCON_IGNORE Signed-off-by: mitsudome-r * port to ROS2 Signed-off-by: mitsudome-r * minor fix Signed-off-by: mitsudome-r * fix CI Signed-off-by: mitsudome-r * remove unnecessary semi-colon Signed-off-by: mitsudome-r * fix library to executable for lanelet2_extension_sample and autoware_lanelet2_validation Signed-off-by: mitsudome-r * fix usage for ROS2 Signed-off-by: mitsudome-r * fix usage message and parameter declaration Signed-off-by: mitsudome-r * fix getting map_file parameter Signed-off-by: mitsudome-r * Port map loader (#44) * port map_loader to ROS2 Signed-off-by: mitsudome-r * fix unintended change Signed-off-by: mitsudome-r * Update map/map_loader/CMakeLists.txt Co-authored-by: Takamasa Horibe Co-authored-by: Takamasa Horibe * Add geometry2 to repos (#76) * add geometry2 package temporarily until new release Signed-off-by: mitsudome-r * trigger-ci Signed-off-by: mitsudome-r * add tf2 dependency to the packages that use tf2_geometry_msgs Signed-off-by: mitsudome-r * Revert "Add geometry2 to repos (#76)" (#96) * Revert "Add geometry2 to repos (#76)" This reverts commit 7dbe25ed5ff7d5f413fda567dcc77a70c79a7826. * Re-add tf2 dependencies * Revert "Re-add tf2 dependencies" This reverts commit e23b0c8b0826cf9518924d33349f9de34b4975df. * Debug CI pipeline * Revert "Debug CI pipeline" This reverts commit 58f1eba550360d82c08230552abfb64b33b23e0f. * Explicitly install known versions of the geometry packages * No need to skip tf2 packages anymore Co-authored-by: Esteve Fernandez * Rename h files to hpp (#142) * Change includes * Rename files * Adjustments to make things compile * Other packages * Adjust copyright notice on 532 out of 699 source files (#143) * Use quotes for includes where appropriate (#144) * Use quotes for includes where appropriate * Fix lint tests * Make tests pass hopefully * Run uncrustify on the entire Pilot.Auto codebase (#151) * Run uncrustify on the entire Pilot.Auto codebase * Exclude open PRs * fixing trasient_local in ROS2 packages (#160) * added linters to lanelet1_extension (#170) * adding linters to map_loader (#171) * adding linters to map_tf_generator (#172) * apply env_var to use_sim_time (#222) * Ros2 v0.8.0 map loader and lanelet2 extension (#279) * Ros2 v0.8 fix typo of "preceding" (#323) * Fix typo of getPrecedingLaneletSequences * Fix comment * Fix rviz2 low FPS (#390) * add nullptr check when publish concatenate data (#369) * Add warning msg when concat pointcloud is not published (#388) Signed-off-by: Kenji Miyake * Change lineString2Marker Signed-off-by: Takagi, Isamu * Change trafficLight2TriangleMarker Signed-off-by: Takagi, Isamu * Change laneletDirectionAsMarker Signed-off-by: Takagi, Isamu * Remove debug code Signed-off-by: Takagi, Isamu * Fix linter problems Signed-off-by: Takagi, Isamu Co-authored-by: Taichi Higashide Co-authored-by: Kenji Miyake <31987104+kenji-miyake@users.noreply.github.com> * [map_loader] modify colors for lane markers for better visualization (#398) Signed-off-by: mitsudome-r * fix empty marker (#423) * Fix typo in map module (#437) * add license (#443) Signed-off-by: Kosuke Murakami * avoid pushing empty marker (#441) * avoid pushing empty marker * size0 -> empty * add use_sim-time option (#454) * Sync public repo (#1228) * [simple_planning_simulator] add readme (#424) * add readme of simple_planning_simulator Signed-off-by: Takamasa Horibe * Update simulator/simple_planning_simulator/README.md * set transit_margin_time to intersect. planner (#460) * Fix pose2twist (#462) Signed-off-by: Takagi, Isamu * Ros2 vehicle info param server (#447) * add vehicle_info_param_server * update vehicle info * apply format * fix bug * skip unnecessary search * delete vehicle param file * fix bug * Ros2 fix topic name part2 (#425) * Fix topic name of traffic_light_classifier Signed-off-by: Takagi, Isamu * Fix topic name of traffic_light_visualization Signed-off-by: Takagi, Isamu * Fix topic name of traffic_light_ssd_fine_detector Signed-off-by: Takagi, Isamu * Fix topic name of traffic_light_map_based_detector Signed-off-by: Takagi, Isamu * Fix lint traffic_light_recognition Signed-off-by: Takagi, Isamu * Fix lint traffic_light_ssd_fine_detector Signed-off-by: Takagi, Isamu * Fix lint traffic_light_classifier Signed-off-by: Takagi, Isamu * Fix lint traffic_light_classifier Signed-off-by: Takagi, Isamu * Fix lint traffic_light_ssd_fine_detector Signed-off-by: Takagi, Isamu * Fix issues in hdd_reader (#466) * Fix some issues detected by Coverity Scan and Clang-Tidy * Update launch command * Add more `close(new_sock)` * Simplify the definitions of struct * fix: re-construct laneletMapLayer for reindex RTree (#463) * Rviz overlay render fix (#461) * Moved painiting in SteeringAngle plugin to update() Signed-off-by: Adam Dabrowski * super class now back to MFD Signed-off-by: Adam Dabrowski * uncrustified Signed-off-by: Adam Dabrowski * acquire data in mutex Signed-off-by: Adam Dabrowski * back to RTD as superclass Signed-off-by: Adam Dabrowski * Rviz overlay render in update (#465) * Moved painiting in SteeringAngle plugin to update() Signed-off-by: Adam Dabrowski * super class now back to MFD Signed-off-by: Adam Dabrowski * uncrustified Signed-off-by: Adam Dabrowski * acquire data in mutex Signed-off-by: Adam Dabrowski * removed unnecessary includes and some dead code Signed-off-by: Adam Dabrowski * Adepted remaining vehicle plugin classes to render-in-update concept. Returned to MFD superclass Signed-off-by: Adam Dabrowski * restored RTD superclass Signed-off-by: Adam Dabrowski Co-authored-by: Takamasa Horibe Co-authored-by: tkimura4 Co-authored-by: Takagi, Isamu <43976882+isamu-takagi@users.noreply.github.com> Co-authored-by: Kazuki Miyahara Co-authored-by: Makoto Tokunaga Co-authored-by: Adam DÄ…browski * Revert "fix: re-construct laneletMapLayer for reindex RTree (#463)" (#1229) This reverts commit d2ecdfe4c58cb4544c9a3ee84947b36b7ee54421. * add pcd file check (#1232) * add pcd file check * add space * add & * use namespace * Unify Apache-2.0 license name (#1242) * Remove use_sim_time for set_parameter (#1260) Signed-off-by: wep21 * Map components (#1311) * Make pointcloud map loader component Signed-off-by: wep21 * Make lanelet2 map loader component Signed-off-by: wep21 * Make map tf generator component Signed-off-by: wep21 * Apply lint Signed-off-by: wep21 * Rename parameter for lanelet2 map path Signed-off-by: wep21 * Fix license Signed-off-by: wep21 * Add comment for filesystem Signed-off-by: wep21 * Fix variable name for glob Signed-off-by: wep21 * Fix dependency for query (#1519) * Fix a small bug (#1644) * Fix minor flaws detected by Clang-Tidy (#1647) - misc-throw-by-value-catch-by-reference - cppcoreguidelines-init-variables - readability-isolate-declaration * Add pre-commit (#1560) * add pre-commit * add pre-commit-config * add additional settings for private repository * use default pre-commit-config * update pre-commit setting * Ignore whitespace for line breaks in markdown * Update .github/workflows/pre-commit.yml Co-authored-by: Kazuki Miyahara * exclude svg * remove pretty-format-json * add double-quote-string-fixer * consider COLCON_IGNORE file when seaching modified package * format file * pre-commit fixes * Update pre-commit.yml * Update .pre-commit-config.yaml Co-authored-by: Kazuki Miyahara Co-authored-by: pre-commit Co-authored-by: Kenji Miyake <31987104+kenji-miyake@users.noreply.github.com> * Porting traffic light viz (#1284) * Feature/traffic light viz (#1001) * add tl map viz * bug fix * update map visualizer * add launch * add install in cmake * remove unused file * fix build error * Fix lint Signed-off-by: wep21 * Fix typo Signed-off-by: wep21 * Fix topic name and qos Signed-off-by: wep21 * Replace deprecated duration api Signed-off-by: wep21 Co-authored-by: Yukihiro Saito Co-authored-by: wep21 * Add markdownlint and prettier (#1661) * Add markdownlint and prettier Signed-off-by: Kenji Miyake * Ignore .param.yaml Signed-off-by: Kenji Miyake * Apply format Signed-off-by: Kenji Miyake * Feature/compare elevation map (#1488) * suppress warnings for declare parameters (#1724) * fix for lanelet2_extension * fix for traffic light ssd fine detector * fix for topic_state_monitor * fix for dummy diag publisher * fix for remote cmd converter * fix for vehicle_info_util * fix for multi object tracker * fix for freespace planner * fix for autoware_error_monitor * add Werror for multi object tracker * fix for multi object tracker * add Werror for liraffic light ssd fine detector * add Werror for topic state monitor * add Werror * add Werror * add Werror * add Werror * fix style * suppress warnings for map (#1773) * add compile option * fix error * add compile option * add maybe unused * fix sign-compare * delete unused * add parentheses * fix for uncrusify * Fix typo * use U * use U Co-authored-by: Kenji Miyake <31987104+kenji-miyake@users.noreply.github.com> * Fix clang warnings (#1859) * Fix -Wreturn-std-move Signed-off-by: Kenji Miyake * Fix -Wunused-private-field Signed-off-by: Kenji Miyake * Ignore -Wnonportable-include-path for mussp Signed-off-by: Kenji Miyake * Fix -Wunused-const-variable Signed-off-by: Kenji Miyake * Fix "can not be used when making a shared object" Signed-off-by: Kenji Miyake * Sync v1.3.0 (#1909) * Add elevation_map to autoware_state_monitor (#1907) Signed-off-by: kosuke55 * Disable saving elevation map temporarily (#1906) Signed-off-by: kosuke55 * Fix typos in README of map_loader (#1923) * Fix typos in README of map_loader * Apply Prettier * fix some typos (#1941) * fix some typos * fix typo * Fix typo Signed-off-by: Kenji Miyake Co-authored-by: Kenji Miyake * Add autoware api (#1979) * Invoke code formatter at pre-commit (#1935) * Run ament_uncrustify at pre-commit * Reformat existing files * Fix copyright and cpplint errors Signed-off-by: Kenji Miyake Co-authored-by: Kenji Miyake * Save elevation_map with pcd md5sum (#1988) * Save elevation_map with pcd md5sum Signed-off-by: kosuke55 * Update sample launch * Fix cpplint * Use hash-library instead of openssl * Use call by reference * Apply format * Set CMAKE_CXX_STANDARD 17 * Save input_pcd.json and shorten directory name when loading multiple pcd * Remove erasing last _ * Modify concatenating file path * Apply Format * Add hash_library_vendor to build_depends.repos * Modify include way * Change function and variable names * Use return * Remove unnecessary input variable * Use unique_ptr * Rename digestMd5 to digest_md5 * Modify variable name * Remove file.close() * Use hash of json * Read hash of json directory * Add newline to package.xml * Add isPcdFile * Fix pre-commit * Use icPcdFile when giving file of pcd * Feature/add virtual traffic light planner (#1588) * Fix deprecated constant of transient local (#1994) Signed-off-by: Takagi, Isamu * Fix lint errors in lanelet2_extension (#2028) Signed-off-by: Kenji Miyake * add sort-package-xml hook in pre-commit (#1881) * add sort xml hook in pre-commit * change retval to exit_status * rename * add prettier plugin-xml * use early return * add license note * add tier4 license * restore prettier * change license order * move local hooks to public repo * move prettier-xml to pre-commit-hooks-ros * update version for bug-fix * apply pre-commit * Revert "[map_loader] modify colors for lane markers for better visualization (#398)" (#2063) This reverts commit 046dc9a770bf03fb8813ddf6aa1b2f05e9357b67. * Fix elevation_map_loader downsample (#2055) * Add elevation_map data dir (#2093) * Minor fixes of map_loader's README (#2116) * Minor fixes of map_loader's README * Fix map_loader run command Co-authored-by: kosuke55 * Fix elevation_map hash due to mutiple slashes of pcd path (#2192) * Fix elevation_map hash due to mutiple slashes of pcd path * Use filesystem lexically_normal * Fix broken links of images on lanelet2_extension docs (#2206) * Add lanelet XML API (#2262) * show traffic light id marker (#1554) (#1678) * show traffic light id * fix typo Co-authored-by: satoshi-ota Co-authored-by: Satoshi OTA <44889564+satoshi-ota@users.noreply.github.com> Co-authored-by: satoshi-ota * Feature/porting behavior path planner (#1645) * Add behavior path planner pkg with Lane Change (#1525) * add lanelet extension funcs Signed-off-by: Takamasa Horibe * add planning msgs for FOA Signed-off-by: Takamasa Horibe * add behavior_path_planner pkg Signed-off-by: Takamasa Horibe * apply clang format Signed-off-by: Takamasa Horibe * add error handling for config load failure Signed-off-by: Takamasa Horibe * replace word: foa with remote control Signed-off-by: Takamasa Horibe * add readme Signed-off-by: Takamasa Horibe * use pointer for return value of path Signed-off-by: Takamasa Horibe * fix hz Signed-off-by: Takamasa Horibe * remove debug print Signed-off-by: Takamasa Horibe * remove shide-shift & avoidance related files Signed-off-by: Takamasa Horibe * Clip path by goal * add build depend for behavior tree cpp Signed-off-by: Takamasa Horibe * temporally disable lint test in lanelet2_extension Signed-off-by: Takamasa Horibe Co-authored-by: rej55 * Add avoidance module in behavior_path_planner (#1528) * Revert "remove shide-shift & avoidance related files" This reverts commit d819ea0291fca251012e4b9ffd16de3896830aa2. * refactor findNewShiftPoint func Signed-off-by: Takamasa Horibe * remove duplicated decleration Signed-off-by: Takamasa Horibe * fix barkward length issue - add clipPathLenght func in avoidance Signed-off-by: Takamasa Horibe * refactor: - translate english - minor modification for traffic distance Signed-off-by: Takamasa Horibe * support debug marker in behavior_path_planner Signed-off-by: Takamasa Horibe * clean up side shift module Signed-off-by: Takamasa Horibe * change topic name Signed-off-by: Takamasa Horibe * remove japanese Signed-off-by: Takamasa Horibe * Update planning/scenario_planning/lane_driving/behavior_planning/behavior_path_planner/include/behavior_path_planner/scene_module/side_shift/side_shift_module.hpp Co-authored-by: Yukihiro Saito * fix typo Signed-off-by: Takamasa Horibe * remove unused var Signed-off-by: Takamasa Horibe * adress reviewer comments: - add const for variables - add comment - fix typo Signed-off-by: Takamasa Horibe * fix typo Signed-off-by: Takamasa Horibe Co-authored-by: Yukihiro Saito * Replace behavior_path utilities with autoware_utils (#1532) * replace calcDistance Signed-off-by: Takamasa Horibe * replace arange Signed-off-by: Takamasa Horibe * replave convertToEigenPt with autoware_utils::fromMsg Signed-off-by: Takamasa Horibe * replace normalizeRadian Signed-off-by: Takamasa Horibe * cosmetic change Signed-off-by: Takamasa Horibe * import #1526 into behavior path planner (#1531) Signed-off-by: Takamasa Horibe * Fix/behavior path empty path output guard (#1536) * add guard Signed-off-by: Takamasa Horibe * Update planning/scenario_planning/lane_driving/behavior_planning/behavior_path_planner/src/behavior_path_planner.cpp * fix lateral jerk calculation (#1549) Signed-off-by: Takamasa Horibe * fix: error handling on exception in behavior_path_planner (#1551) Signed-off-by: Takamasa Horibe * Fix ignore too steep avoidance path (#1550) * ignore too steep path Signed-off-by: Takamasa Horibe * Update planning/scenario_planning/lane_driving/behavior_planning/behavior_path_planner/src/scene_module/avoidance/avoidance_module.cpp * parametrize lateral jerk limit Signed-off-by: Takamasa Horibe * Update planning/scenario_planning/lane_driving/behavior_planning/behavior_path_planner/include/behavior_path_planner/scene_module/avoidance/avoidance_module.hpp Co-authored-by: tkimura4 Co-authored-by: tkimura4 * use offsetNoThrow and add error log (#1615) Signed-off-by: Takamasa Horibe * Ignore object ahead goal for avoidance (#1618) * Ignore object ahead goal for avoidance * Add flag * Fix position of definition of goal_pose * Fix arclength calculation * Fix position of definition of goal_pose * fix intersection stop line (#1636) * fix intersection stop line * fix typo * add document (#1635) Signed-off-by: Takamasa Horibe * Port behavior path planner to ros2 Signed-off-by: wep21 * Apply lint Signed-off-by: wep21 * Fix typo Signed-off-by: wep21 * Fix map qos Signed-off-by: wep21 * debug slope calculation in behavior (#1566) * update * update * revert change of autoware_utils * define getPose in behavior_path_planner * update * update * update * update * interpolate z in obstacle_avoidance_planner * update velocity controller * fix detection area and scene * Update planning/scenario_planning/lane_driving/behavior_planning/behavior_path_planner/src/utilities.cpp Co-authored-by: tkimura4 * update comment in velocity controller * remove debug print * update Co-authored-by: tkimura4 * Address review: Fix config file name Signed-off-by: wep21 * pre-commit fixes Signed-off-by: wep21 * Fix redeclaring parameters Signed-off-by: wep21 * Add missing tf2 geometry function Signed-off-by: wep21 * Apply lint Signed-off-by: wep21 * Fix rclcpp Time initialization Signed-off-by: wep21 * Use now() instead of msg stamp Signed-off-by: wep21 * Use throttle output in getExpandedLanelet Signed-off-by: wep21 * Add missing const Signed-off-by: wep21 * Fix lint Signed-off-by: wep21 Co-authored-by: Takamasa Horibe Co-authored-by: rej55 Co-authored-by: Yukihiro Saito Co-authored-by: tkimura4 Co-authored-by: Takayuki Murooka * change type of traffic light marker (SPHERE_LIST->SPHERE) (#1789) * fix alpha (#1797) * Feature/improve intersection detection area (#1958) * exclude ego_lanes from detection_area * add empty handling * remove unused function * Fix for uncrustify Co-authored-by: Kenji Miyake <31987104+kenji-miyake@users.noreply.github.com> * Apply format (#1999) Signed-off-by: Kenji Miyake Fix cpplint Signed-off-by: Kenji Miyake * Feature/expand drivable area (#1812) * check if ego lane has adjacent lane or not * expand drivable area by using lanelet * remove unnecessary operator * use extra drivable area * fix variable names * fix indent * get polygon by id * fix variable name * remove redundant logic * update area name * disable expand by default Co-authored-by: satoshi-ota * add shoulder road lanelets (#2121) * add shoulder lanelets * Update map/lanelet2_extension/lib/query.cpp Co-authored-by: Daisuke Nishimatsu <42202095+wep21@users.noreply.github.com> * Update map/lanelet2_extension/lib/visualization.cpp Co-authored-by: Daisuke Nishimatsu <42202095+wep21@users.noreply.github.com> * Update map/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp Co-authored-by: Daisuke Nishimatsu <42202095+wep21@users.noreply.github.com> * Update map/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp Co-authored-by: Daisuke Nishimatsu <42202095+wep21@users.noreply.github.com> * Update map/lanelet2_extension/lib/visualization.cpp Co-authored-by: Daisuke Nishimatsu <42202095+wep21@users.noreply.github.com> Co-authored-by: Daisuke Nishimatsu <42202095+wep21@users.noreply.github.com> * Feature/no stopping area reg element (#2144) * add no stopping area to ll2 * add no stopping area visualization * add no stopping area marker to RVIZ * make no stopping area stop line as optional * Update map/map_loader/src/lanelet2_map_loader/lanelet2_map_visualization_node.cpp Co-authored-by: taikitanaka3 <65527974+taikitanaka3@users.noreply.github.com> Co-authored-by: tkimura4 * Add document for new map format (#1778) * add roadside lane doc * fix typo * fix typo * fix typo * fix typo * Add markdown lint * add reason for new subtype definition * fix typo Co-authored-by: kyoichi * Change formatter to clang-format and black (#2332) * Revert "Temporarily comment out pre-commit hooks" This reverts commit 748e9cdb145ce12f8b520bcbd97f5ff899fc28a3. * Replace ament_lint_common with autoware_lint_common Signed-off-by: Kenji Miyake * Remove ament_cmake_uncrustify and ament_clang_format Signed-off-by: Kenji Miyake * Apply Black Signed-off-by: Kenji Miyake * Apply clang-format Signed-off-by: Kenji Miyake * Fix build errors Signed-off-by: Kenji Miyake * Fix for cpplint * Fix include double quotes to angle brackets Signed-off-by: Kenji Miyake * Apply clang-format Signed-off-by: Kenji Miyake * Fix build errors Signed-off-by: Kenji Miyake * Add COLCON_IGNORE (#500) Signed-off-by: Kenji Miyake * port lanelet2_extension (#483) * port with auto_msgs * remove COLCON_IGNORE Co-authored-by: Takayuki Murooka * port map loader (#508) * remove COLCON_IGNORE in system_packages and map_tf_generator (#532) * add readme (#561) * fix old description Co-authored-by: mitsudome-r Co-authored-by: Taichi Higashide Co-authored-by: Kosuke Murakami Co-authored-by: Ryohsuke Mitsudome <43976834+mitsudome-r@users.noreply.github.com> Co-authored-by: Nikolai Morin Co-authored-by: Takamasa Horibe Co-authored-by: Esteve Fernandez Co-authored-by: nik-tier4 <71747268+nik-tier4@users.noreply.github.com> Co-authored-by: isamu-takagi <43976882+isamu-takagi@users.noreply.github.com> Co-authored-by: Kenji Miyake <31987104+kenji-miyake@users.noreply.github.com> Co-authored-by: Kazuki Miyahara Co-authored-by: tkimura4 Co-authored-by: Makoto Tokunaga Co-authored-by: Adam DÄ…browski Co-authored-by: Daisuke Nishimatsu <42202095+wep21@users.noreply.github.com> Co-authored-by: Keisuke Shima <19993104+KeisukeShima@users.noreply.github.com> Co-authored-by: pre-commit Co-authored-by: Yukihiro Saito Co-authored-by: wep21 Co-authored-by: Kosuke Takeuchi Co-authored-by: Hiroki OTA Co-authored-by: Kenji Miyake Co-authored-by: Takeshi Ishita Co-authored-by: Satoshi OTA <44889564+satoshi-ota@users.noreply.github.com> Co-authored-by: satoshi-ota Co-authored-by: rej55 Co-authored-by: Takayuki Murooka Co-authored-by: Sugatyon <32741405+Sugatyon@users.noreply.github.com> Co-authored-by: kyoichi Co-authored-by: Takayuki Murooka Co-authored-by: Takeshi Miura <57553950+1222-takeshi@users.noreply.github.com> --- tmp/lanelet2_extension/CMakeLists.txt | 90 ++ tmp/lanelet2_extension/README.md | 87 ++ .../docs/detection_area.png | Bin 0 -> 37318 bytes .../docs/extra_lanelet_subtypes.md | 45 + .../docs/extra_regulatory_elements.md | 63 + .../docs/lanelet2_format_extension.md | 186 +++ tmp/lanelet2_extension/docs/light_bulbs.png | Bin 0 -> 92100 bytes .../docs/pedestrian_lane.svg | 1 + tmp/lanelet2_extension/docs/road_mark.png | Bin 0 -> 22300 bytes tmp/lanelet2_extension/docs/road_shoulder.svg | 1 + tmp/lanelet2_extension/docs/traffic_light.png | Bin 0 -> 111053 bytes .../docs/traffic_light_regulatory_element.png | Bin 0 -> 116862 bytes .../docs/turn_direction.png | Bin 0 -> 67105 bytes .../io/autoware_osm_parser.hpp | 62 + .../projection/mgrs_projector.hpp | 115 ++ .../autoware_traffic_light.hpp | 96 ++ .../regulatory_elements/detection_area.hpp | 95 ++ .../regulatory_elements/no_stopping_area.hpp | 93 ++ .../regulatory_elements/road_marking.hpp | 71 + .../virtual_traffic_light.hpp | 78 ++ .../utility/message_conversion.hpp | 100 ++ .../lanelet2_extension/utility/query.hpp | 252 ++++ .../lanelet2_extension/utility/utilities.hpp | 84 ++ .../visualization/visualization.hpp | 253 ++++ .../lib/autoware_osm_parser.cpp | 88 ++ .../lib/autoware_traffic_light.cpp | 151 +++ tmp/lanelet2_extension/lib/detection_area.cpp | 157 +++ .../lib/message_conversion.cpp | 194 +++ tmp/lanelet2_extension/lib/mgrs_projector.cpp | 130 ++ .../lib/no_stopping_area.cpp | 157 +++ tmp/lanelet2_extension/lib/query.cpp | 847 ++++++++++++ tmp/lanelet2_extension/lib/road_marking.cpp | 80 ++ tmp/lanelet2_extension/lib/utilities.cpp | 599 +++++++++ .../lib/virtual_traffic_light.cpp | 68 + tmp/lanelet2_extension/lib/visualization.cpp | 1171 +++++++++++++++++ tmp/lanelet2_extension/package.xml | 36 + tmp/lanelet2_extension/src/sample_code.cpp | 120 ++ tmp/lanelet2_extension/src/validation.cpp | 174 +++ .../test/src/test_message_conversion.cpp | 158 +++ .../test/src/test_projector.cpp | 82 ++ .../test/src/test_query.cpp | 131 ++ .../test/src/test_regulatory_elements.cpp | 124 ++ .../test/src/test_utilities.cpp | 109 ++ 43 files changed, 6348 insertions(+) create mode 100644 tmp/lanelet2_extension/CMakeLists.txt create mode 100644 tmp/lanelet2_extension/README.md create mode 100644 tmp/lanelet2_extension/docs/detection_area.png create mode 100644 tmp/lanelet2_extension/docs/extra_lanelet_subtypes.md create mode 100644 tmp/lanelet2_extension/docs/extra_regulatory_elements.md create mode 100644 tmp/lanelet2_extension/docs/lanelet2_format_extension.md create mode 100644 tmp/lanelet2_extension/docs/light_bulbs.png create mode 100644 tmp/lanelet2_extension/docs/pedestrian_lane.svg create mode 100644 tmp/lanelet2_extension/docs/road_mark.png create mode 100644 tmp/lanelet2_extension/docs/road_shoulder.svg create mode 100644 tmp/lanelet2_extension/docs/traffic_light.png create mode 100644 tmp/lanelet2_extension/docs/traffic_light_regulatory_element.png create mode 100644 tmp/lanelet2_extension/docs/turn_direction.png create mode 100644 tmp/lanelet2_extension/include/lanelet2_extension/io/autoware_osm_parser.hpp create mode 100644 tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp create mode 100644 tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp create mode 100644 tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/detection_area.hpp create mode 100644 tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/no_stopping_area.hpp create mode 100644 tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/road_marking.hpp create mode 100644 tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/virtual_traffic_light.hpp create mode 100644 tmp/lanelet2_extension/include/lanelet2_extension/utility/message_conversion.hpp create mode 100644 tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp create mode 100644 tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp create mode 100644 tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp create mode 100644 tmp/lanelet2_extension/lib/autoware_osm_parser.cpp create mode 100644 tmp/lanelet2_extension/lib/autoware_traffic_light.cpp create mode 100644 tmp/lanelet2_extension/lib/detection_area.cpp create mode 100644 tmp/lanelet2_extension/lib/message_conversion.cpp create mode 100644 tmp/lanelet2_extension/lib/mgrs_projector.cpp create mode 100644 tmp/lanelet2_extension/lib/no_stopping_area.cpp create mode 100644 tmp/lanelet2_extension/lib/query.cpp create mode 100644 tmp/lanelet2_extension/lib/road_marking.cpp create mode 100644 tmp/lanelet2_extension/lib/utilities.cpp create mode 100644 tmp/lanelet2_extension/lib/virtual_traffic_light.cpp create mode 100644 tmp/lanelet2_extension/lib/visualization.cpp create mode 100644 tmp/lanelet2_extension/package.xml create mode 100644 tmp/lanelet2_extension/src/sample_code.cpp create mode 100644 tmp/lanelet2_extension/src/validation.cpp create mode 100644 tmp/lanelet2_extension/test/src/test_message_conversion.cpp create mode 100644 tmp/lanelet2_extension/test/src/test_projector.cpp create mode 100644 tmp/lanelet2_extension/test/src/test_query.cpp create mode 100644 tmp/lanelet2_extension/test/src/test_regulatory_elements.cpp create mode 100644 tmp/lanelet2_extension/test/src/test_utilities.cpp diff --git a/tmp/lanelet2_extension/CMakeLists.txt b/tmp/lanelet2_extension/CMakeLists.txt new file mode 100644 index 00000000..c755d45e --- /dev/null +++ b/tmp/lanelet2_extension/CMakeLists.txt @@ -0,0 +1,90 @@ +cmake_minimum_required(VERSION 3.5) +project(lanelet2_extension) + +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_CXX_EXTENSIONS OFF) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic -Werror) +endif() + +find_package(ament_cmake_auto REQUIRED) +ament_auto_find_build_dependencies() + +find_package(PkgConfig) +find_path(GeographicLib_INCLUDE_DIR GeographicLib/Config.h + PATH_SUFFIXES GeographicLib +) +set(GeographicLib_INCLUDE_DIRS ${GeographicLib_INCLUDE_DIR}) + +find_library(GeographicLib_LIBRARIES + NAMES Geographic +) + +find_library(PUGIXML_LIBRARIES + NAMES pugixml +) + +find_path(PUGIXML_INCLUDE_DIRS + NAMES pugixml.hpp + PATH_SUFFIXES pugixml +) + +include_directories( + ${GeographicLib_INCLUDE_DIRS} + ${PUGIXML_INCLUDE_DIRS} +) + +add_definitions(${GeographicLib_DEFINITIONS}) + +ament_auto_add_library(lanelet2_extension_lib SHARED + lib/autoware_osm_parser.cpp + lib/autoware_traffic_light.cpp + lib/detection_area.cpp + lib/no_stopping_area.cpp + lib/message_conversion.cpp + lib/mgrs_projector.cpp + lib/query.cpp + lib/road_marking.cpp + lib/utilities.cpp + lib/virtual_traffic_light.cpp + lib/visualization.cpp +) +target_link_libraries(lanelet2_extension_lib + ${GeographicLib_LIBRARIES} +) + +ament_auto_add_executable(lanelet2_extension_sample src/sample_code.cpp) +add_dependencies(lanelet2_extension_sample lanelet2_extension_lib) +target_link_libraries(lanelet2_extension_sample + lanelet2_extension_lib +) + +ament_auto_add_executable(autoware_lanelet2_validation src/validation.cpp) +add_dependencies(autoware_lanelet2_validation lanelet2_extension_lib) +target_link_libraries(autoware_lanelet2_validation + ${catkin_LIBRARIES} + ${PUGIXML_LIBRARIES} + lanelet2_extension_lib +) + +if(BUILD_TESTING) + find_package(ament_cmake_gtest REQUIRED) + ament_add_gtest(message_conversion-test test/src/test_message_conversion.cpp) + target_link_libraries(message_conversion-test lanelet2_extension_lib) + ament_add_gtest(projector-test test/src/test_projector.cpp) + target_link_libraries(projector-test lanelet2_extension_lib) + ament_add_gtest(query-test test/src/test_query.cpp) + target_link_libraries(query-test lanelet2_extension_lib) + ament_add_gtest(regulatory_elements-test test/src/test_regulatory_elements.cpp) + target_link_libraries(regulatory_elements-test lanelet2_extension_lib) + ament_add_gtest(utilities-test test/src/test_utilities.cpp) + target_link_libraries(utilities-test lanelet2_extension_lib) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() +endif() + +ament_auto_package() diff --git a/tmp/lanelet2_extension/README.md b/tmp/lanelet2_extension/README.md new file mode 100644 index 00000000..6da971b7 --- /dev/null +++ b/tmp/lanelet2_extension/README.md @@ -0,0 +1,87 @@ +# lanelet2_extension package + +This package contains external library for Lanelet2 and is meant to ease the use of Lanelet2 in Autoware. + +## Lanelet Format for Autoware + +Autoware uses extended Lanelet2 Format for Autoware, which means you need to add some tags to default OSM file if you want to fully use Lanelet2 maps. For details about custom tags, please refer to this [document](./docs/lanelet2_format_extension.md). + +## Code API + +### IO + +#### Autoware OSM Parser + +Autoware Lanelet2 Format uses .osm extension as original Lanelet2. +However, there are some custom tags that is used by the parser. + +Currently, this includes: + +- overwriting x,y values with `local_x` and `local_y` tags. +- reading `` tag which contains information about map format version and map version. + +The parser is registered as "autoware_osm_handler" as lanelet parser + +### Projection + +#### MGRS Projector + +MGRS projector projects latitude longitude into MGRS Coordinates. + +### Regulatory Elements + +#### Autoware Traffic Light + +Autoware Traffic Light class allows you to retrieve information about traffic lights. +Autoware Traffic Light class contains following members: + +- traffic light shape +- light bulbs information of traffic lights +- stopline associated to traffic light + +### Utility + +#### Message Conversion + +This contains functions to convert lanelet map objects into ROS messages. +Currently it contains following conversions: + +- lanelet::LaneletMapPtr to/from autoware_auto_mapping_msgs::msg::HADMapBin +- lanelet::Point3d to geometry_msgs::Point +- lanelet::Point2d to geometry_msgs::Point +- lanelet::BasicPoint3d to geometry_msgs::Point + +#### Query + +This module contains functions to retrieve various information from maps. +e.g. crosswalks, trafficlights, stoplines + +#### Utilities + +This module contains other useful functions related to Lanelet. +e.g. matching waypoint with lanelets + +### Visualization + +Visualization contains functions to convert lanelet objects into visualization marker messages. +Currently it contains following conversions: + +- lanelet::Lanelet to Triangle Markers +- lanelet::LineString to LineStrip Markers +- TrafficLights to Triangle Markers + +## Nodes + +### lanelet2_extension_sample + +Code for this explains how this lanelet2_extension library is used. +The executable is not meant to do anything. + +### autoware_lanelet2_extension + +This node checks if an .osm file follows the Autoware version of Lanelet2 format. +You can check by running: + +```sh +ros2 run lanelet2_extension autoware_lanelet2_validation _map_file:= +``` diff --git a/tmp/lanelet2_extension/docs/detection_area.png b/tmp/lanelet2_extension/docs/detection_area.png new file mode 100644 index 0000000000000000000000000000000000000000..0ae10872cb01f95427814425bf1c734ad6ca591d GIT binary patch literal 37318 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 Pi-4t>tts___q~4s?gfBE literal 0 HcmV?d00001 diff --git a/tmp/lanelet2_extension/docs/extra_lanelet_subtypes.md b/tmp/lanelet2_extension/docs/extra_lanelet_subtypes.md new file mode 100644 index 00000000..3632b07f --- /dev/null +++ b/tmp/lanelet2_extension/docs/extra_lanelet_subtypes.md @@ -0,0 +1,45 @@ +# Extra Lanelet Subtypes + +## Roadside Lane + +The subtypes for this lanelet classify the outer lanes adjacent to the driving lane.Since the list of lanelet subtypes defined in this [link](https://github.com/fzi-forschungszentrum-informatik/Lanelet2/blob/master/lanelet2_core/doc/LaneletAndAreaTagging.md) cannot represent the shoulder lane and pedestrian lane described below, two new subtypes are defined.When parking on the street, it is necessary to distinguish between a shoulder lane which can be used by vehicles, and a pedestrian lane which can be used by pedestrians and bicycles.If you park in a shoulder lane, you can use the entire lane for temporary parking, but if you park in a pedestrian lane, you must leave a space of at least 75cm. + +### Road shoulder subtype + +- refers: lanelet with subtype attribute. Subtype explains what the type of roadside it represents. If there is an area outside of this roadside lane that is open to traffic, such as a sidewalk or bike lane, select the road_shoulder subtype. + +![Road shoulder](road_shoulder.svg) + +Sample road shoulder in .osm format is shown below: + +```xml + + + + + + + + + +``` + +### Pedestrian lane subtype + +- refers: lanelet with subtype attribute. Subtype explains what the type of roadside it represents. If there are no passable areas outside of this roadside lane, select the pedestrian_lane subtype. + +![Pedestrian lane](pedestrian_lane.svg) + +Sample pedestrian lane in .osm format is shown below: + +```xml + + + + + + + + + +``` diff --git a/tmp/lanelet2_extension/docs/extra_regulatory_elements.md b/tmp/lanelet2_extension/docs/extra_regulatory_elements.md new file mode 100644 index 00000000..041ec036 --- /dev/null +++ b/tmp/lanelet2_extension/docs/extra_regulatory_elements.md @@ -0,0 +1,63 @@ +# Extra Regulatory Elements + +## Detection Area + +This regulatory element specifies region of interest which vehicle must pay attention whenever it is driving along the associated lanelet. When there are any obstacle in the detection area, vehicle must stop at specified stopline. + +- refers: refers to detection area polygon. There could be multiple detection areas registered to a single regulatory element. +- refline: refers to stop line of the detection area + +![Detection area](detection_area.png) + +Sample detection area in .osm format is shown below: + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +## Road Marking + +This regulatory element specifies related road markings to a lanelet as shown below. + +\* Note that the stopline in the image is for stoplines that are for reference, and normal stoplines should be expressed using TrafficSign regulatory element. + +refers: linestring with type attribute. Type explains what road marking it represents (e.g. stopline). + +![Road marking](road_mark.png) diff --git a/tmp/lanelet2_extension/docs/lanelet2_format_extension.md b/tmp/lanelet2_extension/docs/lanelet2_format_extension.md new file mode 100644 index 00000000..6a206d22 --- /dev/null +++ b/tmp/lanelet2_extension/docs/lanelet2_format_extension.md @@ -0,0 +1,186 @@ +# Modifying Lanelet2 format for Autoware + +## Overview + +About the basics of the default format, please refer to main [Lanelet2 repository](https://github.com/fzi-forschungszentrum-informatik/Lanelet2). (see [here](https://github.com/fzi-forschungszentrum-informatik/Lanelet2/blob/master/lanelet2_core/doc/LaneletPrimitives.md) about primitives) + +In addition to default Lanelet2 Format, users should add following mandatory/optional tags to their osm lanelet files as explained in reset of this document. +Users may use `autoware_lanelet2_validation` [node](../README.md#nodes) to check if their maps are valid. + +The following is the extra format added for Autoware: + +- [extra regulatory elements](extra_regulatory_elements.md) + - Detection Area + - Road Marking +- [extra lanelet subtype](extra_lanelet_subtypes.md) + - Roadside Lane + +## Mandatory Tags + +### Elevation Tags + +Elevation("ele") information for points(`node`) is optional in default Lanelet2 format. +However, some of Autoware packages(e.g. trafficlight_recognizer) need elevation to be included in HD map. Therefore, users must make sure that all points in their osm maps contain elevation tags. + +Here is an example osm syntax for node object. + +```xml + + + +``` + +### TrafficLights + +Default Lanelet2 format uses LineString(`way`) or Polygon class to represent the shape of a traffic light. For Autoware, traffic light objects must be represented only by LineString to avoid confusion, where start point is at bottom left edge and end point is at bottom right edge. Also, "height" tag must be added in order to represent the size in vertical direction (not the position). + +The Following image illustrates how LineString is used to represent shape of Traffic Light in Autoware. +![How LineString is used to represent shape of Traffic Light in Autoware](traffic_light.png) + +Here is an example osm syntax for traffic light object. + +```xml + + + + + + + +``` + +### Turn Directions + +Users must add "turn_direction" tags to lanelets within intersections to indicate vehicle's turning direction. You do not need this tags for lanelets that are not in intersections. If you do not have this tag, Autoware will not be able to light up turning indicators. +This tags only take following values: + +- left +- right +- straight + +Following image illustrates how lanelets should be tagged. + +![Turn Directions: How lanelets should be tagged](turn_direction.png) + +Here is an example of osm syntax for lanelets in intersections. + +```xml + + + + + + + + + + +``` + +## Optional Taggings + +Following tags are optional tags that you may want to add depending on how you want to use your map in Autoware. + +### Meta Info + +Users may add the `MetaInfo` element to their OSM file to indicate format version and map version of their OSM file. This information is not meant to influence Autoware vehicle's behavior, but is published as ROS message so that developers could know which map was used from ROSBAG log files. MetaInfo elements exists in the same hierarchy with `node`, `way`, and `relation` elements, otherwise JOSM wouldn't be able to load the file correctly. + +Here is an example of MetaInfo in osm file: + +```xml + + + + ... + ... + ... + +``` + +### Local Coordinate Expression + +Sometimes users might want to create Lanelet2 maps that are not georeferenced. +In such a case, users may use "local_x", "local_y" taggings to express local positions instead of latitude and longitude. +Autoware Osm Parser will overwrite x,y positions with these tags when they are present. +For z values, use "ele" tags as default Lanelet2 Format. +You would still need to fill in lat and lon attributes so that parser does not crush, but their values could be anything. + +Here is example `node` element in osm with "local_x", "local_y" taggings: + +```xml + + + + + + +``` + +### Light Bulbs in Traffic Lights + +Default Lanelet format can only express shape (base + height) of traffic lights. +However, region_tlr node in Autoware uses positions of each light bulbs to recognize color of traffic light. If users may wish to use this node, "light_bulbs"(`way`) element must be registered to traffic_light regulatory_element object define position and color of each light bulb in traffic lights. If you are using other trafficlight_recognizer nodes(e.g. tlr_mxnet), which only uses bounding box of traffic light, then you do not need to add this object. + +"light_bulbs" object is defined using LineString(`way`), and each node of line string is placed at the center of each light bulb. Also, each node should have "color" and optionally "arrow" tags to describe its type. Also, "traffic_light_id" tag is used to indicate which ID of relevant traffic_light element. + +"color" tag is used to express the color of the light bulb. Currently only three values are used: + +- red +- yellow +- green + +"arrow" tag is used to express the direction of the arrow of light bulb: + +- up +- right +- left +- up_right +- up_left + +Following image illustrates how "light_bulbs" LineString should be created. + +![How "light_bulbs" LineString should be created](light_bulbs.png) + +Here is an example of osm syntax for light_bulb object: + +```xml + + + + + + + + + + + + + + + + + + + + + + + + +``` + +After creating "light_bulbs" elements, you have to register them to traffic_light regulatory element as role "light_bulbs". +The following illustrates how light_bulbs are registered to traffic_light regulatory elements. + +![How light_bulbs are registered to traffic_light regulatory elements](traffic_light_regulatory_element.png) + +```xml + + + + + + + +``` diff --git a/tmp/lanelet2_extension/docs/light_bulbs.png b/tmp/lanelet2_extension/docs/light_bulbs.png new file mode 100644 index 0000000000000000000000000000000000000000..dc1e6e14d7b389490cfa33e3422fb5d5e480d89c GIT binary patch literal 92100 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 Tn6~Om&Memk>pdz{w|n&;gj*92 literal 0 HcmV?d00001 diff --git a/tmp/lanelet2_extension/docs/pedestrian_lane.svg b/tmp/lanelet2_extension/docs/pedestrian_lane.svg new file mode 100644 index 00000000..526506ad --- /dev/null +++ b/tmp/lanelet2_extension/docs/pedestrian_lane.svg @@ -0,0 +1 @@ +
type: lanelet
subtype: pedestrian_lane
type: lanelet subtype: p...
Unaccessible area
(e.g. fences, plants, trees, etc.)
Unaccessible area...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/tmp/lanelet2_extension/docs/road_mark.png b/tmp/lanelet2_extension/docs/road_mark.png new file mode 100644 index 0000000000000000000000000000000000000000..98b41025d5d2b4513c57e7878f68e76962de3372 GIT binary patch literal 22300 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 literal 0 HcmV?d00001 diff --git a/tmp/lanelet2_extension/docs/road_shoulder.svg b/tmp/lanelet2_extension/docs/road_shoulder.svg new file mode 100644 index 00000000..40231247 --- /dev/null +++ b/tmp/lanelet2_extension/docs/road_shoulder.svg @@ -0,0 +1 @@ +
type: lanelet
subtype: road_shoulder
type: lanelet subtype: r...
Accessible area
(e.g. sidewalks, bike lanes, etc.)
Accessible area...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/tmp/lanelet2_extension/docs/traffic_light.png b/tmp/lanelet2_extension/docs/traffic_light.png new file mode 100644 index 0000000000000000000000000000000000000000..aa97c7bc61c7bd0145ec6af1170b29ee9bdfe1da GIT binary patch literal 111053 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 z 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 z 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

(io zAy(ouV+f^lWi8yn)C$yWOWxV^`c8?6!7f7AJfnlDn$XQ3G|ls$eijF#!R^ft-a;w< z(P~uM@7p=)&bgwQi*mlaD^-1qli=%PF5e5f*W(=%x_b09xTFK?s zxT#N^k>sK#|1$Y2GA9w~OCRM6XzrDqZeA=`7oYiM!Relx_`65uwE0;|7dt}N6}t;s zV!a4Dh1RqfD*gkWba>X8@MKzF5hIJuZH3gCFce!RmGRn*71NmNCwSAdknn?zD@8~X ze`g;zS5@o;QH?1uxnM#048%g5HH|lkHo=J}z}a2BqIZd!UEg^hkNt8J(A+H}Ga0lV zNs9*~h@MD(R{|pXx;q0=gDlriv%wgIJ~4Uypq_Z|=%@{WxV?OBG7ZniW*v&{ z6&>b0Hhs$ujRXTwJRF=_fue1-5FR3qo-aNvK64$Xlk>Ebw%OKB#o=|wr`&O#>+Bf` zm#_!#`Riu_?s^J6et4I|xpreHF5QV)mW+I(|5!PTcFBgC?&k25-bpHTp6U%0c z+|&B-87#kFpz*VLQ$(}R%cWbyW!9m2`Q^h!RH^<*rR>1Ojl;npf7-nQl31!e)xy0( z?6V1RwTTp(l%oRLf7fCK>)6@H%Hu!{+w42MvC)v54r=DE7I=3B+dNMDEXtTX>ohGT zvK-NB1wx3Zp#kYM+(W)uBYKne2CNPLw%OzNiZJs2SRLoKH9HRI1%Je}sC3U>qKPEC z@8)SnBh_P`u1LYb!Y(XT!g7gEUw7s0)g8v8J}$1N1J`-UaQCs#xWI5vkCl}7#ec~Y z#FRSB&jUQ3-}^1<9A!tv#?%}}7V4E4E?I)nznx7E) zjU^A7LC;?}SCKu2%5cnzj&pWnZ1^gDG-Q78QuSSw&AuB)yc=fxyJq}B?ioU8*JI<& zeWqY3M<|Qh7+%cya!rwasZ%%ecj!%;dWJ*UrVcw!nA9)>tMd^bA(o+#S=FGjNL^!U zrCf%0oZ?raQ;TvPuMD60NWO(TcCj0<=baQd&RUPC-fTZP+Z;jl@3D%i%c2WXnO*LhxkTK0DonzZ zou1v56OCi*oOsRpox00!k&lI{h%Q?nQ`;G1EKKY*T z6})U6cu|@Xq283{(vZfbz0@LXy6mo%`0Z4z7Jii=?Vyb{TS$91K#wNXny#5T6ptx| zQMM>2ZVzXYDA3>V2ULSc+8f7>VY4h8kwbcW$KZT}o`ZgO_L@3gqt{UYBvmk6XvU(j z$O6T6q1l0R5rGtsL=^rzip|Cmy=&f4rOQbtLq2Ui%@6qzTECA3 z+j}nmj8HzHpUqlgi9e^6wx3X(U?*=mwd93;aGg$FdO5_rMbv-=E9$@0B2O}UcgGMW zV_5!qQzqzLL@W7ZN$>5k8wd6K=d&DoO?=*`#qst;k?9>LRWo5J**ycue$(zZrr(Kx z+c-w;w)!mo+>T7^$Sw54|F}r14{kR;QruW|4GT2!5&d}WZ13Gf2$I_ZB4&UcA}5&h zmGwt>5?G*qbujanVtrugY@O0Cl8cn(o0^yl$;U(YWA&9Dfb2NtRDGetl7I!Gp?`Zf zZJ~L0ZVMrv%em_PkeAzqDjcjQQ%Gy@c*_X-a<#?3xAbrqy@Z@A9>kB1j->VC;zb+d%Y#*U*1py|VdUHPTRV!_8Wm6H?b_ZEu<0udyb zubK2-tb~6wo?4TD5V%`!32Q5N8THRH@o2v`Ss;j}KBl@Br}H!ORgv|`{`X!^j7*~A zM`#T#k(^Wb{j<~SS*?zuZ~_GSY+0=o|5Te@e{n+g#vWg!(LajKvV~{JJ%zjV4nn=t zZ^YJ3MvZ)1Q+P3mc}M8Qk)&Qt+Iw+l&n{d%Z%F(b%D76>Pi+tF{hXQ6KAFT`9pMci z@KBV$sNW1%+rajE!g;rZ=iDpi^uKp9`&riE+BK8uz_k4K89vziPvo|+aInr_78oqx zrZ>)KtPqx6TRS|S9sVUCAV90hGfdze6cq~#S`@Q^wcEEyhAlJJWwg3}-Vdl9^R%Zj}H6#b;ylQTwnPxP4u=sj$l7UT5pVey#PMJ>2h zp4dztn`oD`^(vP~h66stQMzMP@dtdDx1ZgOII)u;Vua9YlMJlR2 z4XRrGQ1$=(_oQJNTpi;i;igG=S6VHu<9ZhgjjT8}HYb`HoQigMi{-Y`tA9c!ce(Oy zy2m`NVh8%A0_H6{)N?7iS5s|DMj>n)ui%p1R$0jDeB=G!id$$O5dnurE)&EL1H^N? z9C{&hi0?=ybtQYj#tK&D&#n~Rx$nla340Y>GQw7j+?LSV=I;eQ^@W-?y5fG14e>Ta zLZwwh!4g0r|DxkcL59BP5~e*`o-KyH&km}5%Y2ErsmuJDN^s18xGCq`7hgtj z^4I4k4aNTg5w-D#dNt+~&=ps;KFhh$8=2rq%7v5B%-Ou}!V(T9a?k{B>B73&`2Ob; zUrep}?+skpls|6W?L1M>_5H`xKAkgd@^e|kl-;Kkf9akuf_i;1%RyvNY9c(nxGK#L zB5opIShNqeGe7%jsOr;Q`saF&E8v3Ru1VRMm5XEk*1IN>|9+SH{sNo2OrS4h(qugI zkR#UQj0IY)TM0Wb6<%;t;#M0pz&unAKNbhrjL5fSbPcgNc{X8ZB+MB}^hw zFBN#uJJgR{+(k|Nf)s4Nea1a&TY19F$#(Y2>xcU=zLjz$^o7AUEEgV2E@otQc6J?$ zi_MEKc|1n9EXlHwNWk@{-6;Q9xiDA!Oi!|eiTv>mSJX(FtezM>XatY_%w?s)6YS2M z4Z-JRLx)$3{uu*3aA#09j36qkHw8ppQnWi9<-p~>QDgO)^YtRia5m3h-@?f+=9z5- zh#v*e^A|V82Yi^rtg2Q14D1bK-8`vQCD(9Q&s`s3rSL>jb8qo+gjqSLsc5W5RVL`4 zzueH(4XaDp8NG;rTicc<czCt4-zkUM_lOYp5wEkdv4M%~)bOypKj75;=NA z6WU@p1xNHorg)`--wNj+c)O(mnM8DO=I-&SP=knJRVv>4>u&L#A@(rb?IT&lHh!EH z^7rj!g7%BiKJ_!7kNA0nbHA;{;)affSPEF-TqG=G)vLZ)v2t!!KOqzqJ>g1nzBw*J zeVUohd(nr#k2Gi4l&dnk^7!NGO5)ZR(LhP})!dxVTAJ|w(kk9sr$4gd@XO`C#v{Qa z=?9pof81MNa_>;N4p?a7;H~aiN(<22%d75D57~;ockfP(rKBMl@Ys#31EP>Ia-7D0 zosW}Iz}!QAE{UltAkJPFmZ{hUFi=VO9`a5$m7fxlL0)bDThp^-j%pRmk1l;5H;F?P z45_S-{ZLlq%lKryU#?m;}7+k`Rvr&@M z-s~|3vkRXAdf## zeNJJlzutSC3)%d^=1m7}WYVzY4(;3hvyCwTzNIUUocdi$QgTqG+{)@WX-tQL z(!~4gZSk@MSgtsJI6C<(^Tx9V1&qxPPBS=oznzrP1lnfXA)w^ulhde+dVZSPrLx7X zdE~%Qbr@t(*Z;s03o;SLu$~>xR^73sX$~$hwoT=VTYJd0oyIZa&QMUPMjCCPS}L!I z9tQzGs0pgd#jMHc@;aC8JdWzD-6Sv|yf+X_lM?jJ5gI&`9SO$`;9(+ak8Vp?TDr`@ z3+1l80U^} z!k)E^q01}y@^le=vB!$fiqVzdQe5hCb!&+P`{}~po?Br7X|g*yTGKt{QiPmeV0BV% za0&2MZ6&Bdt*sZ3+ke+>-*^j5OCfPu?{zqzMz~cTF->-@oSv~P6;KQAw!Weov0nkn z0;tFK8&m2;>Fl_~$o1aCwEj5U8UpHF9)fd|E8PnVWIV8hPN5E%~v$j&3G%! zwc$Sb8cUOTs@QLy-Pu%`->AgDv?HIM!J<6r?k2xXP5Vn(TvG@V^bgSwykDskp8s8h2ru>GMG+{UZMvz(bd$dOQe|*Z(>lZ-dMmmwF{&;iv^C zLB0!8J^U)2Twk=rEHrO4TlsisVamc-*>_WZ7S%hG$pydd>@Rwm7BXnzv{bOjw+ry27WS*9xJ2zJKu;{L{~r<)KeX&B*wh z(G_DajOwY&{Mu%*vv4$Ut<{I#8_(9`(Fv<>O8I*6Zp#y*<8bna*XT4{9)9G5V{vEW z%9-4er#&LOG{!hSCks0{9B2ObVwc&oo*i(WAU7QN8)ryEwtoM!hBKeSLZEEQa@>#m zd-o0EVDZUliSd)!)4M7-&(fE?tVV&%d>pEm9+niZ&u87w(6`XvD{Yav8Ow4n!w@~6 z7x)ric!(&<-(O9-O8eACr&-dY>n=-8AXq@hYNs#BF5GbB+8`Ni+|M@pxUpoUXp9_t z_?7DNjiQ!0h3K`L3F9V>T}-CRV;HCk;o}CFByEbV6P5?WVY0>JkSODkM7;S=AnJu4KWa=e43ua@GFvV^D+{tF$K`u>oJaNTwS!de6k2rP6N=el>4;3DD zbqh&&0haI6$5&aRq==o)dEs6FP}1|t4XX47{oKYAZC{p*O4 z$vGJCigTo1zT!*NJ;w%Pi!?W^WQ0=R4icqc(svxLk{h`_cz(Q)ya%jNea4k9Tv^jO zZO_X2IIY)Ci7ytI8DNF33=$>2rTDHQ6PL;8bSd;RSM#T{RCaW)hYE-K{{HY(qZb!@ z<3W5?+}~dN;ydkp75r}40}W#MZk@G>^fFC*%ouH<0oyW4V;ej|{X7QR^l)oC#ACy_ zJWEeCzA;#>5VoR04$N~~q%5zrAAu)seyD9pd1Y{@nmkIs6VmlLh78h^5A^o>;sf(} zueW%HwT5w@de}W<4GuMTKJ0s%xhSRb5$A}>MFTbkK{i60sBsVhRVmq1^i|Z|xCS=X z68_+K4+GpXCkUZc*O>Ynf{hXbBVeAIdq4Q&d|W`sPl3%akiK=QfG%4L$+>tu8j{Z` zS0FOY#l-V31!AeM?9>gLJ-JA5G8NTtm@PR%xacDV>z`Hu;~(IQFSYl{w2ZwII6n{ z!=9dNT(yYUAX_`N3tN2*W<5^t0dALjGJxgm;WI8oI?-uE0f?2&xEk8G$hlPDU}#d# zLUY9Vxs%*}@qwN{9w$KBNKOm-!7V06bAWTZ_Qw!b72E(dzgJ|Ci74vcqS0A9z_Gt$ zWj1fvoc}h?+x{YO9}1U(kH?JrQ3+vBp`H_5>4KJ5`U3`PjW|F7TMALml|^rsvKdBN zjvNO1ZD?nG7#zWHIPR@k-PqD}?E=upJNiJKGo3RI3Pp|c$^~aAe-ueTWm9qubiq`? z@Lt#WNmT0V-@yHsZ=>jsZDnxDiCa^L_G_fGdY485XsXv@XwSMr-PMP{#>+l>h9h9U zQm-!G0zgs~3Ns*-y+f%rLa3ylc9TtWiPh;=mK60hDKoSbMCshPl$4mcwG4b5w1NF3 z&z|7PkcubbcI>cYkvK{BbP}8iNo+pLBDqS#Xm0&o4Sc%`SqhZA1QYM4~??dNd zyC4@Q0pht);^VSH^VTtF;Iz&GyY^G_CuvrIqH&xyl{q~eCYw$#{U$9j#R4#EkM0kC zltIFM)z4yF^x~PI?Dld`|<3GWoRC-em zhK`)Z!5ix|8Sa>1k3`4v2n~m)FZQt`>IaO-eW4|>j$Oj3F*1c3`gkI8NbCz^T2unw z3Fk?as zJbiAkh$LhXFv4fF|DNgPcN_>1F>(iVOR2nx$=$iDX=Y8aPkr1)E)tcF$83(^%mEH9 zQ$1jEuD25>3J?M$7)hFpkM&L%q~3Ff#;Dj>iLW9El&ly%55nVMA4iqYn@mYsY=Y## zyHTkyKTGoG$NQ6+!3h#_-~`FplJczlhoy$6FEV4a<=6rXhLfulUScoWSY_X2?J1$6OU_x{v26CicQhY$gDJX?I7zKF+Q9 zXcQf)e$aI&?^hc~=d>e`m*JJ9cMqzfyfEIZ=9V$r(AG59Q7t?3V!J-hlZD0 zOcP|faN%HvBc@mU91dI}DMatFT4?-*EL!+1eCoS$$9yM)b6bhv@8qfuWO!dQWY!Mi zc!Nx39kK+7WFTCoemrEf>$bKj^X2rseY}#H&u9 zxiq1Up1HL2%LMH0mF+&eer@kNq{og(5$ivzBU3h7cB}Kr@DM@k7t5{tM0)4?1z!s% zg|QlZkqaQqL~}9{akerm+{DVTrH`|Y4R>Ek_)}kqw_A$ycV zWjeC;$HrxHf}6uY;I4gH*e}Om!zF2rRfr@wTjF0MxwHKGnKDVv=47L4yy@5bQzXKu zq2LXqbT6K7I@Ybiz@`Jmpg*44I>_#~h=aRxwp>?6X|gUaxy=t!=bqgd!ia<0Nu;oj zc6%-^Ois6FDY;cW;lg@^t?5fwS&x%@-95@#|?n^31RbBq-qX#s;{kiL-UV`^8}A89+dGuf7G7$5S(weK-+J9c#aS zyI8D-VplJyx~tM=fIKi!D{84Jq15hR%H!ApAq2hc0u{@ms#HLG6)QgKnvw7SMIHQB(rpc$ws~HkvY1}3|Z_jQ3>e9NuQoa&}p$+_ADdm)s8xR!>Jfwnf!#C3NhA8uXpY91fA)UOFh6;bK*L4lWI{qluN_E8q z?IiGN`g0QDJ$Y&SeGWtNgP?pD7~6uY%7Qnjy1pJjgtwGFB-^txr!nE3!LJR5M9oa# z_7LIVYzki9)G%yD9?R$MZqb(3GMNTA`S^?G_I8PgHVBZ-D0Au4vx{VSy5TI>urYk9 z;4Lz?M);e1_w(f-bTEsxk2>9{k~2vF=FGBr3%~dUz+Y@6KR7AM8Y5w-*=GHMRPkW3 z95Ve(l#DvuEwuVEthwUx>8bIsyt&EiX^^pf>wJ6A4=3hV;ndWWW`@J|0OmD#k@M~N zrM#jd3NtfvBvP006|K#wlO1lxxC_f&K6aBpQDbC5vH0n`p2>FXcaR2zaz?c{e33_0v?Wb-nBXQA> zHz=<aSXI(@%*qW9o7|1agKaf4hkVW6sGnCMM)rW8p>yI4PB+ciTOzaTe*Rs9 zDJnaot%(|+>bLS%HnMY(sQzPa7rk;&Dq&#LKHEp7+c$`(WM13e)tQ{k%97}RKNNH+ z<+Taz{WyUCw=fJDvzfCC-i~-apPmmuND)C0Fw}935^W#IZoO%TNY1TLrIBpq4NYby z0j0IuhWMQDcizD#HMRDRS|E&$$QU|X;v8J3Z?#cZY!egj0-`l>_^$vI{V(S7ni2~G zfVgG~Rd~SLs+c4-^D+BvqDPs%6wE_ntyDQ<2(+cO^n z@)(Ip4#N;zuqz4M8x(8UCT!_4cZ_Wn<|PLda@=tr%F^eV!W3LCt9a{Q&~{GLY5VDz zkNTs2y)Fm$QgCR9ms>s{%PdCnKMSFiB=JWcf*2bI9%gt7&N_L0mAJP06OOq z9mV8NySlQMH9U*~8L6BBZvr`COy6>IHIs0AAGUCrEhJC5NHT>$xcip(mg|9w{YnmK zEX)_j?8v8f{}^{(e2@UZl&_$kg)XHfQ^CY7tM#2mFFkR1f2k*y6`9fa8qiBo{J{UW z*>K?)E17#flw}=0PDbB86_(Wn`k^lvl@+sx(Y> z)Q=Cx1QTt}@O7Iy*pzq2a#@GhUWBXaxwD zeQhl^%*pgzMXE!Qhz9e1lc!hbIPo|`#_)|`nVrX$UKH29(*-CB5`Dg?;m{615v`Qd z^AKl9sNJ=3OP4m%F0Kdk+wV`MGQ8ndaAw?Mz~3D0ktu^BYa$6KKQzMq<24$yH=V*V{@{c9YSxdTqq)q{xavX_X73~Vicdvf7xK>h zVeezT8~lOJk0!hFSy43rDRUf%+25WBhrcnd#-%JwWmIjLKJ1iUZqv*=TXK_nAEKwX zkU6uy!QMjS1uJsc2ow5J(=)EemG-y;oOLGgALbTeeOYq&lUQR1klfo+9HLCj&FC#9 zFBR6p5e`V(&#v!BQr5MvfTy4f+v(oSQOX6L?7rByGjxGSx59zAXJgBChf*xpsnQ$i zi<-)4D)FxEWw#k!;baj-3TMdjs@mzS3i=_0|49XUZK2IE~f+&l;* z)(4h;-wM53hszRfom*H=7L76uarEik}9R$t@dh1nbKYy?R4z`Jys zmcfHQ-S^5*ODabK7VV9h7XH4uwl@39`EM}@+St-%@A8^+D0sNg`eOh2m%k?fa@nwg zJ=LqQxk!bLMX9j;&HwEb(y?9a-%Wa`oU*>a_h1Zpa=Ha1i1}E_{UB*B#V_NBb~z%C zZtLS@Ip3c^meZ1&lTl-~**zx`vTawhbBF9w};-mMvb93cIUR)YGp91M7joE<#^g zY_e~Ttpyu#brlAb0o+VzegE0EJJVR%s@b33!iE+9`tn;HED9uK_>-tEtN5_oE~vG| z6bCI+W=JM}dq|oDKPbHaX}M>Xx;aUo6i1#P+t?4o;jT9WB;qWar>XyH@KwOyyBFWL z00gic3myfj_fEU|59hVxxmw9wdbh_g7Rdi3O7Z9=^uoK{43Q}gEzlZLI`8O6bE-ed zOjeTtb^{=S)fxT)553A@F47+eUnMX_yG-X#_+?J(0NMb=V-o;>{nm)Myv`8ubSg!N zo3|Vt1Fm^nf^jq;`!r(=D;V0%BPU=yrorIn$nF)T43;3fS5LKfk2+atX= zM%EY}kVjiucYgs02AF5@J~WQ)oXe3zS+ln8d{%eRhWPC{d-UJ6Ww*%dHS>S81PyEx ztQF$EE*FVxCFE8?=*alAUVVrJxB|q=fuG-YfMEdzmUU+EKFAYj<`NrW>vP=Dhvhlh z0StA2Ndh9vriJS7%$ASG2$@@1+S`(e$n+gsfb3#oh49>c*k6xxb~4Bsv+5GkZZ3~T zF5JHhh`7+(e#Di<6P)Q7cLLiVVgeWj`aRok*r;oAZPJj`nE;@<;w*<|Hv75nK1~$T zf_-8@B2MzP9obZeS5%-wgax#;^DfL!z8NwnGu7AeaHV8#3Ku!urG!-nO6k?FM*eF3 z*OA`fm~uTs12#eQC&kEl{g2?hjc-LRRa|Zd(eeYHDmsIhJ#T~nr5{#elM9GNhPN%= zx3&yWe1L^@rm$V z-Va`pv7ZvXvG=Jl|KglVO;@p9rhv47XXFuq+jBl8YAlO{JJFo1P8a=BR_j&=Y$QF1 zHl)Zul{E|_bjhhg!R?%1B8~cg%c#)ipfXLU6}BeyY;YVZj5t!j%@B^*Y7f zZ>Psa@qu;K;kgY*k68#2oQ`p@W*P|dPur^xsiUP37-HD!Iuc$e{}PA54B*=rNi+xn zBBaRUnE{K#BAY6XlQbf3JxM@N3n>0aexspN( z06+O@rA_k*&<6II^sH_0@ky5>V`oAFZS`uaT_y;?2oMG_>j3j*+r782y#P2cBWUEYK@+ZYgRniC{emTIJWKFC?bo_Hc#-~Cn{tcRz z+koOM3hyi|w!f8{~8 zp%hqcC0WnOwo3+Jd#%bFU2bT|%hM30Vu}d+JJGIRU^ARmv`UzoGcOo<*2bX&aCvLq z%|%xw;!?+00NCyeM)N@X9~gi3`J_Vi{YM8Q_8nR{kllpPqWHfw#$?|=H$eQ{7^nNZ zPsmWOLA*m5K|DBc$BHAUw!5}>zms~dQC`CsCmo)$2CPtl1UTiB$R>)A%ac|KE}!+- z<(<u(H@%ti@Tt|tK6z2H>OVYeMb*EW?jYR2TC5M*le-?hPBJt zU>kk1M<+fWg!m0Oq1c&Y0c%S(VS&X3Jr zeJBJOHmhf@xBZ%E-Evc`d1QTaz4v@-pHr3e>1mT7wB*t`T?Rm7n|r+t5V1m#rZm)0 zhIb49JItlGo62y0vD)6R`$CJ@V<-$rHLfdOk6=#0-qFRb@%5|4b$yGgm}>Fic)c2n zB}EoiiukE!QV0vQeSkdV?`Xwo?RQfEH94m-o&2KP!CT&(hr0(<4R>yW3QBkgG@JIx=IK;q*k`*b>a* zlCCMUvXVvF&lEJQ-&C{fjR-bWlEF9%2g@-(Rli!Q%w@z_+>oJtDTzrON$-7S3^gyn z>9qZPJm5|OYbZjQxnExt8MK`(nUEZ_YaCuki-ZpTnR2>OCKsH0W~d%K9}0a#R4C4_ zOBiEuc>6oX=SRoU3oE~r%4HWyAf4p9ihLCAb_Le6pER^pXb2d$QFSJx{(+AMQ#O?? zBO!{yNj<{xOu1xv7dUp#eCkN_+2tc)|KJ=Cnxp^^@}pPlEBec^a|FEt;)dd!rbylP zXB(R0L(tgI>C(qls~d3R=*-zOM!v+vT=J>< z`hCxySi>QdWWU>dKpw9r8*xUKxNoXX)|j8~o&_`*@Ttou91bkesV6@KzdNfwOalgl zNt|_hm!|~y9+~j^8pl>%OwAn|{77-#|6U~Sj{^xL{S%*z;fwbBIC?jXz1^-=@8%Tp zi`^x)e$rnYdTsxFBJYqQcdpUU@qPn&B}a)j`!9V^*x#>qPVegJfd-smo}Qk!t5IYZ z$Gu5EP-j-6y@ir`yl?@m?2da3V3xlo_p-J+-MAh|3F*?|YW^j_<$SxqUl+rm|M&$6 zNIiLsv)3mfJu$HF@b2Mue{Yi7c`7l#l_sPTKo|rC1b$b>v45e1;;0449PimVj={tI zKu=7vP*cH6%+YUWsDA|w$+2Orn?0hcuOVW?MevD1(8ro2x3(N8W;?PJDuBz)zUg{2 z;(WpVAXNhd0EG<>+HyP=VaOKX!VEir6}7 zA3Aq^r-U#}T2FEaKDgidg`)3zVM}aP;;JTY3au;w+27^GM)L6)DbjfFU$$Kbh3e~H z)J5Iiz1$R(DLEhIDXw^5?G9yd^mL3PmbOfo19irH;{?IpmaV=%8e{{APgzdmUn_qD zkFb@PK{UqgM089ipWORj=QIm28v@j64zDmQyR2|JV3sl{0HrIlzOWX~Y)kEZ0u&0b zRAPmb*uV1XR|+iBzukrUT^V43m{U@=J#1+`Kb8R359K;Ix#t&V*p_R5DYhFu&-5Ae-ShCrF6$LSs0vf^mMOuky}1IQXyh%z<7F( zyYQDWUWXUhRc7aOmiZ|=P7AQKXDm&IX>Qf7W~@NsX9fv zHu;!KDR01`saW*YeQOMS;@FIP$Q}sbxj;=1v9agJH~<`5{@x}0e{u6+>=2C?sZBm1 z*{&-&7PSN#V2#SpMV^q9eb1ht_2j~=hG*)sfBho!3D~4KkPLNN8%aLy4=LjF$RpBg z>7R88n2ja?;X*gF6yj^=7e-J1WgEMIUxk%A2Ch5V`>su95PxW>Onjc}{a9|gg>>6y zi}0kFllzAr5&=Ay_nM6YQ7Wz@P{j2i_W^Rij%|X7O7abua?}6#*zZ@lc3Ic*!=Wh& zaPXr1@c>rl%S{(~xE(%tZ4YJ?=;IcjUpz1V)t6!5 z!v;^I%*n$5=ENFpj%t6}>3eg3$HUX|@iWkiOC$hxS7*P}_FAww2Qc=iqx1(%wW)JJ zoi@H$yNE zldkl#f8``<>k^7EBkG(}%#DTt37?si2PZ)~px=_DBrC1{riPFg`+r^lg4ZURzpZa1 z#R5~+{d;XLJ+M!n8ZWAYNw9$*Zs)%^%=MES&>pVNjnMimj~D^vu)474^#7C}?#T5G za%uT}h##*d@3RT41Q}0*nYQbFhZ!K(LjuNN0$!F2SoV;u9Hk2(;WvXSg`1OYj>4<1 z<7yFXKPmxlndIy^)H`uC;=IUbi8x_DTc?b)79Sa-OL>Gl0q&Mjnrv z;cu4}GJ6&shUm-NT8*$hEj01iKNUv>5&-5VnH`F`SjhyZ-T9bgg;agKDK1DaemC#u zaQxz!6=_~cIj_kqz=(lDn((=hP+3A6I=06!r*TPxnvhf2xfe|PFS$N_Wl6P)sIs_3 z1o^|;~Ser7d(kcm&GsEVlz)1l6&91ivNou2IyDv z?n#kFEcB~IZ0#C7shc&7bPwI58y8pc7FRS)+pU}y%St)OHCsE7;E#=CjU%nfv$B-UH{AG8 zzrO9dLo@!dN({AmhoIAS>Lx(MPsD>T+tTOQP(FHf=iN3D0ca~_LwmcM_f8(v?>`aq ze>Ctz^vzZlYNDYAfV@8nXVF%8!mjd)9pV5?gO4!6pw1I!!Epyop#$;`cvY!n3W|xP zv~a+zd)js1i4Ox?>V?P!G?G2DkMEK*#kq7%w+zY4O6yH#5OghGIK5`eHCK1SH7K~+ zdlq}Xm;p$z3t{prFtb?KV54B?i*t70b-(+cKf0f!Ab=F(-|$Y;`ABcZ2CMqP_ad)r zv@ca`R8W2bT`;cpV)t&hz!LQ6g@AB`n`e(_NAI4V=x>LLjS-{f)##T}@L>&{H))QG zJszZfmdmgwF1^^`ceMy_?coxSB3y&59q}ymurE|>$&LNz}y)0SV)CO5armj+?+pUZyxa;rBS0yxASV0iEoQ;xSQ7Eq4kQP zSi?cAIl8gaa&8qVi$cDIJ2g;yl-vls5viEmu>C#aSNNrlL!v20wP1Q6-ybF8ryEji z8NcIub`E8?vBoI@{VY1&W~{rZSD~?7MO_RNe&tI?rohk}p^C|~DN+@;P0sfBKTDLh z9glY>uU0|8*EsVKMy*w9G!Snh-nn4L4A5ApH7rTGY^{ zT$U`$t310mmV1rs=oBAp5is&}8cpv?je~VJC~g3_*V*&nm5a{mJsdyrJ^Nr+8};dc znxLJwTUP}K!;3b@P4lXE^vO+1J=5#)eY3qh)|QnLuWH%x5SKV9(tbXVZ0M>R(U)*+ zDXFQKXcUw`3-EEcbsIIuL}DZsCk{?(*#JYYCqq7_6H0=!eO0HYyxuU;l~#mGEx@!c zK)kkBT#T#d?+*J8Z*RkFY`M}j$5#bO0t-!KbK|me2lkg{n=0xJY!;X;mVQ0c6rys< zo4sRMfvj!Q>5vbHHQYUmDOpRh9z z6^iK(0buQFFzYQKw*>0}iw*>i=50d3S*x(eyP<>z(jCeM9Gr~w-Lhs0*H><6p`;OS zGX9wN)sf{L#`V?5x{V-&gT*)<5^ZW)S{3eu2Fy*;hwMr7`Kkvhc4>Ye`BkA40k8eV z3x;VHr>h$hJAF1@V-qRZ+TKt{K@rt+U53n%I&loLL7WJaK0L$*h{7maq zG25*su=}cZa3$B~wIv7kl`YvvjOJ0H41KS!q9I@Jgs{sfPSL<>sr(*rQdS_(eTWy| zdOPc|KQXTWcC`||IJNXym0#kw|2beLDieyx$?P^I#D@2?ylfM2imJ2*zxjH3MFG6M ziUEw?sXfQs3GnW^vpb5mEu!0wKrgM1ZBz(yG{aBTZEfi>HybJ+YTsS*@3uFsdibqG z^fHWw8}9|*Cg_;v6{G-SvU&uMZ~;MIQLs{ z+p?>@MybV}Mwzn04rg=!qjA0CtNP;vx3?q@CnMxC_kgCHkq=RrH-6WEHk`^xC|fbJkOT=xDUg`vrVMXP{P^-=gklozy|#Q?1D-c}jnj zyK6uu7%%M-nq69m2~waKz&^qtE7G8SixD_Qq@x>0}?G(S1XBR*GU@E7bo!&7@F(mN;n6?MV?kF2i>%PMHwB?Od~mXwh0 z?oR3M?(PNw>F(~9?(S}oknZm8+zb8w@8A2{CmwL%yVlICndh!&M>)4K2ZIA*5 zcD(sNZU}RBcyBNJQ0eJd*V?^0uUwl!y=!h(P=P&NhmP7c$FVe(3n$+80V5T-PtwmG z>_utiC+4^rCud8+jk2GCZLO@Vv@~pad08Ac)BTM#;pIwuCf!o!<@P15tl=}(saDOm zZZ`;B=!5P=SEzaW+90>obhxIc1+P}WqAFjyN4F9jAn~p+$0@pm3?q8W?!Oo-1Ogim z%)a2?*#}h2FBBCtPkzf>P$1{b4hFBKACets_~CJA4AwS~K_swvFa<dY=6ThHV&-RzJp@)QV0}T~(BfFeXl1uqOR|9sgsibz;x0Cuuxrgr zGc>oy;AzE^eR0p4{FOfSa^S#TNB9w6EqHPb3PfEod{|6PYbRTuHDytX5VCPj65g4l zO$`_%&cy^A_oSQDzVgjU5P4@MN;kd`5vu*nR6CI02eq8Ewqe7jev7BD=UbER!t2wF zwQeJ)xfbI*4&i5VBAHe`oNQD9zg|}+O>7(q^DURVlD|*1m^5pJhlkJIaH`(o`|X)& zbvxC!^6wB)RTP^(q(~JS%4jDqxDam3zxN*!VOL-4+(dXC|H;CgS6l!eF5J^|@z&-3 zc7?t26YNjK9vyj!`NFyN9%oUD2y8t5Hkdc{Yr4yI#%G6HI*_QgJB*_S^v0bud|zcK z;z(#hTfJKIhsBE+y9BTH@U*jum3%AdW62Q@f5a1Id(a`>hH01E(qU6ICEy6E2j>?L_%_5_S5E%JZhi?zh!IEgErZwJGCW?6{g6kYyfx;T^{A zbcc(x-cfhq-+gOr`WAa#@*ee$73yfwAZ`#4w0EsmM5%3`fz#*N(o?JDR2YfBR(cGA z=)9Co6uu?Bm~a3zDC$9kMdIJ$2a7iQ%AsOshPU#g_5Au*bLAm zIc>CVCZI)!=eF2Rd-N;W^l$qC{|Y=2(vn{U2M8ng(}Bq|Ec5&Oe|*q!GskoGj3@JJ zG8Zp6<;G9J)425^zUod%{~nytwf({kNrT&F{J|NLl}7khEoAV1m&4-rD77XxK#LC) zsCZH)eFc2%S47{Q44*Q9`qqSpn)Ivd@2@Ar1rH)1J&@m_rov{bbn<3?@93;!S0nWt?ab+a*ROT3~p1}7#2D9FtfDm zu4CWOIsX23RRZbG#fHp4YKd!qYGLA!>axrZK15!>a4|d%xWVh>*ni%ws4_23`*kAK zBkqO<8y@Nh_bR2^t8Gliee&@yTdeI9p5UGh-ookcg^7(;kQMNwNf<&VHt3zIg5TE~ zk@MU6b_|pY=S<(r=S=D5XA7byM=N)rDS{=Cj_q0-VCxu{XOA+IdiK_|JtbvPfEHyD z`ggdb-Kaa!ns;Tg>exDUhGEltqgH&QV|EJ~9$E}+u7LvzpbxyGm3$u@nTpdyYrko^ z;UM%0HHV1$=cATB+66A5N3XXoi2Ax^PnrO$h@#q#jI2VABLmpFN%iHsmgS*Efo&ax z0C-c$=zp`ATU?L;VO^@T;a*ZCKb|vp;BpvUMEQE7L8iAX{17NL`MdUbpNFLP7f>S! z0U#l8li*-&3m!x&QMu-D1ZRfyhMc5@xlL-s@7F6(vig-^73H;tpsFrC?6wfTBcyYT zG2dD)!%ynKciM7#{9VVpW7u?&_%A>*L3YAp9p36t?FRNP(0#O-_`4r(lnezCi7Ope zgn{5rxDHOhEux2j0R|j6&=$$tq{O=$KXyyvd)EZj+6ic-k`i?J*F-z0!QBc&sXO5r z3Mo+oV_`Oc%pasa3+&>+IFmz1fFRO!=Gn#6i~k+v<&?In=$Vd+y{0&4P4#|hOyoYO z&|QYk8G#*#lZnY3IdXFpkj(*Mu?wkXq%x%*PyKF9p}gv0HK0V!%{e$rYBA3UXQcRn z!Uy`py9VpP`sixD_9{#Lj2@^Q<~GG3@|rjCs{YJ?(JCZVtT6t4QyX>vZ~2^wm+*My z3qfsFpLy`CV=_~^tTQwCNCZUcw(7*2AV@&YPv?E!f^bXiFgHFiVm4Y!u*$x)fR7VQ z6~np4Db5zB9X~=q+|yC~$Sc7b+OVm#T(bpsN6GhH!cyr9ss1sfo@c8J*qkv3;Vl~e z7A%e`S3?>J-yb_d|40!m&jfg#8O;9Vse1CZU*Gr*O03kH(96XWEXSt$q^Kqvp1%(Z zA|QcZy_9Z3hY2Tviz;m4LJES)1WmD|@>#5qYc70-s*(Z3nA{v#UN@5VJ)FTx6p2Xu zF4ep6MA+~ab&iJtO+sVfwJ$)ONVp9Te8`%YMKpdg14Wc_8L2(b(ziwFf{n>)JCqge zu~Ne=2Qy^Glcsf?ubqOc5QHs{64BJx5@(!O#rHtJGCB7xupAc<^v7ajqOpROrA#O$q`sN zq6naF6xqa?mK3(FNkn* zBvdUq68#63m)o%p++YAqJYjTv`~@P<&HUSNq@g$L?{-ByW@&!Z)=X|mB}WW0PKLMO@> z>CI7>ZfdP>1{B1`isWC|&`RA1b@h>xUv!9s%+X?8S;G$AxYB94WGsvhLa>7}##)2B z&}rge~Bt;qNZcdMT~9skR8B+40+BUV})Tmzh3qFAzJPK{AQtO+2p;=IdHta68)p? zRE|w@i5N$e--TSAZr3?ROf|Y|s<4n@uMD=D`LtUPTxQTqA6l0XNmQ*a)UNfS{*=J8 z3nd^@^sD2W>wqk@ikmi3m2_TIg0i5iDX1w;Ki%_WeqNa<{=*F{<=6z5q0zy}5ao(V z6H$cW7E+A1*7tEmBO{6>G$gDq;+r{?PVwa4-R(?dL=g!3-FR1~t20d|kzee8e9qDt zn&@g}!wR)`b`F{27O+sE4ECbeuX5cgqak_nB`SdZBGp~})1Jm?4F4wHAi=00=9-s4 zT***(yy|x}*y@jO!FhLAG;k^@eVgbpVhbt}vOuk=Hxj`$`Z%Ts;B4&t`PFe}xoOJ

ZBPH0O>ti*)n}eq)Tv)3NSp|#L_TM4u!#t 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 R{A&U`s)|}qu=3CU{x44$_#glP literal 0 HcmV?d00001 diff --git a/tmp/lanelet2_extension/docs/turn_direction.png b/tmp/lanelet2_extension/docs/turn_direction.png new file mode 100644 index 0000000000000000000000000000000000000000..3fd63585e10fee58ee225575f1a7eebf0be4de97 GIT binary patch literal 67105 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 UC)oyP_jpU{gS=R&h{2ct1rWacjQ{`u literal 0 HcmV?d00001 diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/io/autoware_osm_parser.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/io/autoware_osm_parser.hpp new file mode 100644 index 00000000..3dd7ce63 --- /dev/null +++ b/tmp/lanelet2_extension/include/lanelet2_extension/io/autoware_osm_parser.hpp @@ -0,0 +1,62 @@ +// Copyright 2015-2019 Autoware Foundation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Authors: Ryohsuke Mitsudome + +#ifndef LANELET2_EXTENSION__IO__AUTOWARE_OSM_PARSER_HPP_ +#define LANELET2_EXTENSION__IO__AUTOWARE_OSM_PARSER_HPP_ + +#include + +#include +#include + +namespace lanelet +{ +namespace io_handlers +{ +class AutowareOsmParser : public OsmParser +{ +public: + using OsmParser::OsmParser; + + /** + * [parse parse osm file to laneletMap. It is generally same as default + * OsmParser, but it will overwrite x and y value with local_x and local_y + * tags if present] + * @param filename [path to osm file] + * @param errors [any errors caught during parsing] + * @return [returns LaneletMap] + */ + std::unique_ptr parse( + const std::string & filename, ErrorMessages & errors) const; // NOLINT + + /** + * [parseVersions parses MetaInfo tags from osm file] + * @param filename [path to osm file] + * @param format_version [parsed information about map format version] + * @param map_version [parsed information about map version] + */ + static void parseVersions( + const std::string & filename, std::string * format_version, std::string * map_version); + + static constexpr const char * extension() { return ".osm"; } + + static constexpr const char * name() { return "autoware_osm_handler"; } +}; + +} // namespace io_handlers +} // namespace lanelet + +#endif // LANELET2_EXTENSION__IO__AUTOWARE_OSM_PARSER_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp new file mode 100644 index 00000000..402162b8 --- /dev/null +++ b/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp @@ -0,0 +1,115 @@ +// Copyright 2015-2019 Autoware Foundation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Authors: Simon Thompson, Ryohsuke Mitsudome + +#ifndef LANELET2_EXTENSION__PROJECTION__MGRS_PROJECTOR_HPP_ +#define LANELET2_EXTENSION__PROJECTION__MGRS_PROJECTOR_HPP_ + +#include +#include + +#include +#include + +#include +#include + +namespace lanelet +{ +namespace projection +{ +class MGRSProjector : public Projector +{ +public: + explicit MGRSProjector(Origin origin = Origin({0.0, 0.0})); // NOLINT + + /** + * [MGRSProjector::forward projects gps lat/lon to MGRS 100km grid] + * @param gps [point with latitude longitude information] + * @return [projected point in MGRS coordinate] + */ + BasicPoint3d forward(const GPSPoint & gps) const override; + + /** + * [MGRSProjector::forward projects gpgs lat/lon to MGRS xyz coordinate] + * @param gps [point with latitude longitude information] + * @param precision [resolution of MGRS Grid 0=100km, 1=10km, 2=1km, 3=100m, + * 4=10m, 5=1m] + * @return [projected point in MGRS coordinate] + */ + BasicPoint3d forward(const GPSPoint & gps, const int precision) const; + + /** + * [MGRSProjector::reverse projects point within MGRS 100km grid into gps + * lat/lon (WGS84)] + * @param mgrs_point [3d point in MGRS 100km grid] + * @return [projected point in WGS84] + */ + GPSPoint reverse(const BasicPoint3d & mgrs) const override; + + /** + * [MGRSProjector::reverse projects point within MGRS grid into gps lat/lon + * (WGS84)] + * @param mgrs_point [3d point in MGRS grid] + * @param mgrs_code [MGRS grid code] + * @return [projected point in WGS84] + */ + GPSPoint reverse(const BasicPoint3d & mgrs_point, const std::string & mgrs_code) const; + + /** + * [MGRSProjector::setMGRSCode sets MGRS code used for reverse projection] + * @param mgrs_code [MGRS code. Minimum requirement is GZD and 100 km Grid + * Square ID. e.g. "4QFJ"] + */ + void setMGRSCode(const std::string & mgrs_code); + + /** + * [MGRSProjector::setMGRSCode sets MGRS code used for reverse projection from + * gps lat/lon values] + * @param gps [gps point used to find GMRS Grid] + * @param precision [resolution of MGRS Grid 0=100km, 1=10km, 2=1km, 3=100m, + * 4=10m, 5=1m] + */ + void setMGRSCode(const GPSPoint & gps, const int precision = 0); + + /** + * [getProjectedMGRSGrid returns mgrs] + * @return [description] + */ + std::string getProjectedMGRSGrid() const { return projected_grid_; } + + /** + * [isMGRSCodeSet checks if mgrs code is set for reverse projection] + * @return [true if mgrs_code member is set] + */ + bool isMGRSCodeSet() const { return !mgrs_code_.empty(); } + +private: + /** + * mgrs grid code used for reverse function + */ + std::string mgrs_code_; + + /** + * mgrs grid code that was last projected in previous forward function. + * reverse function will use this if isMGRSCodeSet() returns false. + */ + mutable std::string projected_grid_; +}; + +} // namespace projection +} // namespace lanelet + +#endif // LANELET2_EXTENSION__PROJECTION__MGRS_PROJECTOR_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp new file mode 100644 index 00000000..6b385663 --- /dev/null +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp @@ -0,0 +1,96 @@ +// Copyright 2015-2019 Autoware Foundation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Authors: Ryohsuke Mitsudome + +#ifndef LANELET2_EXTENSION__REGULATORY_ELEMENTS__AUTOWARE_TRAFFIC_LIGHT_HPP_ +#define LANELET2_EXTENSION__REGULATORY_ELEMENTS__AUTOWARE_TRAFFIC_LIGHT_HPP_ + +#include +#include + +#include +#include + +namespace lanelet +{ +namespace autoware +{ +struct AutowareRoleNameString +{ + static constexpr const char LightBulbs[] = "light_bulbs"; +}; + +class AutowareTrafficLight : public lanelet::TrafficLight +{ +public: + using Ptr = std::shared_ptr; + static constexpr char RuleName[] = "traffic_light"; + + //! Directly construct a stop line from its required rule parameters. + //! Might modify the input data in oder to get correct tags. + static Ptr make( + Id id, const AttributeMap & attributes, const LineStringsOrPolygons3d & trafficLights, + const Optional & stopLine = {}, const LineStrings3d & lightBulbs = {}) + { + return Ptr{new AutowareTrafficLight(id, attributes, trafficLights, stopLine, lightBulbs)}; + } + + /** + * @brief get the relevant traffic light bulbs + * @return the traffic light bulbs + * + * There might be multiple traffic light bulbs but they are required to show + * the same signal. + */ + ConstLineStrings3d lightBulbs() const; + + /** + * @brief add a new traffic light bulb + * @param primitive the traffic light bulb to add + * + * Traffic light bulbs are represented as linestrings with each point + * expressing position of each light bulb (lamp). + */ + void addLightBulbs(const LineStringOrPolygon3d & primitive); + + /** + * @brief remove a traffic light bulb + * @param primitive the primitive + * @return true if the traffic light bulb existed and was removed + */ + bool removeLightBulbs(const LineStringOrPolygon3d & primitive); + +private: + // the following lines are required so that lanelet2 can create this object + // when loading a map with this regulatory element + friend class lanelet::RegisterRegulatoryElement; + AutowareTrafficLight( + Id id, const AttributeMap & attributes, const LineStringsOrPolygons3d & trafficLights, + const Optional & stopLine, const LineStrings3d & lightBulbs); + explicit AutowareTrafficLight(const lanelet::RegulatoryElementDataPtr & data); +}; +static lanelet::RegisterRegulatoryElement regAutowareTraffic; + +// moved to lanelet2_extension/lib/autoware_traffic_light.cpp to avoid multiple +// definition errors +/* +#if __cplusplus < 201703L +constexpr char AutowareTrafficLight::RuleName[]; // instantiate string in +cpp file #endif +*/ +} // namespace autoware +} // namespace lanelet + +#endif // LANELET2_EXTENSION__REGULATORY_ELEMENTS__AUTOWARE_TRAFFIC_LIGHT_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/detection_area.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/detection_area.hpp new file mode 100644 index 00000000..e73ce32f --- /dev/null +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/detection_area.hpp @@ -0,0 +1,95 @@ +// Copyright 2015-2019 Autoware Foundation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Authors: Ryohsuke Mitsudome + +#ifndef LANELET2_EXTENSION__REGULATORY_ELEMENTS__DETECTION_AREA_HPP_ +#define LANELET2_EXTENSION__REGULATORY_ELEMENTS__DETECTION_AREA_HPP_ + +#include +#include + +#include +#include + +namespace lanelet +{ +namespace autoware +{ +class DetectionArea : public lanelet::RegulatoryElement +{ +public: + using Ptr = std::shared_ptr; + static constexpr char RuleName[] = "detection_area"; + + //! Directly construct a stop line from its required rule parameters. + //! Might modify the input data in oder to get correct tags. + static Ptr make( + Id id, const AttributeMap & attributes, const Polygons3d & detectionAreas, + const LineString3d & stopLine) + { + return Ptr{new DetectionArea(id, attributes, detectionAreas, stopLine)}; + } + + /** + * @brief get the relevant detection_areas + * @return detection_areas + */ + ConstPolygons3d detectionAreas() const; + Polygons3d detectionAreas(); + + /** + * @brief add a new detection area + * @param primitive detection area to add + */ + void addDetectionArea(const Polygon3d & primitive); + + /** + * @brief remove a detection area + * @param primitive the primitive + * @return true if the detection area existed and was removed + */ + bool removeDetectionArea(const Polygon3d & primitive); + + /** + * @brief get the stop line for the detection area + * @return the stop line as LineString + */ + ConstLineString3d stopLine() const; + LineString3d stopLine(); + + /** + * @brief set a new stop line, overwrite the old one + * @param stopLine new stop line + */ + void setStopLine(const LineString3d & stopLine); + + //! Deletes the stop line + void removeStopLine(); + +private: + // the following lines are required so that lanelet2 can create this object + // when loading a map with this regulatory element + friend class lanelet::RegisterRegulatoryElement; + DetectionArea( + Id id, const AttributeMap & attributes, const Polygons3d & detectionAreas, + const LineString3d & stopLine); + explicit DetectionArea(const lanelet::RegulatoryElementDataPtr & data); +}; +static lanelet::RegisterRegulatoryElement regDetectionArea; + +} // namespace autoware +} // namespace lanelet + +#endif // LANELET2_EXTENSION__REGULATORY_ELEMENTS__DETECTION_AREA_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/no_stopping_area.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/no_stopping_area.hpp new file mode 100644 index 00000000..9e2ca0c7 --- /dev/null +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/no_stopping_area.hpp @@ -0,0 +1,93 @@ +// Copyright 2021 Tier IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LANELET2_EXTENSION__REGULATORY_ELEMENTS__NO_STOPPING_AREA_HPP_ +#define LANELET2_EXTENSION__REGULATORY_ELEMENTS__NO_STOPPING_AREA_HPP_ + +#include +#include + +#include +#include + +namespace lanelet +{ +namespace autoware +{ +class NoStoppingArea : public lanelet::RegulatoryElement +{ +public: + using Ptr = std::shared_ptr; + static constexpr char RuleName[] = "no_stopping_area"; + + //! Directly construct a stop line from its required rule parameters. + //! Might modify the input data in oder to get correct tags. + static Ptr make( + Id id, const AttributeMap & attributes, const Polygons3d & no_stopping_areas, + const Optional & stopLine = {}) + { + return Ptr{new NoStoppingArea(id, attributes, no_stopping_areas, stopLine)}; + } + + /** + * @brief get the relevant no stopping area + * @return no stopping area + */ + ConstPolygons3d noStoppingAreas() const; + Polygons3d noStoppingAreas(); + + /** + * @brief add a new no stopping area + * @param primitive no stopping area to add + */ + void addNoStoppingArea(const Polygon3d & primitive); + + /** + * @brief remove a no stopping area + * @param primitive the primitive + * @return true if the no stopping area existed and was removed + */ + bool removeNoStoppingArea(const Polygon3d & primitive); + + /** + * @brief get the stop line for the no stopping area + * @return the stop line as LineString + */ + Optional stopLine() const; + Optional stopLine(); + + /** + * @brief set a new stop line, overwrite the old one + * @param stopLine new stop line + */ + void setStopLine(const LineString3d & stopLine); + + //! Deletes the stop line + void removeStopLine(); + +private: + // the following lines are required so that lanelet2 can create this object + // when loading a map with this regulatory element + friend class lanelet::RegisterRegulatoryElement; + NoStoppingArea( + Id id, const AttributeMap & attributes, const Polygons3d & no_stopping_area, + const Optional & stopLine); + explicit NoStoppingArea(const lanelet::RegulatoryElementDataPtr & data); +}; +static lanelet::RegisterRegulatoryElement regNoStoppingArea; + +} // namespace autoware +} // namespace lanelet + +#endif // LANELET2_EXTENSION__REGULATORY_ELEMENTS__NO_STOPPING_AREA_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/road_marking.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/road_marking.hpp new file mode 100644 index 00000000..62b719d2 --- /dev/null +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/road_marking.hpp @@ -0,0 +1,71 @@ +// Copyright 2020 Tier IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LANELET2_EXTENSION__REGULATORY_ELEMENTS__ROAD_MARKING_HPP_ +#define LANELET2_EXTENSION__REGULATORY_ELEMENTS__ROAD_MARKING_HPP_ + +#include +#include + +#include +#include + +namespace lanelet +{ +namespace autoware +{ +class RoadMarking : public lanelet::RegulatoryElement +{ +public: + using Ptr = std::shared_ptr; + static constexpr char RuleName[] = "road_marking"; + + //! Directly construct a stop line from its required rule parameters. + //! Might modify the input data in oder to get correct tags. + static Ptr make(Id id, const AttributeMap & attributes, const LineString3d & road_marking) + { + return Ptr{new RoadMarking(id, attributes, road_marking)}; + } + + /** + * @brief get the relevant road marking + * @return road marking + */ + ConstLineString3d roadMarking() const; + LineString3d roadMarking(); + + /** + * @brief add a new road marking + * @param primitive road marking to add + */ + void setRoadMarking(const LineString3d & primitive); + + /** + * @brief remove a road marking + */ + void removeRoadMarking(); + +private: + // the following lines are required so that lanelet2 can create this object + // when loading a map with this regulatory element + friend class lanelet::RegisterRegulatoryElement; + RoadMarking(Id id, const AttributeMap & attributes, const LineString3d & roadMarking); + explicit RoadMarking(const lanelet::RegulatoryElementDataPtr & data); +}; +static lanelet::RegisterRegulatoryElement regRoadMarking; + +} // namespace autoware +} // namespace lanelet + +#endif // LANELET2_EXTENSION__REGULATORY_ELEMENTS__ROAD_MARKING_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/virtual_traffic_light.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/virtual_traffic_light.hpp new file mode 100644 index 00000000..d4049953 --- /dev/null +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/virtual_traffic_light.hpp @@ -0,0 +1,78 @@ +// Copyright 2021 Tier IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LANELET2_EXTENSION__REGULATORY_ELEMENTS__VIRTUAL_TRAFFIC_LIGHT_HPP_ +#define LANELET2_EXTENSION__REGULATORY_ELEMENTS__VIRTUAL_TRAFFIC_LIGHT_HPP_ + +#include +#include + +#include +#include + +namespace lanelet +{ +namespace autoware +{ +class VirtualTrafficLight : public lanelet::RegulatoryElement +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + static constexpr char RuleName[] = "virtual_traffic_light"; + + static Ptr make( + const Id id, const AttributeMap & attributes, const LineString3d & virtual_traffic_light) + { + return Ptr{new VirtualTrafficLight(id, attributes, virtual_traffic_light)}; + } + + ConstLineString3d getVirtualTrafficLight() const + { + return getParameters(RoleName::Refers).front(); + } + + Optional getStopLine() const + { + const auto vec = getParameters(RoleName::RefLine); + if (vec.empty()) { + return {}; + } + return vec.front(); + } + + ConstLineString3d getStartLine() const + { + return getParameters("start_line").front(); + } + + ConstLineStrings3d getEndLines() const { return getParameters("end_line"); } + +private: + // the following lines are required so that lanelet2 can create this object + // when loading a map with this regulatory element + friend class lanelet::RegisterRegulatoryElement; + + VirtualTrafficLight( + const Id id, const AttributeMap & attributes, const LineString3d & virtualTrafficLight); + + explicit VirtualTrafficLight(const lanelet::RegulatoryElementDataPtr & data); +}; + +static lanelet::RegisterRegulatoryElement regVirtualTrafficLight; + +} // namespace autoware +} // namespace lanelet + +#endif // LANELET2_EXTENSION__REGULATORY_ELEMENTS__VIRTUAL_TRAFFIC_LIGHT_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/utility/message_conversion.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/utility/message_conversion.hpp new file mode 100644 index 00000000..a1886b86 --- /dev/null +++ b/tmp/lanelet2_extension/include/lanelet2_extension/utility/message_conversion.hpp @@ -0,0 +1,100 @@ +// Copyright 2015-2019 Autoware Foundation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Authors: Simon Thompson, Ryohsuke Mitsudome + +#ifndef LANELET2_EXTENSION__UTILITY__MESSAGE_CONVERSION_HPP_ +#define LANELET2_EXTENSION__UTILITY__MESSAGE_CONVERSION_HPP_ + +#include +#include +#include +#include + +#include +#include +#include + +namespace lanelet +{ +namespace utils +{ +namespace conversion +{ +/** + * [toBinMsg converts lanelet2 map to ROS message. Similar implementation to + * lanelet::io_handlers::BinHandler::write()] + * @param map [lanelet map data] + * @param msg [converted ROS message. Only "data" field is filled] + */ +void toBinMsg(const lanelet::LaneletMapPtr & map, autoware_auto_mapping_msgs::msg::HADMapBin * msg); + +/** + * [fromBinMsg converts ROS message into lanelet2 data. Similar implementation + * to lanelet::io_handlers::BinHandler::parse()] + * @param msg [ROS message for lanelet map] + * @param map [Converted lanelet2 data] + */ +void fromBinMsg(const autoware_auto_mapping_msgs::msg::HADMapBin & msg, lanelet::LaneletMapPtr map); +void fromBinMsg( + const autoware_auto_mapping_msgs::msg::HADMapBin & msg, lanelet::LaneletMapPtr map, + lanelet::traffic_rules::TrafficRulesPtr * traffic_rules, + lanelet::routing::RoutingGraphPtr * routing_graph); + +/** + * [toGeomMsgPt converts various point types to geometry_msgs point] + * @param src [input point(geometry_msgs::msg::Point3, + * Eigen::VEctor3d=lanelet::BasicPoint3d, lanelet::Point3d, lanelet::Point2d) ] + * @param dst [converted geometry_msgs point] + */ +void toGeomMsgPt(const geometry_msgs::msg::Point32 & src, geometry_msgs::msg::Point * dst); +void toGeomMsgPt(const Eigen::Vector3d & src, geometry_msgs::msg::Point * dst); +void toGeomMsgPt(const lanelet::ConstPoint3d & src, geometry_msgs::msg::Point * dst); +void toGeomMsgPt(const lanelet::ConstPoint2d & src, geometry_msgs::msg::Point * dst); + +/** + * [toGeomMsgPt converts various point types to geometry_msgs point] + * @param src [input point(geometry_msgs::msg::Point3, + * Eigen::VEctor3d=lanelet::BasicPoint3d, lanelet::Point3d, lanelet::Point2d) ] + * @return [converted geometry_msgs point] + */ +geometry_msgs::msg::Point toGeomMsgPt(const geometry_msgs::msg::Point32 & src); +geometry_msgs::msg::Point toGeomMsgPt(const Eigen::Vector3d & src); +geometry_msgs::msg::Point toGeomMsgPt(const lanelet::ConstPoint3d & src); +geometry_msgs::msg::Point toGeomMsgPt(const lanelet::ConstPoint2d & src); + +lanelet::ConstPoint3d toLaneletPoint(const geometry_msgs::msg::Point & src); +void toLaneletPoint(const geometry_msgs::msg::Point & src, lanelet::ConstPoint3d * dst); + +/** + * [toGeomMsgPoly converts lanelet polygon to geometry_msgs polygon] + * @param ll_poly [input polygon] + * @param geom_poly [converted geometry_msgs point] + */ +void toGeomMsgPoly( + const lanelet::ConstPolygon3d & ll_poly, geometry_msgs::msg::Polygon * geom_poly); + +/** + * [toGeomMsgPt32 converts Eigen::Vector3d(lanelet:BasicPoint3d to + * geometry_msgs::msg::Point32)] + * @param src [input point] + * @param dst [converted point] + */ +void toGeomMsgPt32(const Eigen::Vector3d & src, geometry_msgs::msg::Point32 * dst); + +} // namespace conversion +} // namespace utils +} // namespace lanelet + +#endif // LANELET2_EXTENSION__UTILITY__MESSAGE_CONVERSION_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp new file mode 100644 index 00000000..2984525c --- /dev/null +++ b/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp @@ -0,0 +1,252 @@ +// Copyright 2015-2019 Autoware Foundation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Authors: Simon Thompson, Ryohsuke Mitsudome + +#ifndef LANELET2_EXTENSION__UTILITY__QUERY_HPP_ +#define LANELET2_EXTENSION__UTILITY__QUERY_HPP_ + +#include "lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp" +#include "lanelet2_extension/regulatory_elements/detection_area.hpp" +#include "lanelet2_extension/regulatory_elements/no_stopping_area.hpp" + +#include +#include + +#include +#include +#include + +#include +#include +#include + +namespace lanelet +{ +using TrafficSignConstPtr = std::shared_ptr; +using TrafficLightConstPtr = std::shared_ptr; +using AutowareTrafficLightConstPtr = std::shared_ptr; +using DetectionAreaConstPtr = std::shared_ptr; +using NoStoppingAreaConstPtr = std::shared_ptr; +} // namespace lanelet + +namespace lanelet +{ +namespace utils +{ +namespace query +{ +/** + * [laneletLayer converts laneletLayer into lanelet vector] + * @param ll_Map [input lanelet map] + * @return [all lanelets in the map] + */ +lanelet::ConstLanelets laneletLayer(const lanelet::LaneletMapConstPtr & ll_Map); + +/** + * [subtypeLanelets extracts Lanelet that has given subtype attribute] + * @param lls [input lanelets with various subtypes] + * @param subtype [subtype of lanelets to be retrieved (e.g. + * lanelet::AttributeValueString::Road)] + * @return [lanelets with given subtype] + */ +lanelet::ConstLanelets subtypeLanelets(const lanelet::ConstLanelets lls, const char subtype[]); + +/** + * [crosswalkLanelets extracts crosswalk lanelets] + * @param lls [input lanelets with various subtypes] + * @return [crosswalk lanelets] + */ +lanelet::ConstLanelets crosswalkLanelets(const lanelet::ConstLanelets lls); +lanelet::ConstLanelets walkwayLanelets(const lanelet::ConstLanelets lls); + +/** + * [roadLanelets extracts road lanelets] + * @param lls [input lanelets with subtype road] + * @return [road lanelets] + */ +lanelet::ConstLanelets roadLanelets(const lanelet::ConstLanelets lls); + +/** + * [shoulderLanelets extracts shoulder lanelets] + * @param lls [input lanelets with subtype shoulder] + * @return [shoulder lanelets] + */ +lanelet::ConstLanelets shoulderLanelets(const lanelet::ConstLanelets lls); +/** + * [trafficLights extracts Traffic Light regulatory element from lanelets] + * @param lanelets [input lanelets] + * @return [traffic light that are associated with input lanelets] + */ +std::vector trafficLights(const lanelet::ConstLanelets lanelets); + +/** + * [autowareTrafficLights extracts Autoware Traffic Light regulatory element + * from lanelets] + * @param lanelets [input lanelets] + * @return [autoware traffic light that are associated with input + * lanelets] + */ +std::vector autowareTrafficLights( + const lanelet::ConstLanelets lanelets); + +/** + * [detectionAreas extracts Detection Area regulatory elements from lanelets] + * @param lanelets [input lanelets] + * @return [detection areas that are associated with input lanelets] + */ +std::vector detectionAreas(const lanelet::ConstLanelets & lanelets); + +/** + * [noStoppingArea extracts NoStopping Area regulatory elements from lanelets] + * @param lanelets [input lanelets] + * @return [no stopping areas that are associated with input lanelets] + */ +std::vector noStoppingAreas( + const lanelet::ConstLanelets & lanelets); + +// query all obstacle polygons in lanelet2 map +lanelet::ConstPolygons3d getAllObstaclePolygons( + const lanelet::LaneletMapConstPtr & lanelet_map_ptr); + +// query all parking lots in lanelet2 map +lanelet::ConstPolygons3d getAllParkingLots(const lanelet::LaneletMapConstPtr & lanelet_map_ptr); + +// query all pedestrian markings in lanelet2 map +lanelet::ConstLineStrings3d getAllPedestrianMarkings( + const lanelet::LaneletMapConstPtr & lanelet_map_ptr); + +// query all parking spaces in lanelet2 map +lanelet::ConstLineStrings3d getAllParkingSpaces( + const lanelet::LaneletMapConstPtr & lanelet_map_ptr); + +// query linked parking spaces from lanelet +lanelet::ConstLineStrings3d getLinkedParkingSpaces( + const lanelet::ConstLanelet & lanelet, const lanelet::LaneletMapConstPtr & lanelet_map_ptr); +lanelet::ConstLineStrings3d getLinkedParkingSpaces( + const lanelet::ConstLanelet & lanelet, const lanelet::ConstLineStrings3d & all_parking_spaces, + const lanelet::ConstPolygons3d & all_parking_lots); +// query linked lanelets from parking space +bool getLinkedLanelet( + const lanelet::ConstLineString3d & parking_space, + const lanelet::ConstLanelets & all_road_lanelets, + const lanelet::ConstPolygons3d & all_parking_lots, lanelet::ConstLanelet * linked_lanelet); +bool getLinkedLanelet( + const lanelet::ConstLineString3d & parking_space, + const lanelet::LaneletMapConstPtr & lanelet_map_ptr, lanelet::ConstLanelet * linked_lanelet); +lanelet::ConstLanelets getLinkedLanelets( + const lanelet::ConstLineString3d & parking_space, + const lanelet::ConstLanelets & all_road_lanelets, + const lanelet::ConstPolygons3d & all_parking_lots); +lanelet::ConstLanelets getLinkedLanelets( + const lanelet::ConstLineString3d & parking_space, + const lanelet::LaneletMapConstPtr & lanelet_map_ptr); + +// get linked parking lot from lanelet +bool getLinkedParkingLot( + const lanelet::ConstLanelet & lanelet, const lanelet::ConstPolygons3d & all_parking_lots, + lanelet::ConstPolygon3d * linked_parking_lot); +bool getLinkedParkingLot( + const lanelet::ConstLineString3d & parking_space, + const lanelet::ConstPolygons3d & all_parking_lots, lanelet::ConstPolygon3d * linked_parking_lot); + +// query linked parking space from parking lot +lanelet::ConstLineStrings3d getLinkedParkingSpaces( + const lanelet::ConstPolygon3d & parking_lot, + const lanelet::ConstLineStrings3d & all_parking_spaces); +// query linked lanelets from parking lot +lanelet::ConstLanelets getLinkedLanelets( + const lanelet::ConstPolygon3d & parking_lot, const lanelet::ConstLanelets & all_road_lanelets); + +/** + * [stopLinesLanelets extracts stoplines that are associated to lanelets] + * @param lanelets [input lanelets] + * @return [stop lines that are associated with input lanelets] + */ +std::vector stopLinesLanelets(const lanelet::ConstLanelets lanelets); + +/** + * [stopLinesLanelet extracts stop lines that are associated with a given + * lanelet] + * @param ll [input lanelet] + * @return [stop lines that are associated with input lanelet] + */ +std::vector stopLinesLanelet(const lanelet::ConstLanelet ll); + +/** + * [stopSignStopLines extracts stoplines that are associated with stop signs] + * @param lanelets [input lanelets] + * @param stop_sign_id [sign id of stop sign] + * @return [array of stoplines] + */ +std::vector stopSignStopLines( + const lanelet::ConstLanelets lanelets, const std::string & stop_sign_id = "stop_sign"); + +ConstLanelets getLaneletsWithinRange( + const lanelet::ConstLanelets & lanelets, const lanelet::BasicPoint2d & search_point, + const double range); +ConstLanelets getLaneletsWithinRange( + const lanelet::ConstLanelets & lanelets, const geometry_msgs::msg::Point & search_point, + const double range); + +ConstLanelets getLaneChangeableNeighbors( + const routing::RoutingGraphPtr & graph, const ConstLanelet & lanelet); +ConstLanelets getLaneChangeableNeighbors( + const routing::RoutingGraphPtr & graph, const ConstLanelets & road_lanelets, + const geometry_msgs::msg::Point & search_point); + +ConstLanelets getAllNeighbors(const routing::RoutingGraphPtr & graph, const ConstLanelet & lanelet); +ConstLanelets getAllNeighborsLeft( + const routing::RoutingGraphPtr & graph, const ConstLanelet & lanelet); +ConstLanelets getAllNeighborsRight( + const routing::RoutingGraphPtr & graph, const ConstLanelet & lanelet); +ConstLanelets getAllNeighbors( + const routing::RoutingGraphPtr & graph, const ConstLanelets & road_lanelets, + const geometry_msgs::msg::Point & search_point); + +bool getClosestLanelet( + const ConstLanelets & lanelets, const geometry_msgs::msg::Pose & search_pose, + ConstLanelet * closest_lanelet_ptr); + +/** + * [getSucceedingLaneletSequences retrieves a sequence of lanelets after the given lanelet. + * The total length of retrieved lanelet sequence at least given length. Returned lanelet sequence + * does not include input lanelet.] + * @param graph [input lanelet routing graph] + * @param lanelet [input lanelet] + * @param length [minimum length of retrieved lanelet sequence] + * @return [lanelet sequence that follows given lanelet] + */ +std::vector getSucceedingLaneletSequences( + const routing::RoutingGraphPtr & graph, const lanelet::ConstLanelet & lanelet, + const double length); + +/** + * [getPrecedingLaneletSequences retrieves a sequence of lanelets before the given lanelet. + * The total length of retrieved lanelet sequence at least given length. Returned lanelet sequence + * does not include input lanelet.] + * @param graph [input lanelet routing graph] + * @param lanelet [input lanelet] + * @param length [minimum length of retrieved lanelet sequence] + * @return [lanelet sequence that leads to given lanelet] + */ +std::vector getPrecedingLaneletSequences( + const routing::RoutingGraphPtr & graph, const lanelet::ConstLanelet & lanelet, + const double length, const lanelet::ConstLanelets & exclude_lanelets = {}); + +} // namespace query +} // namespace utils +} // namespace lanelet + +#endif // LANELET2_EXTENSION__UTILITY__QUERY_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp new file mode 100644 index 00000000..2a5d48de --- /dev/null +++ b/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp @@ -0,0 +1,84 @@ +// Copyright 2015-2019 Autoware Foundation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Authors: Kenji Miyake, Ryohsuke Mitsudome + +#ifndef LANELET2_EXTENSION__UTILITY__UTILITIES_HPP_ +#define LANELET2_EXTENSION__UTILITY__UTILITIES_HPP_ + +#include + +#include +#include + +#include +#include + +#include + +namespace lanelet +{ +namespace utils +{ +lanelet::LineString3d generateFineCenterline( + const lanelet::ConstLanelet & lanelet_obj, const double resolution = 5.0); +lanelet::ConstLineString3d getCenterlineWithOffset( + const lanelet::ConstLanelet & lanelet_obj, const double offset, const double resolution = 5.0); + +lanelet::ConstLanelet getExpandedLanelet( + const lanelet::ConstLanelet & lanelet_obj, const double left_offset, const double right_offset); + +lanelet::ConstLanelets getExpandedLanelets( + const lanelet::ConstLanelets & lanelet_obj, const double left_offset, const double right_offset); + +/** + * @brief Apply a patch for centerline because the original implementation + * doesn't have enough quality + */ +void overwriteLaneletsCenterline( + lanelet::LaneletMapPtr lanelet_map, const double resolution = 5.0, + const bool force_overwrite = false); + +lanelet::ConstLanelets getConflictingLanelets( + const lanelet::routing::RoutingGraphConstPtr & graph, const lanelet::ConstLanelet & lanelet); + +bool lineStringWithWidthToPolygon( + const lanelet::ConstLineString3d & linestring, lanelet::ConstPolygon3d * polygon); + +bool lineStringToPolygon( + const lanelet::ConstLineString3d & linestring, lanelet::ConstPolygon3d * polygon); + +double getLaneletLength2d(const lanelet::ConstLanelet & lanelet); +double getLaneletLength3d(const lanelet::ConstLanelet & lanelet); +double getLaneletLength2d(const lanelet::ConstLanelets & lanelet_sequence); +double getLaneletLength3d(const lanelet::ConstLanelets & lanelet_sequence); + +lanelet::ArcCoordinates getArcCoordinates( + const lanelet::ConstLanelets & lanelet_sequence, const geometry_msgs::msg::Pose & pose); + +lanelet::ConstLineString3d getClosestSegment( + const lanelet::BasicPoint2d & search_pt, const lanelet::ConstLineString3d & linestring); + +lanelet::CompoundPolygon3d getPolygonFromArcLength( + const lanelet::ConstLanelets & lanelets, const double s1, const double s2); +double getLaneletAngle( + const lanelet::ConstLanelet & lanelet, const geometry_msgs::msg::Point & search_point); +bool isInLanelet( + const geometry_msgs::msg::Pose & current_pose, const lanelet::ConstLanelet & lanelet, + const double radius = 0.0); + +} // namespace utils +} // namespace lanelet + +#endif // LANELET2_EXTENSION__UTILITY__UTILITIES_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp new file mode 100644 index 00000000..9d21ac69 --- /dev/null +++ b/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp @@ -0,0 +1,253 @@ +// Copyright 2015-2019 Autoware Foundation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Authors: Simon Thompson, Ryohsuke Mitsudome + +#ifndef LANELET2_EXTENSION__VISUALIZATION__VISUALIZATION_HPP_ +#define LANELET2_EXTENSION__VISUALIZATION__VISUALIZATION_HPP_ + +#include "lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp" +#include "lanelet2_extension/regulatory_elements/no_stopping_area.hpp" +#include "lanelet2_extension/utility/query.hpp" + +#include + +#include +#include + +#include +#include + +#include +#include + +namespace lanelet +{ +namespace visualization +{ +/** + * [lanelet2Triangle converts lanelet into vector of triangles. Used for + * triangulation] + * @param ll [input lanelet] + * @param triangles [array of polygon message, each containing 3 vertices] + */ +void lanelet2Triangle( + const lanelet::ConstLanelet & ll, std::vector * triangles); + +/** + * [polygon2Triangle converts polygon into vector of triangles] + * @param polygon [input polygon] + * @param triangles [array of polygon message, each containing 3 vertices] + */ +void polygon2Triangle( + const geometry_msgs::msg::Polygon & polygon, + std::vector * triangles); + +/** + * [lanelet2Polygon converts lanelet into a polygon] + * @param ll [input lanelet] + * @param polygon [polygon message containing shape of the input lanelet.] + */ +void lanelet2Polygon(const lanelet::ConstLanelet & ll, geometry_msgs::msg::Polygon * polygon); + +/** + * [initLineStringMarker initializes marker to visualize shape of linestring] + * @param marker [output marker message] + * @param frame_id [frame id of the marker] + * @param ns [namespace of the marker] + * @param c [color of the marker] + */ +void initLineStringMarker( + visualization_msgs::msg::Marker * marker, const std::string frame_id, const std::string ns, + const std_msgs::msg::ColorRGBA c); + +/** + * [pushLineStringMarker pushes marker vertices to visualize shape of linestring] + * @param marker [output marker message] + * @param ls [input linestring] + * @param c [color of the marker] + * @param lss [thickness of the marker] + */ +void pushLineStringMarker( + visualization_msgs::msg::Marker * marker, const lanelet::ConstLineString3d & ls, + const std_msgs::msg::ColorRGBA c, const float lss = 0.1); + +/** + * [initTrafficLightTriangleMarker initializes marker to visualize shape of traffic + * lights] + * @param marker [created marker] + * @param ns [namespace of the marker] + * @param duration [lifetime of the marker] + */ +void initTrafficLightTriangleMarker( + visualization_msgs::msg::Marker * marker, const std::string ns, + const rclcpp::Duration duration = rclcpp::Duration(0, 0)); + +/** + * [pushTrafficLightTriangleMarker pushes marker vertices to visualize shape of traffic + * lights] + * @param marker [created marker] + * @param ls [linestring that represents traffic light shape] + * @param cl [color of the marker] + * @param scale [scale of the marker] + */ +void pushTrafficLightTriangleMarker( + visualization_msgs::msg::Marker * marker, const lanelet::ConstLineString3d ls, + const std_msgs::msg::ColorRGBA cl, const double scale = 1.0); + +/** + * [laneletsBoundaryAsMarkerArray create marker array to visualize shape of + * boundaries of lanelets] + * @param lanelets [input lanelets] + * @param c [color of the boundary] + * @param viz_centerline [flag to visualize centerline or not] + * @return [created marker array] + */ +visualization_msgs::msg::MarkerArray laneletsBoundaryAsMarkerArray( + const lanelet::ConstLanelets & lanelets, const std_msgs::msg::ColorRGBA c, + const bool viz_centerline, const std::string & additional_namespace = ""); +/** + * [laneletsAsTriangleMarkerArray create marker array to visualize shape of the + * lanelet] + * @param ns [namespace of the marker] + * @param lanelets [input lanelets] + * @param c [color of the marker] + * @return [created marker] + */ +visualization_msgs::msg::MarkerArray laneletsAsTriangleMarkerArray( + const std::string ns, const lanelet::ConstLanelets & lanelets, const std_msgs::msg::ColorRGBA c); + +/** + * [laneletDirectionAsMarkerArray create marker array to visualize direction of + * the lanelet] + * @param lanelets [input lanelets] + * @return [created marker array] + */ +visualization_msgs::msg::MarkerArray laneletDirectionAsMarkerArray( + const lanelet::ConstLanelets lanelets, const std::string & additional_namespace = ""); + +/** + * [lineStringsAsMarkerArray creates marker array to visualize shape of + * linestrings] + * @param line_strings [input linestrings] + * @param name_space [namespace of the marker] + * @param c [color of the marker] + * @param lss [thickness of the marker] + * @return [created marker array] + */ +visualization_msgs::msg::MarkerArray lineStringsAsMarkerArray( + const std::vector line_strings, const std::string name_space, + const std_msgs::msg::ColorRGBA c, const double lss); + +/** + * [autowareTrafficLightsAsMarkerArray creates marker array to visualize traffic + * lights] + * @param tl_reg_elems [traffic light regulatory elements] + * @param c [color of the marker] + * @param duration [lifetime of the marker] + * @return [created marker array] + */ +visualization_msgs::msg::MarkerArray autowareTrafficLightsAsMarkerArray( + const std::vector tl_reg_elems, + const std_msgs::msg::ColorRGBA c, const rclcpp::Duration duration = rclcpp::Duration(0, 0), + const double scale = 1.0); + +/** + * [generateTrafficLightIdMaker creates marker array to visualize traffic id + * lights] + * @param tl_reg_elems [traffic light regulatory elements] + * @param c [color of the marker] + * @param duration [lifetime of the marker] + * @return [created marker array] + */ +visualization_msgs::msg::MarkerArray generateTrafficLightIdMaker( + const std::vector tl_reg_elems, + const std_msgs::msg::ColorRGBA c, const rclcpp::Duration duration = rclcpp::Duration(0, 0), + const double scale = 1.0); + +/** + * [trafficLightsAsTriangleMarkerArray creates marker array to visualize shape + * of traffic lights] + * @param tl_reg_elems [traffic light regulatory elements] + * @param c [color of the marker] + * @param duration [lifetime of the marker] + * @return [created marker array] + */ +visualization_msgs::msg::MarkerArray trafficLightsAsTriangleMarkerArray( + const std::vector tl_reg_elems, const std_msgs::msg::ColorRGBA c, + const rclcpp::Duration duration = rclcpp::Duration(0, 0), const double scale = 1.0); + +/** + * [detectionAreasAsMarkerArray creates marker array to visualize detection areas] + * @param da_reg_elems [detection area regulatory elements] + * @param c [color of the marker] + * @param duration [lifetime of the marker] + */ +visualization_msgs::msg::MarkerArray detectionAreasAsMarkerArray( + const std::vector & da_reg_elems, + const std_msgs::msg::ColorRGBA c, const rclcpp::Duration duration = rclcpp::Duration(0, 0)); + +/** + * [noStoppingAreasAsMarkerArray creates marker array to visualize detection areas] + * @param no_reg_elems [mp stopping area regulatory elements] + * @param c [color of the marker] + * @param duration [lifetime of the marker] + */ +visualization_msgs::msg::MarkerArray noStoppingAreasAsMarkerArray( + const std::vector & no_reg_elems, + const std_msgs::msg::ColorRGBA c, const rclcpp::Duration duration = rclcpp::Duration(0, 0)); + +/** + * [pedestrianMarkingsAsMarkerArray creates marker array to visualize pedestrian markings] + * @param pedestrian_markings [pedestrian marking polygon] + * @param c [color of the marker] + */ +visualization_msgs::msg::MarkerArray pedestrianMarkingsAsMarkerArray( + const lanelet::ConstLineStrings3d & pedestrian_markings, const std_msgs::msg::ColorRGBA & c); + +/** + * [parkingLotsAsMarkerArray creates marker array to visualize parking lots] + * @param parking_lots [parking lot polygon] + * @param c [color of the marker] + */ +visualization_msgs::msg::MarkerArray parkingLotsAsMarkerArray( + const lanelet::ConstPolygons3d & parking_lots, const std_msgs::msg::ColorRGBA & c); + +/** + * [parkingSpacesAsMarkerArray creates marker array to visualize parking spaces] + * @param parking_spaces [parking space line string] + * @param c [color of the marker] + */ +visualization_msgs::msg::MarkerArray parkingSpacesAsMarkerArray( + const lanelet::ConstLineStrings3d & parking_spaces, const std_msgs::msg::ColorRGBA & c); + +/** + * [detectionAreasAsMarkerArray creates marker array to visualize lanelet_id] + * @param road_lanelets [road lanelets] + * @param c [color of the marker] + * @param duration [lifetime of the marker] + * @param scale [scale of the marker] + * @return visualization_msgs::msg::MarkerArray + */ +visualization_msgs::msg::MarkerArray generateLaneletIdMarker( + const lanelet::ConstLanelets road_lanelets, const std_msgs::msg::ColorRGBA c, + const double scale = 0.5); + +visualization_msgs::msg::MarkerArray obstaclePolygonsAsMarkerArray( + const lanelet::ConstPolygons3d & obstacle_polygons, const std_msgs::msg::ColorRGBA & c); + +} // namespace visualization +} // namespace lanelet + +#endif // LANELET2_EXTENSION__VISUALIZATION__VISUALIZATION_HPP_ diff --git a/tmp/lanelet2_extension/lib/autoware_osm_parser.cpp b/tmp/lanelet2_extension/lib/autoware_osm_parser.cpp new file mode 100644 index 00000000..984147a4 --- /dev/null +++ b/tmp/lanelet2_extension/lib/autoware_osm_parser.cpp @@ -0,0 +1,88 @@ +// Copyright 2015-2019 Autoware Foundation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Authors: Ryohsuke Mitsudome + +#include "lanelet2_extension/io/autoware_osm_parser.hpp" + +#include +#include +#include +#include + +#include +#include + +namespace lanelet +{ +namespace io_handlers +{ +std::unique_ptr AutowareOsmParser::parse( + const std::string & filename, ErrorMessages & errors) const +{ + auto map = OsmParser::parse(filename, errors); + + // overwrite x and y values if there are local_x, local_y tags + for (Point3d point : map->pointLayer) { + if (point.hasAttribute("local_x")) { + point.x() = point.attribute("local_x").asDouble().value(); + } + if (point.hasAttribute("local_y")) { + point.y() = point.attribute("local_y").asDouble().value(); + } + } + + // rerun align function in just in case + for (Lanelet & lanelet : map->laneletLayer) { + LineString3d new_left, new_right; + std::tie(new_left, new_right) = geometry::align(lanelet.leftBound(), lanelet.rightBound()); + lanelet.setLeftBound(new_left); + lanelet.setRightBound(new_right); + } + + return map; +} + +namespace +{ +RegisterParser regParser; +} + +void AutowareOsmParser::parseVersions( + const std::string & filename, std::string * format_version, std::string * map_version) +{ + if (format_version == nullptr || map_version == nullptr) { + std::cerr << __FUNCTION__ << ": either format_version or map_version is null pointer!"; + return; + } + + pugi::xml_document doc; + auto result = doc.load_file(filename.c_str()); + if (!result) { + throw lanelet::ParseError( + std::string("Errors occurred while parsing osm file: ") + result.description()); + } + + auto osmNode = doc.child("osm"); + auto metainfo = osmNode.child("MetaInfo"); + if (metainfo.attribute("format_version")) { + *format_version = metainfo.attribute("format_version").value(); + } + if (metainfo.attribute("map_version")) { + *map_version = metainfo.attribute("map_version").value(); + } +} + +} // namespace io_handlers +} // namespace lanelet diff --git a/tmp/lanelet2_extension/lib/autoware_traffic_light.cpp b/tmp/lanelet2_extension/lib/autoware_traffic_light.cpp new file mode 100644 index 00000000..d7c7b503 --- /dev/null +++ b/tmp/lanelet2_extension/lib/autoware_traffic_light.cpp @@ -0,0 +1,151 @@ +// Copyright 2015-2019 Autoware Foundation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Authors: Ryohsuke Mitsudome + +#include "lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp" + +#include + +#include + +#include +#include +#include +#include + +namespace lanelet +{ +namespace autoware +{ +namespace +{ +template +bool findAndErase(const T & primitive, RuleParameters * member) +{ + if (member == nullptr) { + std::cerr << __FUNCTION__ << ": member is null pointer"; + return false; + } + auto it = std::find(member->begin(), member->end(), RuleParameter(primitive)); + if (it == member->end()) { + return false; + } + member->erase(it); + return true; +} + +template +RuleParameters toRuleParameters(const std::vector & primitives) +{ + auto cast_func = [](const auto & elem) { return static_cast(elem); }; + return utils::transform(primitives, cast_func); +} + +template <> +RuleParameters toRuleParameters(const std::vector & primitives) +{ + auto cast_func = [](const auto & elem) { return elem.asRuleParameter(); }; + return utils::transform(primitives, cast_func); +} + +LineStringsOrPolygons3d getLsOrPoly(const RuleParameterMap & paramsMap, RoleName role) +{ + auto params = paramsMap.find(role); + if (params == paramsMap.end()) { + return {}; + } + LineStringsOrPolygons3d result; + for (auto & param : params->second) { + auto l = boost::get(¶m); + if (l != nullptr) { + result.push_back(*l); + } + auto p = boost::get(¶m); + if (p != nullptr) { + result.push_back(*p); + } + } + return result; +} + +[[maybe_unused]] ConstLineStringsOrPolygons3d getConstLsOrPoly( + const RuleParameterMap & params, RoleName role) +{ + auto cast_func = [](auto & lsOrPoly) { + return static_cast(lsOrPoly); + }; + return utils::transform(getLsOrPoly(params, role), cast_func); +} + +[[maybe_unused]] RegulatoryElementDataPtr constructAutowareTrafficLightData( + Id id, const AttributeMap & attributes, const LineStringsOrPolygons3d & trafficLights, + const Optional & stopLine, const LineStrings3d & lightBulbs) +{ + RuleParameterMap rpm = {{RoleNameString::Refers, toRuleParameters(trafficLights)}}; + + if (!!stopLine) { + RuleParameters rule_parameters = {*stopLine}; + rpm.insert(std::make_pair(RoleNameString::RefLine, rule_parameters)); + } + if (!lightBulbs.empty()) { + rpm.insert(std::make_pair(AutowareRoleNameString::LightBulbs, toRuleParameters(lightBulbs))); + } + + auto data = std::make_shared(id, rpm, attributes); + data->attributes[AttributeName::Type] = AttributeValueString::RegulatoryElement; + data->attributes[AttributeName::Subtype] = AttributeValueString::TrafficLight; + return data; +} +} // namespace + +constexpr const char AutowareRoleNameString::LightBulbs[]; + +AutowareTrafficLight::AutowareTrafficLight(const RegulatoryElementDataPtr & data) +: TrafficLight(data) +{ +} + +AutowareTrafficLight::AutowareTrafficLight( + Id id, const AttributeMap & attributes, const LineStringsOrPolygons3d & trafficLights, + const Optional & stopLine, const LineStrings3d & lightBulbs) +: TrafficLight(id, attributes, trafficLights, stopLine) +{ + for (const auto & lightBulb : lightBulbs) { + addLightBulbs(lightBulb); + } +} + +ConstLineStrings3d AutowareTrafficLight::lightBulbs() const +{ + return getParameters(AutowareRoleNameString::LightBulbs); +} + +void AutowareTrafficLight::addLightBulbs(const LineStringOrPolygon3d & primitive) +{ + parameters()[AutowareRoleNameString::LightBulbs].emplace_back(primitive.asRuleParameter()); +} + +bool AutowareTrafficLight::removeLightBulbs(const LineStringOrPolygon3d & primitive) +{ + return findAndErase( + primitive.asRuleParameter(), ¶meters().find(AutowareRoleNameString::LightBulbs)->second); +} + +#if __cplusplus < 201703L +constexpr char AutowareTrafficLight::RuleName[]; // instantiate string in cpp file +#endif + +} // namespace autoware +} // namespace lanelet diff --git a/tmp/lanelet2_extension/lib/detection_area.cpp b/tmp/lanelet2_extension/lib/detection_area.cpp new file mode 100644 index 00000000..b9cf5650 --- /dev/null +++ b/tmp/lanelet2_extension/lib/detection_area.cpp @@ -0,0 +1,157 @@ +// Copyright 2015-2019 Autoware Foundation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Authors: Ryohsuke Mitsudome + +#include "lanelet2_extension/regulatory_elements/detection_area.hpp" + +#include + +#include + +#include +#include +#include +#include + +namespace lanelet +{ +namespace autoware +{ +namespace +{ +template +bool findAndErase(const T & primitive, RuleParameters * member) +{ + if (member == nullptr) { + std::cerr << __FUNCTION__ << ": member is null pointer"; + return false; + } + auto it = std::find(member->begin(), member->end(), RuleParameter(primitive)); + if (it == member->end()) { + return false; + } + member->erase(it); + return true; +} + +template +RuleParameters toRuleParameters(const std::vector & primitives) +{ + auto cast_func = [](const auto & elem) { return static_cast(elem); }; + return utils::transform(primitives, cast_func); +} + +// template <> +// RuleParameters toRuleParameters(const std::vector& primitives) +// { +// auto cast_func = [](const auto& elem) { return elem.asRuleParameter(); }; +// return utils::transform(primitives, cast_func); +// } + +Polygons3d getPoly(const RuleParameterMap & paramsMap, RoleName role) +{ + auto params = paramsMap.find(role); + if (params == paramsMap.end()) { + return {}; + } + + Polygons3d result; + for (auto & param : params->second) { + auto p = boost::get(¶m); + if (p != nullptr) { + result.push_back(*p); + } + } + return result; +} + +ConstPolygons3d getConstPoly(const RuleParameterMap & params, RoleName role) +{ + auto cast_func = [](auto & poly) { return static_cast(poly); }; + return utils::transform(getPoly(params, role), cast_func); +} + +RegulatoryElementDataPtr constructDetectionAreaData( + Id id, const AttributeMap & attributes, const Polygons3d & detectionAreas, + const LineString3d & stopLine) +{ + RuleParameterMap rpm = {{RoleNameString::Refers, toRuleParameters(detectionAreas)}}; + + RuleParameters rule_parameters = {stopLine}; + rpm.insert(std::make_pair(RoleNameString::RefLine, rule_parameters)); + + auto data = std::make_shared(id, rpm, attributes); + data->attributes[AttributeName::Type] = AttributeValueString::RegulatoryElement; + data->attributes[AttributeName::Subtype] = "detection_area"; + return data; +} +} // namespace + +DetectionArea::DetectionArea(const RegulatoryElementDataPtr & data) : RegulatoryElement(data) +{ + if (getConstPoly(data->parameters, RoleName::Refers).empty()) { + throw InvalidInputError("No detection area defined!"); + } + if (getParameters(RoleName::RefLine).size() != 1) { + throw InvalidInputError("There must be exactly one stopline defined!"); + } +} + +DetectionArea::DetectionArea( + Id id, const AttributeMap & attributes, const Polygons3d & detectionAreas, + const LineString3d & stopLine) +: DetectionArea(constructDetectionAreaData(id, attributes, detectionAreas, stopLine)) +{ +} + +ConstPolygons3d DetectionArea::detectionAreas() const +{ + return getConstPoly(parameters(), RoleName::Refers); +} +Polygons3d DetectionArea::detectionAreas() { return getPoly(parameters(), RoleName::Refers); } + +void DetectionArea::addDetectionArea(const Polygon3d & primitive) +{ + parameters()["detection_area"].emplace_back(primitive); +} + +bool DetectionArea::removeDetectionArea(const Polygon3d & primitive) +{ + return findAndErase(primitive, ¶meters().find("detection_area")->second); +} + +ConstLineString3d DetectionArea::stopLine() const +{ + return getParameters(RoleName::RefLine).front(); +} + +LineString3d DetectionArea::stopLine() +{ + return getParameters(RoleName::RefLine).front(); +} + +void DetectionArea::setStopLine(const LineString3d & stopLine) +{ + parameters()[RoleName::RefLine] = {stopLine}; +} + +void DetectionArea::removeStopLine() { parameters()[RoleName::RefLine] = {}; } + +#if __cplusplus < 201703L +constexpr char DetectionArea::RuleName[]; // instantiate string in cpp file +#endif + +} // namespace autoware +} // namespace lanelet diff --git a/tmp/lanelet2_extension/lib/message_conversion.cpp b/tmp/lanelet2_extension/lib/message_conversion.cpp new file mode 100644 index 00000000..bc58e403 --- /dev/null +++ b/tmp/lanelet2_extension/lib/message_conversion.cpp @@ -0,0 +1,194 @@ +// Copyright 2015-2019 Autoware Foundation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Authors: Simon Thompson, Ryohsuke Mitsudome + +#include "lanelet2_extension/utility/message_conversion.hpp" + +#include "lanelet2_extension/projection/mgrs_projector.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace lanelet +{ +namespace utils +{ +namespace conversion +{ +void toBinMsg(const lanelet::LaneletMapPtr & map, autoware_auto_mapping_msgs::msg::HADMapBin * msg) +{ + if (msg == nullptr) { + std::cerr << __FUNCTION__ << "msg is null pointer!"; + return; + } + + std::stringstream ss; + boost::archive::binary_oarchive oa(ss); + oa << *map; + auto id_counter = lanelet::utils::getId(); + oa << id_counter; + + std::string data_str(ss.str()); + + msg->data.clear(); + msg->data.assign(data_str.begin(), data_str.end()); +} + +void fromBinMsg(const autoware_auto_mapping_msgs::msg::HADMapBin & msg, lanelet::LaneletMapPtr map) +{ + if (!map) { + std::cerr << __FUNCTION__ << ": map is null pointer!"; + return; + } + + std::string data_str; + data_str.assign(msg.data.begin(), msg.data.end()); + + std::stringstream ss; + ss << data_str; + boost::archive::binary_iarchive oa(ss); + oa >> *map; + lanelet::Id id_counter; + oa >> id_counter; + lanelet::utils::registerId(id_counter); + // *map = std::move(laneletMap); +} + +void fromBinMsg( + const autoware_auto_mapping_msgs::msg::HADMapBin & msg, lanelet::LaneletMapPtr map, + lanelet::traffic_rules::TrafficRulesPtr * traffic_rules, + lanelet::routing::RoutingGraphPtr * routing_graph) +{ + fromBinMsg(msg, map); + *traffic_rules = lanelet::traffic_rules::TrafficRulesFactory::create( + lanelet::Locations::Germany, lanelet::Participants::Vehicle); + *routing_graph = lanelet::routing::RoutingGraph::build(*map, **traffic_rules); +} + +void toGeomMsgPt(const geometry_msgs::msg::Point32 & src, geometry_msgs::msg::Point * dst) +{ + if (dst == nullptr) { + std::cerr << __FUNCTION__ << "pointer is null!"; + return; + } + dst->x = src.x; + dst->y = src.y; + dst->z = src.z; +} +void toGeomMsgPt(const Eigen::Vector3d & src, geometry_msgs::msg::Point * dst) +{ + if (dst == nullptr) { + std::cerr << __FUNCTION__ << "pointer is null!"; + return; + } + dst->x = src.x(); + dst->y = src.y(); + dst->z = src.z(); +} +void toGeomMsgPt(const lanelet::ConstPoint3d & src, geometry_msgs::msg::Point * dst) +{ + if (dst == nullptr) { + std::cerr << __FUNCTION__ << "pointer is null!"; + return; + } + dst->x = src.x(); + dst->y = src.y(); + dst->z = src.z(); +} +void toGeomMsgPt(const lanelet::ConstPoint2d & src, geometry_msgs::msg::Point * dst) +{ + if (dst == nullptr) { + std::cerr << __FUNCTION__ << "pointer is null!" << std::endl; + return; + } + dst->x = src.x(); + dst->y = src.y(); + dst->z = 0; +} + +void toGeomMsgPt32(const Eigen::Vector3d & src, geometry_msgs::msg::Point32 * dst) +{ + if (dst == nullptr) { + std::cerr << __FUNCTION__ << "pointer is null!" << std::endl; + return; + } + dst->x = src.x(); + dst->y = src.y(); + dst->z = src.z(); +} + +geometry_msgs::msg::Point toGeomMsgPt(const geometry_msgs::msg::Point32 & src) +{ + geometry_msgs::msg::Point dst; + toGeomMsgPt(src, &dst); + return dst; +} +geometry_msgs::msg::Point toGeomMsgPt(const Eigen::Vector3d & src) +{ + geometry_msgs::msg::Point dst; + toGeomMsgPt(src, &dst); + return dst; +} +geometry_msgs::msg::Point toGeomMsgPt(const lanelet::ConstPoint3d & src) +{ + geometry_msgs::msg::Point dst; + toGeomMsgPt(src, &dst); + return dst; +} +geometry_msgs::msg::Point toGeomMsgPt(const lanelet::ConstPoint2d & src) +{ + geometry_msgs::msg::Point dst; + toGeomMsgPt(src, &dst); + return dst; +} + +lanelet::ConstPoint3d toLaneletPoint(const geometry_msgs::msg::Point & src) +{ + lanelet::ConstPoint3d dst; + toLaneletPoint(src, &dst); + return dst; +} + +void toLaneletPoint(const geometry_msgs::msg::Point & src, lanelet::ConstPoint3d * dst) +{ + *dst = lanelet::Point3d(lanelet::InvalId, src.x, src.y, src.z); +} + +void toGeomMsgPoly(const lanelet::ConstPolygon3d & ll_poly, geometry_msgs::msg::Polygon * geom_poly) +{ + geom_poly->points.clear(); + geom_poly->points.reserve(ll_poly.size()); + for (const auto & ll_pt : ll_poly) { + geometry_msgs::msg::Point32 geom_pt32; + utils::conversion::toGeomMsgPt32(ll_pt.basicPoint(), &geom_pt32); + geom_poly->points.push_back(geom_pt32); + } +} + +} // namespace conversion +} // namespace utils +} // namespace lanelet diff --git a/tmp/lanelet2_extension/lib/mgrs_projector.cpp b/tmp/lanelet2_extension/lib/mgrs_projector.cpp new file mode 100644 index 00000000..3caa85af --- /dev/null +++ b/tmp/lanelet2_extension/lib/mgrs_projector.cpp @@ -0,0 +1,130 @@ +// Copyright 2015-2019 Autoware Foundation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Authors: Simon Thompson, Ryohsuke Mitsudome + +#include "lanelet2_extension/projection/mgrs_projector.hpp" + +#include +#include +#include +#include +#include + +namespace lanelet +{ +namespace projection +{ +MGRSProjector::MGRSProjector(Origin origin) : Projector(origin) {} + +BasicPoint3d MGRSProjector::forward(const GPSPoint & gps) const +{ + BasicPoint3d mgrs_point(forward(gps, 0)); + return mgrs_point; +} + +BasicPoint3d MGRSProjector::forward(const GPSPoint & gps, const int precision) const +{ + std::string prev_projected_grid = projected_grid_; + + BasicPoint3d mgrs_point{0., 0., gps.ele}; + BasicPoint3d utm_point{0., 0., gps.ele}; + int zone{}; + bool northp{}; + std::string mgrs_code; + + try { + GeographicLib::UTMUPS::Forward(gps.lat, gps.lon, zone, northp, utm_point.x(), utm_point.y()); + GeographicLib::MGRS::Forward( + zone, northp, utm_point.x(), utm_point.y(), gps.lat, precision, mgrs_code); + } catch (const GeographicLib::GeographicErr & err) { + std::cerr << err.what() << std::endl; + return mgrs_point; + } + + // get mgrs values from utm values + mgrs_point.x() = fmod(utm_point.x(), 1e5); + mgrs_point.y() = fmod(utm_point.y(), 1e5); + projected_grid_ = mgrs_code; + + if (!prev_projected_grid.empty() && prev_projected_grid != projected_grid_) { + std::cerr << "Projected MGRS Grid changed from last projection. Projected point" + "might be far away from previously projected point." + << std::endl + << "You may want to use different projector."; + } + + return mgrs_point; +} + +GPSPoint MGRSProjector::reverse(const BasicPoint3d & mgrs_point) const +{ + GPSPoint gps{0., 0., 0.}; + // reverse function cannot be used if mgrs_code_ is not set + if (isMGRSCodeSet()) { + gps = reverse(mgrs_point, mgrs_code_); + } else if (!projected_grid_.empty()) { + gps = reverse(mgrs_point, projected_grid_); + } else { + std::cerr << "cannot run reverse operation if mgrs code is not set in projector." << std::endl + << "use setMGRSCode function or explicitly give mgrs code as an argument."; + } + return gps; +} + +GPSPoint MGRSProjector::reverse( + const BasicPoint3d & mgrs_point, const std::string & mgrs_code) const +{ + GPSPoint gps{0., 0., mgrs_point.z()}; + BasicPoint3d utm_point{0., 0., gps.ele}; + + int zone{}; + int prec{}; + bool northp{}; + try { + GeographicLib::MGRS::Reverse( + mgrs_code, zone, northp, utm_point.x(), utm_point.y(), prec, false); + utm_point.x() += fmod(mgrs_point.x(), pow(10, 5 - prec)); + utm_point.y() += fmod(mgrs_point.y(), pow(10, 5 - prec)); + GeographicLib::UTMUPS::Reverse(zone, northp, utm_point.x(), utm_point.y(), gps.lat, gps.lon); + } catch (const GeographicLib::GeographicErr & err) { + std::cerr << "Failed to convert from MGRS to WGS"; + return gps; + } + + return gps; +} + +void MGRSProjector::setMGRSCode(const std::string & mgrs_code) { mgrs_code_ = mgrs_code; } + +void MGRSProjector::setMGRSCode(const GPSPoint & gps, const int precision) +{ + BasicPoint3d utm_point{0., 0., gps.ele}; + int zone{}; + bool northp{}; + std::string mgrs_code; + + try { + GeographicLib::UTMUPS::Forward(gps.lat, gps.lon, zone, northp, utm_point.x(), utm_point.y()); + GeographicLib::MGRS::Forward( + zone, northp, utm_point.x(), utm_point.y(), gps.lat, precision, mgrs_code); + } catch (const GeographicLib::GeographicErr & err) { + std::cerr << err.what() << std::endl; + } + + setMGRSCode(mgrs_code); +} + +} // namespace projection +} // namespace lanelet diff --git a/tmp/lanelet2_extension/lib/no_stopping_area.cpp b/tmp/lanelet2_extension/lib/no_stopping_area.cpp new file mode 100644 index 00000000..517da9fb --- /dev/null +++ b/tmp/lanelet2_extension/lib/no_stopping_area.cpp @@ -0,0 +1,157 @@ +// Copyright 2021 Tier IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lanelet2_extension/regulatory_elements/no_stopping_area.hpp" + +#include + +#include + +#include +#include +#include +#include + +namespace lanelet +{ +namespace autoware +{ +namespace +{ +template +bool findAndErase(const T & primitive, RuleParameters * member) +{ + if (member == nullptr) { + std::cerr << __FUNCTION__ << ": member is null pointer"; + return false; + } + auto it = std::find(member->begin(), member->end(), RuleParameter(primitive)); + if (it == member->end()) { + return false; + } + member->erase(it); + return true; +} + +template +Optional tryGetFront(const std::vector & vec) +{ + if (vec.empty()) { + return {}; + } + return vec.front(); +} + +template +RuleParameters toRuleParameters(const std::vector & primitives) +{ + auto cast_func = [](const auto & elem) { return static_cast(elem); }; + return utils::transform(primitives, cast_func); +} + +Polygons3d getPoly(const RuleParameterMap & paramsMap, RoleName role) +{ + auto params = paramsMap.find(role); + if (params == paramsMap.end()) { + return {}; + } + + Polygons3d result; + for (auto & param : params->second) { + auto p = boost::get(¶m); + if (p != nullptr) { + result.push_back(*p); + } + } + return result; +} + +ConstPolygons3d getConstPoly(const RuleParameterMap & params, RoleName role) +{ + auto cast_func = [](auto & poly) { return static_cast(poly); }; + return utils::transform(getPoly(params, role), cast_func); +} + +RegulatoryElementDataPtr constructNoStoppingAreaData( + Id id, const AttributeMap & attributes, const Polygons3d & noStoppingAreas, + const Optional & stopLine = {}) +{ + RuleParameterMap rpm = {{RoleNameString::Refers, toRuleParameters(noStoppingAreas)}}; + if (!!stopLine) { + rpm.insert({RoleNameString::RefLine, {*stopLine}}); + } + + auto data = std::make_shared(id, std::move(rpm), attributes); + data->attributes[AttributeName::Type] = AttributeValueString::RegulatoryElement; + data->attributes[AttributeName::Subtype] = "no_stopping_area"; + return data; +} +} // namespace + +NoStoppingArea::NoStoppingArea(const RegulatoryElementDataPtr & data) : RegulatoryElement(data) +{ + if (getConstPoly(data->parameters, RoleName::Refers).empty()) { + throw InvalidInputError("no stopping area defined!"); + } + if (getParameters(RoleName::RefLine).size() > 1) { + throw InvalidInputError("There can not exist more than one stop line!"); + } +} + +NoStoppingArea::NoStoppingArea( + Id id, const AttributeMap & attributes, const Polygons3d & no_stopping_areas, + const Optional & stopLine) +: NoStoppingArea(constructNoStoppingAreaData(id, attributes, no_stopping_areas, stopLine)) +{ +} + +ConstPolygons3d NoStoppingArea::noStoppingAreas() const +{ + return getConstPoly(parameters(), RoleName::Refers); +} +Polygons3d NoStoppingArea::noStoppingAreas() { return getPoly(parameters(), RoleName::Refers); } + +void NoStoppingArea::addNoStoppingArea(const Polygon3d & primitive) +{ + parameters()["no_stopping_area"].emplace_back(primitive); +} + +bool NoStoppingArea::removeNoStoppingArea(const Polygon3d & primitive) +{ + return findAndErase(primitive, ¶meters().find("no_stopping_area")->second); +} + +Optional NoStoppingArea::stopLine() const +{ + return tryGetFront(getParameters(RoleName::RefLine)); +} + +Optional NoStoppingArea::stopLine() +{ + return tryGetFront(getParameters(RoleName::RefLine)); +} + +void NoStoppingArea::setStopLine(const LineString3d & stopLine) +{ + parameters()[RoleName::RefLine] = {stopLine}; +} + +void NoStoppingArea::removeStopLine() { parameters()[RoleName::RefLine] = {}; } + +#if __cplusplus < 201703L +constexpr char NoStoppingArea::RuleName[]; // instantiate string in cpp file +#endif + +} // namespace autoware +} // namespace lanelet diff --git a/tmp/lanelet2_extension/lib/query.cpp b/tmp/lanelet2_extension/lib/query.cpp new file mode 100644 index 00000000..9cca2eb7 --- /dev/null +++ b/tmp/lanelet2_extension/lib/query.cpp @@ -0,0 +1,847 @@ +// Copyright 2015-2019 Autoware Foundation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Authors: Simon Thompson, Ryohsuke Mitsudome + +#include "lanelet2_extension/utility/query.hpp" + +#include "lanelet2_extension/utility/message_conversion.hpp" +#include "lanelet2_extension/utility/utilities.hpp" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using lanelet::utils::to2D; +namespace +{ +double getAngleDifference(const double angle1, const double angle2) +{ + Eigen::Vector2d vec1, vec2; + vec1 << std::cos(angle1), std::sin(angle1); + vec2 << std::cos(angle2), std::sin(angle2); + const double diff_angle = std::acos(vec1.dot(vec2)); + return std::fabs(diff_angle); +} + +} // namespace + +namespace lanelet +{ +namespace utils +{ +// returns all lanelets in laneletLayer - don't know how to convert +// PrimitiveLayer -> std::vector +lanelet::ConstLanelets query::laneletLayer(const lanelet::LaneletMapConstPtr & ll_map) +{ + lanelet::ConstLanelets lanelets; + if (!ll_map) { + std::cerr << "No map received!"; + return lanelets; + } + + for (auto li = ll_map->laneletLayer.begin(); li != ll_map->laneletLayer.end(); li++) { + lanelets.push_back(*li); + } + + return lanelets; +} + +lanelet::ConstLanelets query::subtypeLanelets( + const lanelet::ConstLanelets lls, const char subtype[]) +{ + lanelet::ConstLanelets subtype_lanelets; + + for (auto li = lls.begin(); li != lls.end(); li++) { + lanelet::ConstLanelet ll = *li; + + if (ll.hasAttribute(lanelet::AttributeName::Subtype)) { + lanelet::Attribute attr = ll.attribute(lanelet::AttributeName::Subtype); + if (attr.value() == subtype) { + subtype_lanelets.push_back(ll); + } + } + } + + return subtype_lanelets; +} + +lanelet::ConstLanelets query::crosswalkLanelets(const lanelet::ConstLanelets lls) +{ + return query::subtypeLanelets(lls, lanelet::AttributeValueString::Crosswalk); +} + +lanelet::ConstLanelets query::walkwayLanelets(const lanelet::ConstLanelets lls) +{ + return query::subtypeLanelets(lls, lanelet::AttributeValueString::Walkway); +} + +lanelet::ConstLanelets query::roadLanelets(const lanelet::ConstLanelets lls) +{ + return query::subtypeLanelets(lls, lanelet::AttributeValueString::Road); +} + +lanelet::ConstLanelets query::shoulderLanelets(const lanelet::ConstLanelets lls) +{ + return query::subtypeLanelets(lls, "road_shoulder"); +} + +std::vector query::trafficLights( + const lanelet::ConstLanelets lanelets) +{ + std::vector tl_reg_elems; + + for (auto i = lanelets.begin(); i != lanelets.end(); i++) { + lanelet::ConstLanelet ll = *i; + std::vector ll_tl_re = + ll.regulatoryElementsAs(); + + // insert unique tl into array + for (auto tli = ll_tl_re.begin(); tli != ll_tl_re.end(); tli++) { + lanelet::TrafficLightConstPtr tl_ptr = *tli; + lanelet::Id id = tl_ptr->id(); + bool unique_id = true; + for (auto ii = tl_reg_elems.begin(); ii != tl_reg_elems.end(); ii++) { + if (id == (*ii)->id()) { + unique_id = false; + break; + } + } + if (unique_id) { + tl_reg_elems.push_back(tl_ptr); + } + } + } + return tl_reg_elems; +} + +std::vector query::autowareTrafficLights( + const lanelet::ConstLanelets lanelets) +{ + std::vector tl_reg_elems; + + for (auto i = lanelets.begin(); i != lanelets.end(); i++) { + lanelet::ConstLanelet ll = *i; + std::vector ll_tl_re = + ll.regulatoryElementsAs(); + + // insert unique tl into array + for (auto tli = ll_tl_re.begin(); tli != ll_tl_re.end(); tli++) { + lanelet::AutowareTrafficLightConstPtr tl_ptr = *tli; + lanelet::Id id = tl_ptr->id(); + bool unique_id = true; + + for (auto ii = tl_reg_elems.begin(); ii != tl_reg_elems.end(); ii++) { + if (id == (*ii)->id()) { + unique_id = false; + break; + } + } + + if (unique_id) { + tl_reg_elems.push_back(tl_ptr); + } + } + } + return tl_reg_elems; +} + +std::vector query::detectionAreas( + const lanelet::ConstLanelets & lanelets) +{ + std::vector da_reg_elems; + + for (auto i = lanelets.begin(); i != lanelets.end(); i++) { + lanelet::ConstLanelet ll = *i; + std::vector ll_da_re = + ll.regulatoryElementsAs(); + + // insert unique tl into array + for (const auto & da_ptr : ll_da_re) { + lanelet::Id id = da_ptr->id(); + bool unique_id = true; + + for (auto ii = da_reg_elems.begin(); ii != da_reg_elems.end(); ii++) { + if (id == (*ii)->id()) { + unique_id = false; + break; + } + } + + if (unique_id) { + da_reg_elems.push_back(da_ptr); + } + } + } + return da_reg_elems; +} + +std::vector query::noStoppingAreas( + const lanelet::ConstLanelets & lanelets) +{ + std::vector no_reg_elems; + + for (auto i = lanelets.begin(); i != lanelets.end(); i++) { + lanelet::ConstLanelet ll = *i; + std::vector ll_no_re = + ll.regulatoryElementsAs(); + + // insert unique tl into array + for (const auto & no_ptr : ll_no_re) { + lanelet::Id id = no_ptr->id(); + bool unique_id = true; + + for (auto ii = no_reg_elems.begin(); ii != no_reg_elems.end(); ii++) { + if (id == (*ii)->id()) { + unique_id = false; + break; + } + } + + if (unique_id) { + no_reg_elems.push_back(no_ptr); + } + } + } + return no_reg_elems; +} + +lanelet::ConstPolygons3d query::getAllObstaclePolygons( + const lanelet::LaneletMapConstPtr & lanelet_map_ptr) +{ + lanelet::ConstPolygons3d obstacle_polygons; + for (const auto & poly : lanelet_map_ptr->polygonLayer) { + const std::string type = poly.attributeOr(lanelet::AttributeName::Type, "none"); + if (type.compare("obstacle") == 0) { + obstacle_polygons.push_back(poly); + } + } + return obstacle_polygons; +} + +lanelet::ConstPolygons3d query::getAllParkingLots( + const lanelet::LaneletMapConstPtr & lanelet_map_ptr) +{ + lanelet::ConstPolygons3d parking_lots; + for (const auto & poly : lanelet_map_ptr->polygonLayer) { + const std::string type = poly.attributeOr(lanelet::AttributeName::Type, "none"); + if (type.compare("parking_lot") == 0) { + parking_lots.push_back(poly); + } + } + return parking_lots; +} + +lanelet::ConstLineStrings3d query::getAllPedestrianMarkings( + const lanelet::LaneletMapConstPtr & lanelet_map_ptr) +{ + lanelet::ConstLineStrings3d pedestrian_markings; + for (const auto & ls : lanelet_map_ptr->lineStringLayer) { + const std::string type = ls.attributeOr(lanelet::AttributeName::Type, "none"); + if (type.compare("pedestrian_marking") == 0) { + pedestrian_markings.push_back(ls); + } + } + return pedestrian_markings; +} + +lanelet::ConstLineStrings3d query::getAllParkingSpaces( + const lanelet::LaneletMapConstPtr & lanelet_map_ptr) +{ + lanelet::ConstLineStrings3d parking_spaces; + for (const auto & ls : lanelet_map_ptr->lineStringLayer) { + const std::string type = ls.attributeOr(lanelet::AttributeName::Type, "none"); + if (type.compare("parking_space") == 0) { + parking_spaces.push_back(ls); + } + } + return parking_spaces; +} + +bool query::getLinkedLanelet( + const lanelet::ConstLineString3d & parking_space, + const lanelet::LaneletMapConstPtr & lanelet_map_ptr, lanelet::ConstLanelet * linked_lanelet) +{ + const auto & all_lanelets = query::laneletLayer(lanelet_map_ptr); + const auto & all_road_lanelets = query::roadLanelets(all_lanelets); + const auto & all_parking_lots = query::getAllParkingLots(lanelet_map_ptr); + return query::getLinkedLanelet( + parking_space, all_road_lanelets, all_parking_lots, linked_lanelet); +} + +bool query::getLinkedLanelet( + const lanelet::ConstLineString3d & parking_space, + const lanelet::ConstLanelets & all_road_lanelets, + const lanelet::ConstPolygons3d & all_parking_lots, lanelet::ConstLanelet * linked_lanelet) +{ + const auto & linked_lanelets = + getLinkedLanelets(parking_space, all_road_lanelets, all_parking_lots); + if (linked_lanelets.empty()) { + return false; + } + + double min_distance = std::numeric_limits::max(); + for (const auto & lanelet : linked_lanelets) { + const double distance = boost::geometry::distance( + to2D(parking_space).basicLineString(), lanelet.polygon2d().basicPolygon()); + if (distance < min_distance) { + *linked_lanelet = lanelet; + min_distance = distance; + } + } + return true; +} + +lanelet::ConstLanelets query::getLinkedLanelets( + const lanelet::ConstLineString3d & parking_space, + const lanelet::LaneletMapConstPtr & lanelet_map_ptr) +{ + const auto & all_lanelets = query::laneletLayer(lanelet_map_ptr); + const auto & all_road_lanelets = query::roadLanelets(all_lanelets); + const auto & all_parking_lots = query::getAllParkingLots(lanelet_map_ptr); + + return query::getLinkedLanelets(parking_space, all_road_lanelets, all_parking_lots); +} + +lanelet::ConstLanelets query::getLinkedLanelets( + const lanelet::ConstLineString3d & parking_space, + const lanelet::ConstLanelets & all_road_lanelets, + const lanelet::ConstPolygons3d & all_parking_lots) +{ + lanelet::ConstLanelets linked_lanelets; + + // get lanelets within same parking lot + lanelet::ConstPolygon3d linked_parking_lot; + if (!getLinkedParkingLot(parking_space, all_parking_lots, &linked_parking_lot)) { + return linked_lanelets; + } + const auto & candidate_lanelets = getLinkedLanelets(linked_parking_lot, all_road_lanelets); + + // get lanelets that are close to parking space and facing to parking space + for (const auto & lanelet : candidate_lanelets) { + // check if parking space is close to lanelet + const double distance = boost::geometry::distance( + to2D(parking_space).basicLineString(), lanelet.polygon2d().basicPolygon()); + constexpr double distance_thresh = 5.0; + if (distance > distance_thresh) { + continue; + } + + // check if parking space is facing lanelet + const Eigen::Vector3d direction = + parking_space.back().basicPoint() - parking_space.front().basicPoint(); + const Eigen::Vector3d new_pt = parking_space.front().basicPoint() - direction * distance_thresh; + + const lanelet::Point3d check_line_p1(lanelet::InvalId, new_pt.x(), new_pt.y(), new_pt.z()); + const lanelet::Point3d check_line_p2(lanelet::InvalId, parking_space.back().basicPoint()); + const lanelet::LineString3d check_line(lanelet::InvalId, {check_line_p1, check_line_p2}); + + const double new_distance = boost::geometry::distance( + to2D(check_line).basicLineString(), lanelet.polygon2d().basicPolygon()); + if (new_distance < std::numeric_limits::epsilon()) { + linked_lanelets.push_back(lanelet); + } + } + + return linked_lanelets; +} + +// get overlapping lanelets +lanelet::ConstLanelets query::getLinkedLanelets( + const lanelet::ConstPolygon3d & parking_lot, const lanelet::ConstLanelets & all_road_lanelets) +{ + lanelet::ConstLanelets linked_lanelets; + for (const auto & lanelet : all_road_lanelets) { + const double distance = boost::geometry::distance( + lanelet.polygon2d().basicPolygon(), to2D(parking_lot).basicPolygon()); + if (distance < std::numeric_limits::epsilon()) { + linked_lanelets.push_back(lanelet); + } + } + return linked_lanelets; +} + +lanelet::ConstLineStrings3d query::getLinkedParkingSpaces( + const lanelet::ConstLanelet & lanelet, const lanelet::LaneletMapConstPtr & lanelet_map_ptr) +{ + const auto & all_parking_spaces = query::getAllParkingSpaces(lanelet_map_ptr); + const auto & all_parking_lots = query::getAllParkingLots(lanelet_map_ptr); + return getLinkedParkingSpaces(lanelet, all_parking_spaces, all_parking_lots); +} + +lanelet::ConstLineStrings3d query::getLinkedParkingSpaces( + const lanelet::ConstLanelet & lanelet, const lanelet::ConstLineStrings3d & all_parking_spaces, + const lanelet::ConstPolygons3d & all_parking_lots) +{ + lanelet::ConstLineStrings3d linked_parking_spaces; + + // get parking spaces that are in same parking lot. + lanelet::ConstPolygon3d linked_parking_lot; + if (!getLinkedParkingLot(lanelet, all_parking_lots, &linked_parking_lot)) { + return linked_parking_spaces; + } + const auto & possible_parking_spaces = + getLinkedParkingSpaces(linked_parking_lot, all_parking_spaces); + + // check for parking spaces that are within 5m and facing towards lanelet + for (const auto & parking_space : possible_parking_spaces) { + // check if parking space is close to lanelet + const double distance = boost::geometry::distance( + to2D(parking_space).basicLineString(), lanelet.polygon2d().basicPolygon()); + constexpr double distance_thresh = 5.0; + if (distance > distance_thresh) { + continue; + } + + // check if parking space is facing lanelet + const Eigen::Vector3d direction = + parking_space.back().basicPoint() - parking_space.front().basicPoint(); + const Eigen::Vector3d new_pt = parking_space.front().basicPoint() - direction * distance_thresh; + + const lanelet::Point3d check_line_p1(lanelet::InvalId, new_pt.x(), new_pt.y(), new_pt.z()); + const lanelet::Point3d check_line_p2(lanelet::InvalId, parking_space.back().basicPoint()); + const lanelet::LineString3d check_line(lanelet::InvalId, {check_line_p1, check_line_p2}); + + const double new_distance = boost::geometry::distance( + to2D(check_line).basicLineString(), lanelet.polygon2d().basicPolygon()); + if (new_distance < std::numeric_limits::epsilon()) { + linked_parking_spaces.push_back(parking_space); + } + } + return linked_parking_spaces; +} + +// get overlapping parking lot +bool query::getLinkedParkingLot( + const lanelet::ConstLanelet & lanelet, const lanelet::ConstPolygons3d & all_parking_lots, + lanelet::ConstPolygon3d * linked_parking_lot) +{ + for (const auto & parking_lot : all_parking_lots) { + const double distance = boost::geometry::distance( + lanelet.polygon2d().basicPolygon(), to2D(parking_lot).basicPolygon()); + if (distance < std::numeric_limits::epsilon()) { + *linked_parking_lot = parking_lot; + return true; + } + } + return false; +} + +// get overlapping parking lot +bool query::getLinkedParkingLot( + const lanelet::ConstLineString3d & parking_space, + const lanelet::ConstPolygons3d & all_parking_lots, lanelet::ConstPolygon3d * linked_parking_lot) +{ + for (const auto & parking_lot : all_parking_lots) { + const double distance = boost::geometry::distance( + to2D(parking_space).basicLineString(), to2D(parking_lot).basicPolygon()); + if (distance < std::numeric_limits::epsilon()) { + *linked_parking_lot = parking_lot; + return true; + } + } + return false; +} + +lanelet::ConstLineStrings3d query::getLinkedParkingSpaces( + const lanelet::ConstPolygon3d & parking_lot, + const lanelet::ConstLineStrings3d & all_parking_spaces) +{ + lanelet::ConstLineStrings3d linked_parking_spaces; + for (const auto & parking_space : all_parking_spaces) { + const double distance = boost::geometry::distance( + to2D(parking_space).basicLineString(), to2D(parking_lot).basicPolygon()); + if (distance < std::numeric_limits::epsilon()) { + linked_parking_spaces.push_back(parking_space); + } + } + return linked_parking_spaces; +} + +// return all stop lines and ref lines from a given set of lanelets +std::vector query::stopLinesLanelets( + const lanelet::ConstLanelets lanelets) +{ + std::vector stoplines; + + for (auto lli = lanelets.begin(); lli != lanelets.end(); lli++) { + std::vector ll_stoplines; + ll_stoplines = query::stopLinesLanelet(*lli); + stoplines.insert(stoplines.end(), ll_stoplines.begin(), ll_stoplines.end()); + } + + return stoplines; +} + +// return all stop and ref lines from a given lanelet +std::vector query::stopLinesLanelet(const lanelet::ConstLanelet ll) +{ + std::vector stoplines; + + // find stop lines referenced by right of way reg. elems. + std::vector> right_of_way_reg_elems = + ll.regulatoryElementsAs(); + + if (right_of_way_reg_elems.size() > 0) { + // lanelet has a right of way elem element + for (auto j = right_of_way_reg_elems.begin(); j < right_of_way_reg_elems.end(); j++) { + if ((*j)->getManeuver(ll) == lanelet::ManeuverType::Yield) { + // lanelet has a yield reg. elem. + lanelet::Optional row_stopline_opt = (*j)->stopLine(); + if (!!row_stopline_opt) { + stoplines.push_back(row_stopline_opt.get()); + } + } + } + } + + // find stop lines referenced by traffic lights + std::vector> traffic_light_reg_elems = + ll.regulatoryElementsAs(); + + if (traffic_light_reg_elems.size() > 0) { + // lanelet has a traffic light elem element + for (auto j = traffic_light_reg_elems.begin(); j < traffic_light_reg_elems.end(); j++) { + lanelet::Optional traffic_light_stopline_opt = (*j)->stopLine(); + if (!!traffic_light_stopline_opt) { + stoplines.push_back(traffic_light_stopline_opt.get()); + } + } + } + // find stop lines referenced by traffic signs + std::vector> traffic_sign_reg_elems = + ll.regulatoryElementsAs(); + + if (traffic_sign_reg_elems.size() > 0) { + // lanelet has a traffic sign reg elem - can have multiple ref lines (but + // stop sign shod have 1 + for (auto j = traffic_sign_reg_elems.begin(); j < traffic_sign_reg_elems.end(); j++) { + lanelet::ConstLineStrings3d traffic_sign_stoplines = (*j)->refLines(); + if (traffic_sign_stoplines.size() > 0) { + stoplines.push_back(traffic_sign_stoplines.front()); + } + } + } + return stoplines; +} + +std::vector query::stopSignStopLines( + const lanelet::ConstLanelets lanelets, const std::string & stop_sign_id) +{ + std::vector stoplines; + + std::set checklist; + + for (const auto & ll : lanelets) { + // find stop lines referenced by traffic signs + std::vector> traffic_sign_reg_elems = + ll.regulatoryElementsAs(); + + if (traffic_sign_reg_elems.size() > 0) { + // lanelet has a traffic sign reg elem - can have multiple ref lines (but + // stop sign shod have 1 + for (const auto & ts : traffic_sign_reg_elems) { + // skip if traffic sign is not stop sign + if (ts->type() != stop_sign_id) { + continue; + } + + lanelet::ConstLineStrings3d traffic_sign_stoplines = ts->refLines(); + + // only add new items + if (traffic_sign_stoplines.size() > 0) { + auto id = traffic_sign_stoplines.front().id(); + if (checklist.find(id) == checklist.end()) { + checklist.insert(id); + stoplines.push_back(traffic_sign_stoplines.front()); + } + } + } + } + } + return stoplines; +} + +ConstLanelets query::getLaneletsWithinRange( + const lanelet::ConstLanelets & lanelets, const lanelet::BasicPoint2d & search_point, + const double range) +{ + ConstLanelets near_lanelets; + for (const auto & ll : lanelets) { + lanelet::BasicPolygon2d poly = ll.polygon2d().basicPolygon(); + double distance = lanelet::geometry::distance(poly, search_point); + if (distance <= range) { + near_lanelets.push_back(ll); + } + } + return near_lanelets; +} + +ConstLanelets query::getLaneletsWithinRange( + const lanelet::ConstLanelets & lanelets, const geometry_msgs::msg::Point & search_point, + const double range) +{ + return getLaneletsWithinRange( + lanelets, lanelet::BasicPoint2d(search_point.x, search_point.y), range); +} + +ConstLanelets query::getLaneChangeableNeighbors( + const routing::RoutingGraphPtr & graph, const ConstLanelet & lanelet) +{ + return graph->besides(lanelet); +} + +ConstLanelets query::getLaneChangeableNeighbors( + const routing::RoutingGraphPtr & graph, const ConstLanelets & road_lanelets, + const geometry_msgs::msg::Point & search_point) +{ + const auto lanelets = + getLaneletsWithinRange(road_lanelets, search_point, std::numeric_limits::epsilon()); + ConstLanelets road_slices; + for (const auto & llt : lanelets) { + const auto tmp_road_slice = getLaneChangeableNeighbors(graph, llt); + road_slices.insert(road_slices.end(), tmp_road_slice.begin(), tmp_road_slice.end()); + } + return road_slices; +} + +ConstLanelets query::getAllNeighbors( + const routing::RoutingGraphPtr & graph, const ConstLanelet & lanelet) +{ + ConstLanelets lanelets; + + ConstLanelets left_lanelets = getAllNeighborsLeft(graph, lanelet); + ConstLanelets right_lanelets = getAllNeighborsRight(graph, lanelet); + + std::reverse(left_lanelets.begin(), left_lanelets.end()); + lanelets.insert(lanelets.end(), left_lanelets.begin(), left_lanelets.end()); + lanelets.push_back(lanelet); + lanelets.insert(lanelets.end(), right_lanelets.begin(), right_lanelets.end()); + + return lanelets; +} + +ConstLanelets query::getAllNeighborsRight( + const routing::RoutingGraphPtr & graph, const ConstLanelet & lanelet) +{ + ConstLanelets lanelets; + auto right_lane = + (!!graph->right(lanelet)) ? graph->right(lanelet) : graph->adjacentRight(lanelet); + while (!!right_lane) { + lanelets.push_back(right_lane.get()); + right_lane = (!!graph->right(right_lane.get())) ? graph->right(right_lane.get()) + : graph->adjacentRight(right_lane.get()); + } + return lanelets; +} + +ConstLanelets query::getAllNeighborsLeft( + const routing::RoutingGraphPtr & graph, const ConstLanelet & lanelet) +{ + ConstLanelets lanelets; + auto left_lane = (!!graph->left(lanelet)) ? graph->left(lanelet) : graph->adjacentLeft(lanelet); + while (!!left_lane) { + lanelets.push_back(left_lane.get()); + left_lane = (!!graph->left(left_lane.get())) ? graph->left(left_lane.get()) + : graph->adjacentLeft(left_lane.get()); + } + return lanelets; +} + +ConstLanelets query::getAllNeighbors( + const routing::RoutingGraphPtr & graph, const ConstLanelets & road_lanelets, + const geometry_msgs::msg::Point & search_point) +{ + const auto lanelets = + getLaneletsWithinRange(road_lanelets, search_point, std::numeric_limits::epsilon()); + ConstLanelets road_slices; + for (const auto & llt : lanelets) { + const auto tmp_road_slice = getAllNeighbors(graph, llt); + road_slices.insert(road_slices.end(), tmp_road_slice.begin(), tmp_road_slice.end()); + } + return road_slices; +} + +bool query::getClosestLanelet( + const ConstLanelets & lanelets, const geometry_msgs::msg::Pose & search_pose, + ConstLanelet * closest_lanelet_ptr) +{ + if (closest_lanelet_ptr == nullptr) { + std::cerr << "argument closest_lanelet_ptr is null! Failed to find closest lanelet" + << std::endl; + return false; + } + + if (lanelets.empty()) { + return false; + } + + bool found = false; + + lanelet::BasicPoint2d search_point(search_pose.position.x, search_pose.position.y); + + // find by distance + lanelet::ConstLanelets candidate_lanelets; + { + double min_distance = std::numeric_limits::max(); + for (const auto & llt : lanelets) { + double distance = + boost::geometry::comparable_distance(llt.polygon2d().basicPolygon(), search_point); + + if (std::abs(distance - min_distance) <= std::numeric_limits::epsilon()) { + candidate_lanelets.push_back(llt); + } else if (distance < min_distance) { + found = true; + candidate_lanelets.clear(); + candidate_lanelets.push_back(llt); + min_distance = distance; + } + } + } + + // find by angle + { + double min_angle = std::numeric_limits::max(); + double pose_yaw = tf2::getYaw(search_pose.orientation); + for (const auto & llt : candidate_lanelets) { + lanelet::ConstLineString3d segment = getClosestSegment(search_point, llt.centerline()); + double segment_angle = std::atan2( + segment.back().y() - segment.front().y(), segment.back().x() - segment.front().x()); + double angle_diff = getAngleDifference(segment_angle, pose_yaw); + if (angle_diff < min_angle) { + min_angle = angle_diff; + *closest_lanelet_ptr = llt; + } else if ((segment_angle - pose_yaw) < 1e-04) { + min_angle = std::abs(segment_angle - pose_yaw); + *closest_lanelet_ptr = llt; + } + } + } + + return found; +} + +std::vector> getSucceedingLaneletSequencesRecursive( + const routing::RoutingGraphPtr & graph, const lanelet::ConstLanelet & lanelet, + const double length) +{ + std::vector> succeeding_lanelet_sequences; + + const auto next_lanelets = graph->following(lanelet); + const double lanelet_length = utils::getLaneletLength3d(lanelet); + + // end condition of the recursive function + if (next_lanelets.empty() || lanelet_length >= length) { + succeeding_lanelet_sequences.push_back({lanelet}); + return succeeding_lanelet_sequences; + } + + for (const auto & next_lanelet : next_lanelets) { + // get lanelet sequence after next_lanelet + auto tmp_lanelet_sequences = + getSucceedingLaneletSequencesRecursive(graph, next_lanelet, length - lanelet_length); + for (auto & tmp_lanelet_sequence : tmp_lanelet_sequences) { + tmp_lanelet_sequence.push_front(lanelet); + succeeding_lanelet_sequences.push_back(tmp_lanelet_sequence); + } + } + return succeeding_lanelet_sequences; +} + +std::vector> getPrecedingLaneletSequencesRecursive( + const routing::RoutingGraphPtr & graph, const lanelet::ConstLanelet & lanelet, + const double length, const lanelet::ConstLanelets & exclude_lanelets) +{ + std::vector> preceding_lanelet_sequences; + + const auto prev_lanelets = graph->previous(lanelet); + const double lanelet_length = utils::getLaneletLength3d(lanelet); + + // end condition of the recursive function + if (prev_lanelets.empty() || lanelet_length >= length) { + preceding_lanelet_sequences.push_back({lanelet}); + return preceding_lanelet_sequences; + } + + for (const auto & prev_lanelet : prev_lanelets) { + if (lanelet::utils::contains(exclude_lanelets, prev_lanelet)) { + // if prev_lanelet is included in exclude_lanelets, + // remove prev_lanelet from preceding_lanelet_sequences + continue; + } + + // get lanelet sequence after prev_lanelet + auto tmp_lanelet_sequences = getPrecedingLaneletSequencesRecursive( + graph, prev_lanelet, length - lanelet_length, exclude_lanelets); + for (auto & tmp_lanelet_sequence : tmp_lanelet_sequences) { + tmp_lanelet_sequence.push_back(lanelet); + preceding_lanelet_sequences.push_back(tmp_lanelet_sequence); + } + } + + if (preceding_lanelet_sequences.empty()) { + preceding_lanelet_sequences.push_back({lanelet}); + } + return preceding_lanelet_sequences; +} + +std::vector query::getSucceedingLaneletSequences( + const routing::RoutingGraphPtr & graph, const lanelet::ConstLanelet & lanelet, + const double length) +{ + std::vector lanelet_sequences_vec; + const auto next_lanelets = graph->following(lanelet); + for (const auto & next_lanelet : next_lanelets) { + const auto lanelet_sequences_deq = + getSucceedingLaneletSequencesRecursive(graph, next_lanelet, length); + for (const auto & lanelet_sequence : lanelet_sequences_deq) { + lanelet_sequences_vec.emplace_back(lanelet_sequence.begin(), lanelet_sequence.end()); + } + } + return lanelet_sequences_vec; +} + +std::vector query::getPrecedingLaneletSequences( + const routing::RoutingGraphPtr & graph, const lanelet::ConstLanelet & lanelet, + const double length, const lanelet::ConstLanelets & exclude_lanelets) +{ + std::vector lanelet_sequences_vec; + const auto prev_lanelets = graph->previous(lanelet); + for (const auto & prev_lanelet : prev_lanelets) { + if (lanelet::utils::contains(exclude_lanelets, prev_lanelet)) { + // if prev_lanelet is included in exclude_lanelets, + // remove prev_lanelet from preceding_lanelet_sequences + continue; + } + // convert deque into vector + const auto lanelet_sequences_deq = + getPrecedingLaneletSequencesRecursive(graph, prev_lanelet, length, exclude_lanelets); + for (const auto & lanelet_sequence : lanelet_sequences_deq) { + lanelet_sequences_vec.emplace_back(lanelet_sequence.begin(), lanelet_sequence.end()); + } + } + return lanelet_sequences_vec; +} + +} // namespace utils +} // namespace lanelet diff --git a/tmp/lanelet2_extension/lib/road_marking.cpp b/tmp/lanelet2_extension/lib/road_marking.cpp new file mode 100644 index 00000000..a63d74d2 --- /dev/null +++ b/tmp/lanelet2_extension/lib/road_marking.cpp @@ -0,0 +1,80 @@ +// Copyright 2020 Tier IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lanelet2_extension/regulatory_elements/road_marking.hpp" + +#include + +#include + +#include +#include +#include +#include + +namespace lanelet +{ +namespace autoware +{ +namespace +{ +RegulatoryElementDataPtr constructRoadMarkingData( + Id id, const AttributeMap & attributes, const LineString3d & road_marking) +{ + RuleParameterMap rpm; + RuleParameters rule_parameters = {road_marking}; + rpm.insert(std::make_pair(RoleNameString::Refers, rule_parameters)); + + auto data = std::make_shared(id, rpm, attributes); + data->attributes[AttributeName::Type] = AttributeValueString::RegulatoryElement; + data->attributes[AttributeName::Subtype] = "road_marking"; + return data; +} +} // namespace + +RoadMarking::RoadMarking(const RegulatoryElementDataPtr & data) : RegulatoryElement(data) +{ + if (getParameters(RoleName::Refers).size() != 1) { + throw InvalidInputError("There must be exactly one road marking defined!"); + } +} + +RoadMarking::RoadMarking(Id id, const AttributeMap & attributes, const LineString3d & road_marking) +: RoadMarking(constructRoadMarkingData(id, attributes, road_marking)) +{ +} + +ConstLineString3d RoadMarking::roadMarking() const +{ + return getParameters(RoleName::Refers).front(); +} + +LineString3d RoadMarking::roadMarking() +{ + return getParameters(RoleName::Refers).front(); +} + +void RoadMarking::setRoadMarking(const LineString3d & road_marking) +{ + parameters()[RoleName::Refers] = {road_marking}; +} + +void RoadMarking::removeRoadMarking() { parameters()[RoleName::Refers] = {}; } + +#if __cplusplus < 201703L +constexpr char RoadMarking::RuleName[]; // instantiate string in cpp file +#endif + +} // namespace autoware +} // namespace lanelet diff --git a/tmp/lanelet2_extension/lib/utilities.cpp b/tmp/lanelet2_extension/lib/utilities.cpp new file mode 100644 index 00000000..ebd64ca5 --- /dev/null +++ b/tmp/lanelet2_extension/lib/utilities.cpp @@ -0,0 +1,599 @@ +// Copyright 2015-2019 Autoware Foundation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Authors: Kenji Miyake, Ryohsuke Mitsudome + +#include "lanelet2_extension/utility/utilities.hpp" + +#include "lanelet2_extension/utility/message_conversion.hpp" +#include "lanelet2_extension/utility/query.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace lanelet +{ +namespace utils +{ +namespace +{ +[[maybe_unused]] bool exists(const std::vector & array, const int element) +{ + return std::find(array.begin(), array.end(), element) != array.end(); +} + +/** + * [getContactingLanelets retrieves id of lanelets which has distance 0m to + * search_point] + * @param lanelet_map [pointer to lanelet] + * @param trafficRules [traffic rules to ignore lanelets that are not + * traversable] + * @param search_point [2D point used for searching] + * @param contacting_lanelet_ids [array of lanelet ids that is contacting with + * search_point] + */ +[[maybe_unused]] void getContactingLanelets( + const lanelet::LaneletMapPtr lanelet_map, + const lanelet::traffic_rules::TrafficRulesPtr traffic_rules, + const lanelet::BasicPoint2d search_point, std::vector * contacting_lanelet_ids) +{ + if (!lanelet_map) { + std::cerr << "No lanelet map is set!" << std::endl; + return; + } + + if (contacting_lanelet_ids == nullptr) { + std::cerr << __FUNCTION__ << " contacting_lanelet_ids is null pointer!" << std::endl; + return; + } + + for (const auto & ll : lanelet_map->laneletLayer) { + if (!traffic_rules->canPass(ll)) { + continue; + } + lanelet::BasicPolygon2d poly = ll.polygon2d().basicPolygon(); + double distance = lanelet::geometry::distance(poly, search_point); + if (distance < std::numeric_limits::epsilon()) { + contacting_lanelet_ids->push_back(ll.id()); + } + } +} + +std::vector calculateSegmentDistances(const lanelet::ConstLineString3d & line_string) +{ + std::vector segment_distances; + segment_distances.reserve(line_string.size() - 1); + + for (size_t i = 1; i < line_string.size(); ++i) { + const auto distance = lanelet::geometry::distance(line_string[i], line_string[i - 1]); + segment_distances.push_back(distance); + } + + return segment_distances; +} + +std::vector calculateAccumulatedLengths(const lanelet::ConstLineString3d & line_string) +{ + const auto segment_distances = calculateSegmentDistances(line_string); + + std::vector accumulated_lengths{0}; + accumulated_lengths.reserve(segment_distances.size() + 1); + std::partial_sum( + std::begin(segment_distances), std::end(segment_distances), + std::back_inserter(accumulated_lengths)); + + return accumulated_lengths; +} + +std::pair findNearestIndexPair( + const std::vector & accumulated_lengths, const double target_length) +{ + // List size + const auto N = accumulated_lengths.size(); + + // Front + if (target_length < accumulated_lengths.at(1)) { + return std::make_pair(0, 1); + } + + // Back + if (target_length > accumulated_lengths.at(N - 2)) { + return std::make_pair(N - 2, N - 1); + } + + // Middle + for (std::size_t i = 1; i < N; ++i) { + if ( + accumulated_lengths.at(i - 1) <= target_length && + target_length <= accumulated_lengths.at(i)) { + return std::make_pair(i - 1, i); + } + } + + // Throw an exception because this never happens + throw std::runtime_error("No nearest point found."); +} + +std::vector resamplePoints( + const lanelet::ConstLineString3d & line_string, const int num_segments) +{ + // Calculate length + const auto line_length = lanelet::geometry::length(line_string); + + // Calculate accumulated lengths + const auto accumulated_lengths = calculateAccumulatedLengths(line_string); + + // Create each segment + std::vector resampled_points; + for (auto i = 0; i <= num_segments; ++i) { + // Find two nearest points + const auto target_length = (static_cast(i) / num_segments) * line_length; + const auto index_pair = findNearestIndexPair(accumulated_lengths, target_length); + + // Apply linear interpolation + const lanelet::BasicPoint3d back_point = line_string[index_pair.first]; + const lanelet::BasicPoint3d front_point = line_string[index_pair.second]; + const auto direction_vector = (front_point - back_point); + + const auto back_length = accumulated_lengths.at(index_pair.first); + const auto front_length = accumulated_lengths.at(index_pair.second); + const auto segment_length = front_length - back_length; + const auto target_point = + back_point + (direction_vector * (target_length - back_length) / segment_length); + + // Add to list + resampled_points.push_back(target_point); + } + + return resampled_points; +} +lanelet::LineString3d getLineStringFromArcLength( + const lanelet::ConstLineString3d & linestring, const double s1, const double s2) +{ + lanelet::Points3d points; + double accumulated_length = 0; + size_t start_index = linestring.size(); + for (size_t i = 0; i < linestring.size() - 1; i++) { + const auto p1 = linestring[i]; + const auto p2 = linestring[i + 1]; + const double length = boost::geometry::distance(p1.basicPoint(), p2.basicPoint()); + if (accumulated_length + length > s1) { + start_index = i; + break; + } + accumulated_length += length; + } + if (start_index < linestring.size() - 1) { + const auto p1 = linestring[start_index]; + const auto p2 = linestring[start_index + 1]; + const double residue = s1 - accumulated_length; + const auto direction_vector = (p2.basicPoint() - p1.basicPoint()).normalized(); + const auto start_basic_point = p1.basicPoint() + residue * direction_vector; + const auto start_point = lanelet::Point3d(lanelet::InvalId, start_basic_point); + points.push_back(start_point); + } + + accumulated_length = 0; + size_t end_index = linestring.size(); + for (size_t i = 0; i < linestring.size() - 1; i++) { + const auto p1 = linestring[i]; + const auto p2 = linestring[i + 1]; + const double length = boost::geometry::distance(p1.basicPoint(), p2.basicPoint()); + if (accumulated_length + length > s2) { + end_index = i; + break; + } + accumulated_length += length; + } + + for (size_t i = start_index + 1; i < end_index; i++) { + const auto p = lanelet::Point3d(linestring[i]); + points.push_back(p); + } + if (end_index < linestring.size() - 1) { + const auto p1 = linestring[end_index]; + const auto p2 = linestring[end_index + 1]; + const double residue = s2 - accumulated_length; + const auto direction_vector = (p2.basicPoint() - p1.basicPoint()).normalized(); + const auto end_basic_point = p1.basicPoint() + residue * direction_vector; + const auto end_point = lanelet::Point3d(lanelet::InvalId, end_basic_point); + points.push_back(end_point); + } + return lanelet::LineString3d(lanelet::InvalId, points); +} + +lanelet::ConstLanelet combineLanelets(const lanelet::ConstLanelets lanelets) +{ + lanelet::Points3d lefts, rights, centers; + for (const auto & llt : lanelets) { + for (const auto & pt : llt.leftBound()) { + lefts.push_back(lanelet::Point3d(pt)); + } + for (const auto & pt : llt.rightBound()) { + rights.push_back(lanelet::Point3d(pt)); + } + for (const auto & pt : llt.centerline()) { + centers.push_back(lanelet::Point3d(pt)); + } + } + const auto left_bound = lanelet::LineString3d(lanelet::InvalId, lefts); + const auto right_bound = lanelet::LineString3d(lanelet::InvalId, rights); + const auto center_line = lanelet::LineString3d(lanelet::InvalId, centers); + auto combined_lanelet = lanelet::Lanelet(lanelet::InvalId, left_bound, right_bound); + combined_lanelet.setCenterline(center_line); + return std::move(combined_lanelet); +} + +} // namespace + +lanelet::LineString3d generateFineCenterline( + const lanelet::ConstLanelet & lanelet_obj, const double resolution) +{ + // Get length of longer border + const double left_length = lanelet::geometry::length(lanelet_obj.leftBound()); + const double right_length = lanelet::geometry::length(lanelet_obj.rightBound()); + const double longer_distance = (left_length > right_length) ? left_length : right_length; + const int num_segments = std::max(static_cast(ceil(longer_distance / resolution)), 1); + + // Resample points + const auto left_points = resamplePoints(lanelet_obj.leftBound(), num_segments); + const auto right_points = resamplePoints(lanelet_obj.rightBound(), num_segments); + + // Create centerline + lanelet::LineString3d centerline(lanelet::utils::getId()); + for (int i = 0; i < num_segments + 1; i++) { + // Add ID for the average point of left and right + const auto center_basic_point = (right_points.at(i) + left_points.at(i)) / 2; + const lanelet::Point3d center_point( + lanelet::utils::getId(), center_basic_point.x(), center_basic_point.y(), + center_basic_point.z()); + centerline.push_back(center_point); + } + return centerline; +} + +lanelet::ConstLineString3d getCenterlineWithOffset( + const lanelet::ConstLanelet & lanelet_obj, const double offset, const double resolution) +{ + // Get length of longer border + const double left_length = lanelet::geometry::length(lanelet_obj.leftBound()); + const double right_length = lanelet::geometry::length(lanelet_obj.rightBound()); + const double longer_distance = (left_length > right_length) ? left_length : right_length; + const int num_segments = std::max(static_cast(ceil(longer_distance / resolution)), 1); + + // Resample points + const auto left_points = lanelet::utils::resamplePoints(lanelet_obj.leftBound(), num_segments); + const auto right_points = resamplePoints(lanelet_obj.rightBound(), num_segments); + + // Create centerline + lanelet::LineString3d centerline(lanelet::utils::getId()); + for (int i = 0; i < num_segments + 1; i++) { + // Add ID for the average point of left and right + const auto center_basic_point = (right_points.at(i) + left_points.at(i)) / 2; + + const auto vec_right_2_left = (left_points.at(i) - right_points.at(i)).normalized(); + + const auto offset_center_basic_point = center_basic_point + vec_right_2_left * offset; + + const lanelet::Point3d center_point( + lanelet::utils::getId(), offset_center_basic_point.x(), offset_center_basic_point.y(), + offset_center_basic_point.z()); + centerline.push_back(center_point); + } + return static_cast(centerline); +} + +lanelet::ConstLanelet getExpandedLanelet( + const lanelet::ConstLanelet & lanelet_obj, const double left_offset, const double right_offset) +{ + using lanelet::geometry::offsetNoThrow; + using lanelet::geometry::internal::checkForInversion; + + const auto & orig_left_bound_2d = lanelet_obj.leftBound2d().basicLineString(); + const auto & orig_right_bound_2d = lanelet_obj.rightBound2d().basicLineString(); + + // Note: The lanelet::geometry::offset throws exception when the undesired inversion is found. + // Use offsetNoThrow until the logic is updated to handle the inversion. + // TODO(Horibe) update + const auto & expanded_left_bound_2d = offsetNoThrow(orig_left_bound_2d, left_offset); + const auto & expanded_right_bound_2d = offsetNoThrow(orig_right_bound_2d, right_offset); + rclcpp::Clock clock{RCL_ROS_TIME}; + try { + checkForInversion(orig_left_bound_2d, expanded_left_bound_2d, left_offset); + checkForInversion(orig_right_bound_2d, expanded_right_bound_2d, right_offset); + } catch (const lanelet::GeometryError & e) { + RCLCPP_ERROR_THROTTLE( + rclcpp::get_logger("lanelet2_extension"), clock, 1000, + "Fail to expand lanelet. output may be undesired. Lanelet points interval in map data could " + "be too narrow."); + } + + const auto toPoints3d = [](const lanelet::BasicLineString2d & ls2d, const double z) { + lanelet::Points3d output; + for (const auto & pt : ls2d) { + output.push_back(lanelet::Point3d(lanelet::InvalId, pt.x(), pt.y(), z)); + } + return output; + }; + + // Original z value cannot be used directly since the offset function can vary the points size. + const lanelet::Points3d ex_lefts = + toPoints3d(expanded_left_bound_2d, lanelet_obj.leftBound3d().basicLineString().at(0).z()); + const lanelet::Points3d ex_rights = + toPoints3d(expanded_right_bound_2d, lanelet_obj.rightBound3d().basicLineString().at(0).z()); + + const auto & extended_left_bound_3d = lanelet::LineString3d(lanelet::InvalId, ex_lefts); + const auto & expanded_right_bound_3d = lanelet::LineString3d(lanelet::InvalId, ex_rights); + const auto & lanelet = lanelet::Lanelet( + lanelet_obj.id(), extended_left_bound_3d, expanded_right_bound_3d, lanelet_obj.attributes()); + + return lanelet; +} + +lanelet::ConstLanelets getExpandedLanelets( + const lanelet::ConstLanelets & lanelet_obj, const double left_offset, const double right_offset) +{ + lanelet::ConstLanelets lanelets; + for (const auto & llt : lanelet_obj) { + lanelets.push_back(getExpandedLanelet(llt, left_offset, right_offset)); + } + return lanelets; +} + +void overwriteLaneletsCenterline( + lanelet::LaneletMapPtr lanelet_map, const double resolution, const bool force_overwrite) +{ + for (auto & lanelet_obj : lanelet_map->laneletLayer) { + if (force_overwrite || !lanelet_obj.hasCustomCenterline()) { + const auto fine_center_line = generateFineCenterline(lanelet_obj, resolution); + lanelet_obj.setCenterline(fine_center_line); + } + } +} + +lanelet::ConstLanelets getConflictingLanelets( + const lanelet::routing::RoutingGraphConstPtr & graph, const lanelet::ConstLanelet & lanelet) +{ + const auto & llt_or_areas = graph->conflicting(lanelet); + lanelet::ConstLanelets lanelets; + lanelets.reserve(llt_or_areas.size()); + for (const auto & l_or_a : llt_or_areas) { + auto llt_opt = l_or_a.lanelet(); + if (!!llt_opt) { + lanelets.push_back(llt_opt.get()); + } + } + return lanelets; +} + +bool lineStringWithWidthToPolygon( + const lanelet::ConstLineString3d & linestring, lanelet::ConstPolygon3d * polygon) +{ + if (polygon == nullptr) { + std::cerr << __func__ << ": polygon is null pointer! Failed to convert to polygon." + << std::endl; + return false; + } + if (linestring.size() != 2) { + std::cerr << __func__ << ": linestring" << linestring.id() << " must have 2 points! (" + << linestring.size() << " != 2)" << std::endl + << "Failed to convert to polygon."; + return false; + } + if (!linestring.hasAttribute("width")) { + std::cerr << __func__ << ": linestring" << linestring.id() + << " does not have width tag. Failed to convert to polygon."; + return false; + } + + const Eigen::Vector3d direction = + linestring.back().basicPoint() - linestring.front().basicPoint(); + const double width = linestring.attributeOr("width", 0.0); + const Eigen::Vector3d direction_left = + (Eigen::AngleAxisd(M_PI_2, Eigen::Vector3d::UnitZ()) * direction).normalized(); + + const Eigen::Vector3d eigen_p1 = linestring.front().basicPoint() + direction_left * width / 2; + const Eigen::Vector3d eigen_p2 = linestring.back().basicPoint() + direction_left * width / 2; + const Eigen::Vector3d eigen_p3 = linestring.back().basicPoint() - direction_left * width / 2; + const Eigen::Vector3d eigen_p4 = linestring.front().basicPoint() - direction_left * width / 2; + + const lanelet::Point3d p1(lanelet::InvalId, eigen_p1.x(), eigen_p1.y(), eigen_p1.z()); + const lanelet::Point3d p2(lanelet::InvalId, eigen_p2.x(), eigen_p2.y(), eigen_p2.z()); + const lanelet::Point3d p3(lanelet::InvalId, eigen_p3.x(), eigen_p3.y(), eigen_p3.z()); + const lanelet::Point3d p4(lanelet::InvalId, eigen_p4.x(), eigen_p4.y(), eigen_p4.z()); + + *polygon = lanelet::Polygon3d(lanelet::InvalId, {p1, p2, p3, p4}); + + return true; +} + +bool lineStringToPolygon( + const lanelet::ConstLineString3d & linestring, lanelet::ConstPolygon3d * polygon) +{ + if (polygon == nullptr) { + RCLCPP_ERROR_STREAM( + rclcpp::get_logger("lanelet2_extension.visualization"), + __func__ << ": polygon is null pointer! Failed to convert to polygon."); + return false; + } + if (linestring.size() < 4) { + if (linestring.size() < 3 || linestring.front().id() == linestring.back().id()) { + RCLCPP_ERROR_STREAM( + rclcpp::get_logger("lanelet2_extension.visualization"), + __func__ << ": linestring" << linestring.id() + << " must have more than different 3 points! (size is " << linestring.size() << ")" + << std::endl + << "Failed to convert to polygon."); + return false; + } + } + + lanelet::Polygon3d llt_poly; + + for (const auto & lp : linestring) { + llt_poly.push_back(lanelet::Point3d( + lanelet::InvalId, lp.basicPoint().x(), lp.basicPoint().y(), lp.basicPoint().z())); + } + + if (linestring.front().id() == linestring.back().id()) { + llt_poly.pop_back(); + } + + *polygon = llt_poly; + + return true; +} + +double getLaneletLength2d(const lanelet::ConstLanelet & lanelet) +{ + return boost::geometry::length(lanelet::utils::to2D(lanelet.centerline()).basicLineString()); +} + +double getLaneletLength3d(const lanelet::ConstLanelet & lanelet) +{ + return boost::geometry::length(lanelet.centerline().basicLineString()); +} + +double getLaneletLength2d(const lanelet::ConstLanelets & lanelet_sequence) +{ + double length = 0; + for (const auto & llt : lanelet_sequence) { + length += getLaneletLength2d(llt); + } + return length; +} + +double getLaneletLength3d(const lanelet::ConstLanelets & lanelet_sequence) +{ + double length = 0; + for (const auto & llt : lanelet_sequence) { + length += getLaneletLength3d(llt); + } + return length; +} + +lanelet::ArcCoordinates getArcCoordinates( + const lanelet::ConstLanelets & lanelet_sequence, const geometry_msgs::msg::Pose & pose) +{ + lanelet::ConstLanelet closest_lanelet; + lanelet::utils::query::getClosestLanelet(lanelet_sequence, pose, &closest_lanelet); + + double length = 0; + lanelet::ArcCoordinates arc_coordinates; + for (const auto & llt : lanelet_sequence) { + const auto & centerline_2d = lanelet::utils::to2D(llt.centerline()); + if (llt == closest_lanelet) { + const auto lanelet_point = lanelet::utils::conversion::toLaneletPoint(pose.position); + arc_coordinates = lanelet::geometry::toArcCoordinates( + centerline_2d, lanelet::utils::to2D(lanelet_point).basicPoint()); + arc_coordinates.length += length; + break; + } + length += boost::geometry::length(centerline_2d); + } + return arc_coordinates; +} + +lanelet::ConstLineString3d getClosestSegment( + const lanelet::BasicPoint2d & search_pt, const lanelet::ConstLineString3d & linestring) +{ + if (linestring.size() < 2) { + return lanelet::LineString3d(); + } + + lanelet::ConstLineString3d closest_segment; + double min_distance = std::numeric_limits::max(); + + for (size_t i = 1; i < linestring.size(); i++) { + lanelet::BasicPoint3d prev_basic_pt = linestring[i - 1].basicPoint(); + lanelet::BasicPoint3d current_basic_pt = linestring[i].basicPoint(); + + lanelet::Point3d prev_pt( + lanelet::InvalId, prev_basic_pt.x(), prev_basic_pt.y(), prev_basic_pt.z()); + lanelet::Point3d current_pt( + lanelet::InvalId, current_basic_pt.x(), current_basic_pt.y(), current_basic_pt.z()); + + lanelet::LineString3d current_segment(lanelet::InvalId, {prev_pt, current_pt}); + double distance = lanelet::geometry::distance2d( + lanelet::utils::to2D(current_segment).basicLineString(), search_pt); + if (distance < min_distance) { + closest_segment = current_segment; + min_distance = distance; + } + } + return closest_segment; +} + +lanelet::CompoundPolygon3d getPolygonFromArcLength( + const lanelet::ConstLanelets & lanelets, const double s1, const double s2) +{ + const auto combined_lanelet = combineLanelets(lanelets); + const auto total_length = getLaneletLength2d(combined_lanelet); + + // make sure that s1, and s2 are between [0, lane_length] + const auto s1_saturated = std::max(0.0, std::min(s1, total_length)); + const auto s2_saturated = std::max(0.0, std::min(s2, total_length)); + + const auto ratio_s1 = s1_saturated / total_length; + const auto ratio_s2 = s2_saturated / total_length; + + const auto s1_left = + ratio_s1 * boost::geometry::length(combined_lanelet.leftBound().basicLineString()); + const auto s2_left = + ratio_s2 * boost::geometry::length(combined_lanelet.leftBound().basicLineString()); + const auto s1_right = + ratio_s1 * boost::geometry::length(combined_lanelet.rightBound().basicLineString()); + const auto s2_right = + ratio_s2 * boost::geometry::length(combined_lanelet.rightBound().basicLineString()); + + const auto left_bound = + getLineStringFromArcLength(combined_lanelet.leftBound(), s1_left, s2_left); + const auto right_bound = + getLineStringFromArcLength(combined_lanelet.rightBound(), s1_right, s2_right); + + const auto & lanelet = lanelet::Lanelet(lanelet::InvalId, left_bound, right_bound); + return lanelet.polygon3d(); +} + +double getLaneletAngle( + const lanelet::ConstLanelet & lanelet, const geometry_msgs::msg::Point & search_point) +{ + lanelet::BasicPoint2d llt_search_point(search_point.x, search_point.y); + lanelet::ConstLineString3d segment = getClosestSegment(llt_search_point, lanelet.centerline()); + return std::atan2( + segment.back().y() - segment.front().y(), segment.back().x() - segment.front().x()); +} + +bool isInLanelet( + const geometry_msgs::msg::Pose & current_pose, const lanelet::ConstLanelet & lanelet, + const double radius) +{ + const lanelet::BasicPoint2d p(current_pose.position.x, current_pose.position.y); + if (boost::geometry::distance(p, lanelet.polygon2d().basicPolygon()) < radius) { + return true; + } + return false; +} + +} // namespace utils +} // namespace lanelet diff --git a/tmp/lanelet2_extension/lib/virtual_traffic_light.cpp b/tmp/lanelet2_extension/lib/virtual_traffic_light.cpp new file mode 100644 index 00000000..9d7fdc6f --- /dev/null +++ b/tmp/lanelet2_extension/lib/virtual_traffic_light.cpp @@ -0,0 +1,68 @@ +// Copyright 2021 Tier IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lanelet2_extension/regulatory_elements/virtual_traffic_light.hpp" + +#include + +#include + +#include +#include +#include +#include + +namespace lanelet +{ +namespace autoware +{ +namespace +{ +RegulatoryElementDataPtr constructVirtualTrafficLightData( + const Id id, const AttributeMap & attributes, const LineString3d & virtual_traffic_light) +{ + RuleParameterMap rpm; + RuleParameters rule_parameters = {virtual_traffic_light}; + rpm.insert(std::make_pair(RoleNameString::Refers, rule_parameters)); + + auto data = std::make_shared(id, rpm, attributes); + data->attributes[AttributeName::Type] = AttributeValueString::RegulatoryElement; + data->attributes[AttributeName::Subtype] = "virtual_traffic_light"; + return data; +} +} // namespace + +VirtualTrafficLight::VirtualTrafficLight(const RegulatoryElementDataPtr & data) +: RegulatoryElement(data) +{ + if (getParameters("start_line").size() != 1) { + throw InvalidInputError("There must be exactly one start_line defined!"); + } + if (getParameters("end_line").empty()) { + throw InvalidInputError("No end_line defined!"); + } +} + +VirtualTrafficLight::VirtualTrafficLight( + const Id id, const AttributeMap & attributes, const LineString3d & virtual_traffic_light) +: VirtualTrafficLight(constructVirtualTrafficLightData(id, attributes, virtual_traffic_light)) +{ +} + +#if __cplusplus < 201703L +constexpr char VirtualTrafficLight::RuleName[]; // instantiate string in cpp file +#endif + +} // namespace autoware +} // namespace lanelet diff --git a/tmp/lanelet2_extension/lib/visualization.cpp b/tmp/lanelet2_extension/lib/visualization.cpp new file mode 100644 index 00000000..fc9f0686 --- /dev/null +++ b/tmp/lanelet2_extension/lib/visualization.cpp @@ -0,0 +1,1171 @@ +// Copyright 2015-2019 Autoware Foundation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Authors: Simon Thompson, Ryohsuke Mitsudome + +#include "lanelet2_extension/visualization/visualization.hpp" + +#include "lanelet2_extension/utility/message_conversion.hpp" +#include "lanelet2_extension/utility/query.hpp" +#include "lanelet2_extension/utility/utilities.hpp" + +#include + +#include +#include + +#include +#include +#include +#include + +namespace +{ +template +bool exists(const std::unordered_set & set, const T & element) +{ + return std::find(set.begin(), set.end(), element) != set.end(); +} + +void adjacentPoints( + const int i, const int N, const geometry_msgs::msg::Polygon poly, + geometry_msgs::msg::Point32 * p0, geometry_msgs::msg::Point32 * p1, + geometry_msgs::msg::Point32 * p2) +{ + if (p0 == nullptr || p1 == nullptr || p2 == nullptr) { + std::cerr << __FUNCTION__ << ": either p0, p1, or p2 is null pointer!" << std::endl; + return; + } + + *p1 = poly.points[i]; + if (i == 0) { + *p0 = poly.points[N - 1]; + } else { + *p0 = poly.points[i - 1]; + } + + if (i < N - 1) { + *p2 = poly.points[i + 1]; + } else { + *p2 = poly.points[0]; + } +} + +[[maybe_unused]] double hypot( + const geometry_msgs::msg::Point32 & p0, const geometry_msgs::msg::Point32 & p1) +{ + return sqrt(pow((p1.x - p0.x), 2.0) + pow((p1.y - p0.y), 2.0)); +} + +bool isAttributeValue( + const lanelet::ConstPoint3d p, const std::string attr_str, const std::string value_str) +{ + lanelet::Attribute attr = p.attribute(attr_str); + if (attr.value().compare(value_str) == 0) { + return true; + } + return false; +} + +bool isLaneletAttributeValue( + const lanelet::ConstLanelet ll, const std::string attr_str, const std::string value_str) +{ + lanelet::Attribute attr = ll.attribute(attr_str); + if (attr.value().compare(value_str) == 0) { + return true; + } + return false; +} + +void initLightMarker(visualization_msgs::msg::Marker * marker, const std::string ns) +{ + if (marker == nullptr) { + std::cerr << __FUNCTION__ << ": marker is null pointer!" << std::endl; + return; + } + + float s = 0.3; + + marker->header.frame_id = "map"; + marker->header.stamp = rclcpp::Time(); + marker->frame_locked = true; + marker->ns = ns; + marker->id = 0; + marker->lifetime = rclcpp::Duration(0, 0); + marker->type = visualization_msgs::msg::Marker::SPHERE; + marker->scale.x = s; + marker->scale.y = s; + marker->scale.z = s; +} + +bool inputLightMarker(visualization_msgs::msg::Marker * marker, lanelet::ConstPoint3d p) +{ + if (marker == nullptr) { + std::cerr << __FUNCTION__ << ": marker is null pointer!" << std::endl; + return false; + } + + marker->id = p.id(); + + geometry_msgs::msg::Point point; + marker->pose.position.x = p.x(); + marker->pose.position.y = p.y(); + marker->pose.position.z = p.z(); + + std_msgs::msg::ColorRGBA color; + marker->color.r = 0.0f; + marker->color.g = 0.0f; + marker->color.b = 0.0f; + marker->color.a = 0.3f; + + if (isAttributeValue(p, "color", "red")) { + marker->color.r = 0.3f; + marker->color.g = 0.0f; + marker->color.b = 0.0f; + } else if (isAttributeValue(p, "color", "green")) { + marker->color.r = 0.0f; + marker->color.g = 0.3f; + marker->color.b = 0.0f; + } else if (isAttributeValue(p, "color", "yellow")) { + marker->color.r = 0.3f; + marker->color.g = 0.3f; + marker->color.b = 0.0f; + } else { + marker->color.r = 0.3f; + marker->color.g = 0.3f; + marker->color.b = 0.3f; + } + return true; +} + +void initLaneletDirectionMarker(visualization_msgs::msg::Marker * marker, const std::string ns) +{ + if (marker == nullptr) { + std::cerr << __FUNCTION__ << ": marker is null pointer!" << std::endl; + return; + } + + float s = 1.0; + + marker->header.frame_id = "map"; + marker->header.stamp = rclcpp::Time(); + marker->frame_locked = true; + marker->ns = ns; + marker->id = 0; + marker->type = visualization_msgs::msg::Marker::TRIANGLE_LIST; + marker->lifetime = rclcpp::Duration(0, 0); + + marker->pose.position.x = 0.0; // p.x(); + marker->pose.position.y = 0.0; // p.y(); + marker->pose.position.z = 0.0; // p.z(); + marker->pose.orientation.x = 0.0; + marker->pose.orientation.y = 0.0; + marker->pose.orientation.z = 0.0; + marker->pose.orientation.w = 1.0; + marker->scale.x = s; + marker->scale.y = s; + marker->scale.z = s; + marker->color.r = 1.0f; + marker->color.g = 1.0f; + marker->color.b = 1.0f; + marker->color.a = 0.999; +} + +void pushLaneletDirectionMarker( + visualization_msgs::msg::Marker * marker, const lanelet::ConstLanelet ll) +{ + if (marker == nullptr) { + std::cerr << __FUNCTION__ << ": marker is null pointer!" << std::endl; + return; + } + + lanelet::BasicPoint3d pt[3]; + pt[0].x() = 0.0; + pt[0].y() = -0.3; + pt[0].z() = 0.0; + pt[1].x() = 0.0; + pt[1].y() = 0.3; + pt[1].z() = 0; + pt[2].x() = 1.0; + pt[2].y() = 0.0; + pt[2].z() = 0; + + lanelet::BasicPoint3d pc, pc2; + lanelet::ConstLineString3d center_ls = ll.centerline(); + lanelet::Attribute attr = ll.attribute("turn_direction"); + + std_msgs::msg::ColorRGBA c; + c.r = 0.5; + c.g = 0.5; + c.b = 0.5; + c.a = 0.5; + + if (isLaneletAttributeValue(ll, "turn_direction", "right")) { + c.r = 0.5; + c.g = 0.5; + c.b = 0.6; + } else if (isLaneletAttributeValue(ll, "turn_direction", "left")) { + c.r = 0.5; + c.g = 0.6; + c.b = 0.6; + } + + for (std::size_t ci = 0; ci < center_ls.size() - 1;) { + pc = center_ls[ci]; + if (center_ls.size() > 1) { + pc2 = center_ls[ci + 1]; + } else { + return; + } + + double heading = atan2(pc2.y() - pc.y(), pc2.x() - pc.x()); + + lanelet::BasicPoint3d pt_tf[3]; + + Eigen::Vector3d axis(0, 0, 1); + Eigen::Transform t(Eigen::AngleAxis(heading, axis)); + + for (int i = 0; i < 3; i++) { + pt_tf[i] = t * pt[i] + pc; + } + + geometry_msgs::msg::Point gp[3]; + + for (int i = 0; i < 3; i++) { + std_msgs::msg::ColorRGBA cn = c; + + gp[i].x = pt_tf[i].x(); + gp[i].y = pt_tf[i].y(); + gp[i].z = pt_tf[i].z(); + marker->points.push_back(gp[i]); + marker->colors.push_back(cn); + } + ci = ci + 1; + } +} + +bool isClockWise(const geometry_msgs::msg::Polygon & polygon) +{ + const int N = polygon.points.size(); + const double x_offset = polygon.points[0].x; + const double y_offset = polygon.points[0].y; + double sum = 0.0; + for (std::size_t i = 0; i < polygon.points.size(); ++i) { + sum += (polygon.points[i].x - x_offset) * (polygon.points[(i + 1) % N].y - y_offset) - + (polygon.points[i].y - y_offset) * (polygon.points[(i + 1) % N].x - x_offset); + } + + return sum < 0.0; +} + +// Is angle AOB less than 180? +// https://qiita.com/fujii-kotaro/items/a411f2a45627ed2f156e +bool isAcuteAngle( + const geometry_msgs::msg::Point32 & a, const geometry_msgs::msg::Point32 & o, + const geometry_msgs::msg::Point32 & b) +{ + return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x) >= 0; +} + +// https://qiita.com/fujii-kotaro/items/a411f2a45627ed2f156e +bool isWithinTriangle( + const geometry_msgs::msg::Point32 & a, const geometry_msgs::msg::Point32 & b, + const geometry_msgs::msg::Point32 & c, const geometry_msgs::msg::Point32 & p) +{ + double c1 = (b.x - a.x) * (p.y - b.y) - (b.y - a.y) * (p.x - b.x); + double c2 = (c.x - b.x) * (p.y - c.y) - (c.y - b.y) * (p.x - c.x); + double c3 = (a.x - c.x) * (p.y - a.y) - (a.y - c.y) * (p.x - a.x); + + return (c1 > 0.0 && c2 > 0.0 && c3 > 0.0) || (c1 < 0.0 && c2 < 0.0 && c3 < 0.0); +} + +visualization_msgs::msg::Marker createPolygonMarker( + const std::string & name_space, const std_msgs::msg::ColorRGBA & color) +{ + visualization_msgs::msg::Marker marker; + marker.header.frame_id = "map"; + marker.header.stamp = rclcpp::Time(); + marker.frame_locked = true; + marker.id = 0; + marker.ns = name_space; + marker.type = visualization_msgs::msg::Marker::TRIANGLE_LIST; + marker.lifetime = rclcpp::Duration(0, 0); + marker.pose.position.x = 0.0; + marker.pose.position.y = 0.0; + marker.pose.position.z = 0.0; + marker.pose.orientation.x = 0.0; + marker.pose.orientation.y = 0.0; + marker.pose.orientation.z = 0.0; + marker.pose.orientation.w = 1.0; + marker.scale.x = 1.0; + marker.scale.y = 1.0; + marker.scale.z = 1.0; + marker.color = color; + return marker; +} + +void pushPolygonMarker( + visualization_msgs::msg::Marker * marker, const lanelet::ConstPolygon3d & polygon, + const std_msgs::msg::ColorRGBA & color) +{ + if (marker == nullptr) { + std::cerr << __FUNCTION__ << ": marker is null pointer!" << std::endl; + return; + } + + if (polygon.size() < 3) { + return; + } + + geometry_msgs::msg::Polygon geom_poly; + lanelet::utils::conversion::toGeomMsgPoly(polygon, &geom_poly); + + std::vector triangles; + lanelet::visualization::polygon2Triangle(geom_poly, &triangles); + + for (const auto & tri : triangles) { + geometry_msgs::msg::Point geom_pts[3]; + for (int i = 0; i < 3; i++) { + lanelet::utils::conversion::toGeomMsgPt(tri.points[i], &geom_pts[i]); + marker->points.push_back(geom_pts[i]); + marker->colors.push_back(color); + } + } +} + +} // anonymous namespace + +namespace lanelet +{ +void visualization::lanelet2Triangle( + const lanelet::ConstLanelet & ll, std::vector * triangles) +{ + if (triangles == nullptr) { + std::cerr << __FUNCTION__ << ": triangles is null pointer!" << std::endl; + return; + } + + triangles->clear(); + geometry_msgs::msg::Polygon ll_poly; + lanelet2Polygon(ll, &ll_poly); + polygon2Triangle(ll_poly, triangles); +} + +void visualization::polygon2Triangle( + const geometry_msgs::msg::Polygon & polygon, std::vector * triangles) +{ + geometry_msgs::msg::Polygon poly = polygon; + if (!isClockWise(poly)) { + std::reverse(poly.points.begin(), poly.points.end()); + } + + // ear clipping: find smallest internal angle in polygon + int N = poly.points.size(); + + // array of angles for each vertex + std::vector is_acute_angle; + is_acute_angle.assign(N, false); + for (int i = 0; i < N; i++) { + geometry_msgs::msg::Point32 p0, p1, p2; + + adjacentPoints(i, N, poly, &p0, &p1, &p2); + is_acute_angle.at(i) = isAcuteAngle(p0, p1, p2); + } + + // start ear clipping + while (N >= 3) { + int clipped_vertex = -1; + + for (int i = 0; i < N; i++) { + bool theta = is_acute_angle.at(i); + if (theta == true) { + geometry_msgs::msg::Point32 p0, p1, p2; + adjacentPoints(i, N, poly, &p0, &p1, &p2); + + int j_begin = (i + 2) % N; + int j_end = (i - 1 + N) % N; + bool is_ear = true; + for (int j = j_begin; j != j_end; j = (j + 1) % N) { + if (isWithinTriangle(p0, p1, p2, poly.points.at(j))) { + is_ear = false; + break; + } + } + + if (is_ear) { + clipped_vertex = i; + break; + } + } + } + if (clipped_vertex < 0 || clipped_vertex >= N) { + // print in yellow to indicate warning + std::cerr << "\033[1;33mCould not find valid vertex for ear clipping triangulation. " + "Triangulation result might be invalid\033[0m" + << std::endl; + clipped_vertex = 0; + } + + // create triangle + geometry_msgs::msg::Point32 p0, p1, p2; + adjacentPoints(clipped_vertex, N, poly, &p0, &p1, &p2); + geometry_msgs::msg::Polygon triangle; + triangle.points.push_back(p0); + triangle.points.push_back(p1); + triangle.points.push_back(p2); + triangles->push_back(triangle); + + // remove vertex of center of angle + auto it = poly.points.begin(); + std::advance(it, clipped_vertex); + poly.points.erase(it); + + // remove from angle list + auto it_angle = is_acute_angle.begin(); + std::advance(it_angle, clipped_vertex); + is_acute_angle.erase(it_angle); + + // update angle list + N = poly.points.size(); + if (clipped_vertex == N) { + clipped_vertex = 0; + } + adjacentPoints(clipped_vertex, N, poly, &p0, &p1, &p2); + is_acute_angle.at(clipped_vertex) = isAcuteAngle(p0, p1, p2); + + int i_prev = (clipped_vertex == 0) ? N - 1 : clipped_vertex - 1; + adjacentPoints(i_prev, N, poly, &p0, &p1, &p2); + is_acute_angle.at(i_prev) = isAcuteAngle(p0, p1, p2); + } +} + +void visualization::lanelet2Polygon( + const lanelet::ConstLanelet & ll, geometry_msgs::msg::Polygon * polygon) +{ + if (polygon == nullptr) { + std::cerr << __FUNCTION__ << ": polygon is null pointer!" << std::endl; + return; + } + + lanelet::CompoundPolygon3d ll_poly = ll.polygon3d(); + + polygon->points.clear(); + polygon->points.reserve(ll_poly.size()); + + for (const auto & pt : ll_poly) { + geometry_msgs::msg::Point32 pt32; + utils::conversion::toGeomMsgPt32(pt.basicPoint(), &pt32); + polygon->points.push_back(pt32); + } +} + +visualization_msgs::msg::MarkerArray visualization::laneletDirectionAsMarkerArray( + const lanelet::ConstLanelets lanelets, const std::string & additional_namespace) +{ + visualization_msgs::msg::MarkerArray marker_array; + visualization_msgs::msg::Marker marker; + initLaneletDirectionMarker(&marker, additional_namespace + "lanelet direction"); + + for (auto lli = lanelets.begin(); lli != lanelets.end(); lli++) { + lanelet::ConstLanelet ll = *lli; + if (ll.hasAttribute(std::string("turn_direction"))) { + pushLaneletDirectionMarker(&marker, ll); + } + } + if (marker.points.empty()) { + return marker_array; + } + marker_array.markers.push_back(marker); + return marker_array; +} + +visualization_msgs::msg::MarkerArray visualization::autowareTrafficLightsAsMarkerArray( + const std::vector tl_reg_elems, + const std_msgs::msg::ColorRGBA c, const rclcpp::Duration duration, const double scale) +{ + visualization_msgs::msg::MarkerArray tl_marker_array; + if (tl_reg_elems.empty()) { + return tl_marker_array; + } + visualization_msgs::msg::Marker marker_tri; + visualization_msgs::msg::Marker marker_sph; + initLightMarker(&marker_sph, "traffic_light"); + visualization::initTrafficLightTriangleMarker(&marker_tri, "traffic_light_triangle", duration); + + for (auto tli = tl_reg_elems.begin(); tli != tl_reg_elems.end(); tli++) { + lanelet::ConstLineStrings3d light_bulbs; + lanelet::AutowareTrafficLightConstPtr tl = *tli; + + const auto lights = tl->trafficLights(); + for (const auto & lsp : lights) { + if (lsp.isLineString()) { // traffic lights can either polygons or linestrings + lanelet::ConstLineString3d ls = static_cast(lsp); + visualization::pushTrafficLightTriangleMarker(&marker_tri, ls, c, scale); + } + } + + tl_marker_array.markers.push_back(marker_tri); + + light_bulbs = tl->lightBulbs(); + for (auto ls : light_bulbs) { + lanelet::ConstLineString3d l = static_cast(ls); + for (auto pt : l) { + if (pt.hasAttribute("color")) { + if (inputLightMarker(&marker_sph, pt)) { + tl_marker_array.markers.push_back(marker_sph); + } + } + } + } + } + + return tl_marker_array; +} + +visualization_msgs::msg::MarkerArray visualization::generateTrafficLightIdMaker( + const std::vector tl_reg_elems, + const std_msgs::msg::ColorRGBA c, const rclcpp::Duration duration, const double scale) +{ + visualization_msgs::msg::MarkerArray tl_id_marker_array; + + for (auto tli = tl_reg_elems.begin(); tli != tl_reg_elems.end(); tli++) { + lanelet::ConstLineStrings3d light_bulbs; + lanelet::AutowareTrafficLightConstPtr tl = *tli; + + const auto lights = tl->trafficLights(); + for (const auto & lsp : lights) { + if (lsp.isLineString()) { // traffic lights can either polygons or + // linestrings + lanelet::ConstLineString3d ls = static_cast(lsp); + + visualization_msgs::msg::Marker marker; + marker.header.frame_id = "map"; + marker.header.stamp = rclcpp::Time(); + marker.ns = "traffic_light_id"; + marker.id = ls.id(); + marker.type = marker.TEXT_VIEW_FACING; + marker.lifetime = duration; + marker.action = marker.ADD; + marker.pose.position.x = (ls.front().x() + ls.back().x()) / 2; + marker.pose.position.y = (ls.front().y() + ls.back().y()) / 2; + marker.pose.position.z = ls.front().z() + 1.0; + marker.pose.orientation.x = 0.0; + marker.pose.orientation.y = 0.0; + marker.pose.orientation.z = 0.0; + marker.pose.orientation.w = 1.0; + marker.color = c; + marker.scale.z = scale; + marker.frame_locked = true; + marker.text = std::to_string(ls.id()); + tl_id_marker_array.markers.push_back(marker); + } + } + } + + return tl_id_marker_array; +} + +visualization_msgs::msg::MarkerArray visualization::detectionAreasAsMarkerArray( + const std::vector & da_reg_elems, + const std_msgs::msg::ColorRGBA c, const rclcpp::Duration duration) +{ + visualization_msgs::msg::MarkerArray marker_array; + visualization_msgs::msg::Marker marker; + visualization_msgs::msg::Marker line_marker; + + if (da_reg_elems.empty()) { + return marker_array; + } + + marker.header.frame_id = "map"; + marker.header.stamp = rclcpp::Time(); + marker.frame_locked = true; + marker.ns = "detection_area"; + marker.id = 0; + marker.type = visualization_msgs::msg::Marker::TRIANGLE_LIST; + marker.lifetime = duration; + marker.pose.position.x = 0.0; // p.x(); + marker.pose.position.y = 0.0; // p.y(); + marker.pose.position.z = 0.0; // p.z(); + marker.pose.orientation.x = 0.0; + marker.pose.orientation.y = 0.0; + marker.pose.orientation.z = 0.0; + marker.pose.orientation.w = 1.0; + marker.scale.x = 1.0; + marker.scale.y = 1.0; + marker.scale.z = 1.0; + marker.color.r = 1.0f; + marker.color.g = 1.0f; + marker.color.b = 1.0f; + marker.color.a = 0.999; + + std_msgs::msg::ColorRGBA line_c; + line_c.r = 0.5; + line_c.g = 0.5; + line_c.b = 0.5; + line_c.a = 0.999; + visualization::initLineStringMarker(&line_marker, "map", "detection_area_stopline", line_c); + + for (const auto & da_reg_elem : da_reg_elems) { + marker.points.clear(); + marker.colors.clear(); + marker.id = da_reg_elem->id(); + + // area visualization + const auto detection_areas = da_reg_elem->detectionAreas(); + for (const auto & detection_area : detection_areas) { + geometry_msgs::msg::Polygon geom_poly; + utils::conversion::toGeomMsgPoly(detection_area, &geom_poly); + + std::vector triangles; + polygon2Triangle(geom_poly, &triangles); + + for (auto tri : triangles) { + geometry_msgs::msg::Point tri0[3]; + + for (int i = 0; i < 3; i++) { + utils::conversion::toGeomMsgPt(tri.points[i], &tri0[i]); + marker.points.push_back(tri0[i]); + marker.colors.push_back(c); + } + } // for triangles0 + } // for detection areas + marker_array.markers.push_back(marker); + + // stop line visualization + visualization::pushLineStringMarker(&line_marker, da_reg_elem->stopLine(), line_c, 0.5); + } // for regulatory elements + + marker_array.markers.push_back(line_marker); + return marker_array; +} + +visualization_msgs::msg::MarkerArray visualization::noStoppingAreasAsMarkerArray( + const std::vector & no_reg_elems, + const std_msgs::msg::ColorRGBA c, const rclcpp::Duration duration) +{ + visualization_msgs::msg::MarkerArray marker_array; + visualization_msgs::msg::Marker marker; + visualization_msgs::msg::Marker line_marker; + + if (no_reg_elems.empty()) { + return marker_array; + } + + marker.header.frame_id = "map"; + marker.header.stamp = rclcpp::Time(); + marker.frame_locked = true; + marker.ns = "no_stopping_area"; + marker.id = 0; + marker.type = visualization_msgs::msg::Marker::TRIANGLE_LIST; + marker.lifetime = duration; + marker.pose.position.x = 0.0; // p.x(); + marker.pose.position.y = 0.0; // p.y(); + marker.pose.position.z = 0.0; // p.z(); + marker.pose.orientation.x = 0.0; + marker.pose.orientation.y = 0.0; + marker.pose.orientation.z = 0.0; + marker.pose.orientation.w = 1.0; + marker.scale.x = 1.0; + marker.scale.y = 1.0; + marker.scale.z = 1.0; + marker.color.r = 1.0f; + marker.color.g = 1.0f; + marker.color.b = 1.0f; + marker.color.a = 0.999; + + std_msgs::msg::ColorRGBA line_c; + line_c.r = 0.5; + line_c.g = 0.5; + line_c.b = 0.5; + line_c.a = 0.999; + visualization::initLineStringMarker(&line_marker, "map", "no_stopping_area_stopline", line_c); + + for (const auto & no_reg_elem : no_reg_elems) { + marker.points.clear(); + marker.colors.clear(); + marker.id = no_reg_elem->id(); + + // area visualization + const auto no_stopping_areas = no_reg_elem->noStoppingAreas(); + for (const auto & no_stopping_area : no_stopping_areas) { + geometry_msgs::msg::Polygon geom_poly; + utils::conversion::toGeomMsgPoly(no_stopping_area, &geom_poly); + + std::vector triangles; + polygon2Triangle(geom_poly, &triangles); + + for (auto tri : triangles) { + geometry_msgs::msg::Point tri0[3]; + + for (int i = 0; i < 3; i++) { + utils::conversion::toGeomMsgPt(tri.points[i], &tri0[i]); + marker.points.push_back(tri0[i]); + marker.colors.push_back(c); + } + } // for triangles0 + } // for no_stopping areas + marker_array.markers.push_back(marker); + const auto & stop_line = no_reg_elem->stopLine(); + // stop line visualization + if (stop_line) { + visualization::pushLineStringMarker(&line_marker, stop_line.value(), line_c, 0.5); + } + } // for regulatory elements + if (!line_marker.points.empty()) { + marker_array.markers.push_back(line_marker); + } + return marker_array; +} + +visualization_msgs::msg::MarkerArray visualization::pedestrianMarkingsAsMarkerArray( + const lanelet::ConstLineStrings3d & pedestrian_markings, const std_msgs::msg::ColorRGBA & c) +{ + visualization_msgs::msg::MarkerArray marker_array; + if (pedestrian_markings.empty()) { + return marker_array; + } + + visualization_msgs::msg::Marker marker = createPolygonMarker("pedestrian_marking", c); + for (const auto & linestring : pedestrian_markings) { + lanelet::ConstPolygon3d polygon; + if (utils::lineStringToPolygon(linestring, &polygon)) { + pushPolygonMarker(&marker, polygon, c); + } else { + RCLCPP_ERROR_STREAM( + rclcpp::get_logger("lanelet2_extension.visualization"), + "pedestrian marking " << linestring.id() << " failed conversion."); + } + } + + if (!marker.points.empty()) { + marker_array.markers.push_back(marker); + } + return marker_array; +} + +visualization_msgs::msg::MarkerArray visualization::parkingLotsAsMarkerArray( + const lanelet::ConstPolygons3d & parking_lots, const std_msgs::msg::ColorRGBA & c) +{ + visualization_msgs::msg::MarkerArray marker_array; + if (parking_lots.empty()) { + return marker_array; + } + + visualization_msgs::msg::Marker marker = createPolygonMarker("parking_lots", c); + for (const auto & polygon : parking_lots) { + pushPolygonMarker(&marker, polygon, c); + } + + if (!marker.points.empty()) { + marker_array.markers.push_back(marker); + } + return marker_array; +} +visualization_msgs::msg::MarkerArray visualization::parkingSpacesAsMarkerArray( + const lanelet::ConstLineStrings3d & parking_spaces, const std_msgs::msg::ColorRGBA & c) +{ + visualization_msgs::msg::MarkerArray marker_array; + if (parking_spaces.empty()) { + return marker_array; + } + + visualization_msgs::msg::Marker marker = createPolygonMarker("parking_space", c); + for (const auto & linestring : parking_spaces) { + lanelet::ConstPolygon3d polygon; + if (utils::lineStringWithWidthToPolygon(linestring, &polygon)) { + pushPolygonMarker(&marker, polygon, c); + } else { + std::cerr << "parking space " << linestring.id() << " failed conversion." << std::endl; + } + } + + if (!marker.points.empty()) { + marker_array.markers.push_back(marker); + } + return marker_array; +} + +visualization_msgs::msg::MarkerArray visualization::generateLaneletIdMarker( + const lanelet::ConstLanelets road_lanelets, const std_msgs::msg::ColorRGBA c, const double scale) +{ + visualization_msgs::msg::MarkerArray markers; + for (const auto & ll : road_lanelets) { + visualization_msgs::msg::Marker marker; + marker.header.frame_id = "map"; + marker.header.stamp = rclcpp::Clock().now(); + marker.ns = "lanelet_id"; + marker.id = ll.id(); + marker.type = marker.TEXT_VIEW_FACING; + marker.action = marker.ADD; + const auto centerline = ll.centerline(); + const size_t target_position_index = centerline.size() / 2; + const auto target_position = centerline[target_position_index]; + marker.pose.position.x = target_position.x(); + marker.pose.position.y = target_position.y(); + marker.pose.position.z = target_position.z(); + marker.pose.orientation.x = 0.0; + marker.pose.orientation.y = 0.0; + marker.pose.orientation.z = 0.0; + marker.pose.orientation.w = 1.0; + marker.color = c; + marker.scale.z = scale; + marker.frame_locked = true; + marker.text = std::to_string(ll.id()); + markers.markers.push_back(marker); + } + return markers; +} + +visualization_msgs::msg::MarkerArray visualization::obstaclePolygonsAsMarkerArray( + const lanelet::ConstPolygons3d & obstacle_polygons, const std_msgs::msg::ColorRGBA & c) +{ + visualization_msgs::msg::MarkerArray marker_array; + if (obstacle_polygons.empty()) { + return marker_array; + } + + visualization_msgs::msg::Marker marker = createPolygonMarker("obstacles", c); + for (const auto & polygon : obstacle_polygons) { + pushPolygonMarker(&marker, polygon, c); + } + + if (!marker.points.empty()) { + marker_array.markers.push_back(marker); + } + return marker_array; +} + +visualization_msgs::msg::MarkerArray visualization::lineStringsAsMarkerArray( + const std::vector line_strings, const std::string name_space, + const std_msgs::msg::ColorRGBA c, const double lss) +{ + visualization_msgs::msg::MarkerArray ls_marker_array; + if (line_strings.empty()) { + return ls_marker_array; + } + std::unordered_set added; + visualization_msgs::msg::Marker ls_marker; + visualization::initLineStringMarker(&ls_marker, "map", name_space, c); + + for (auto i = line_strings.begin(); i != line_strings.end(); i++) { + const lanelet::ConstLineString3d & ls = *i; + if (!exists(added, ls.id())) { + visualization::pushLineStringMarker(&ls_marker, ls, c, lss); + added.insert(ls.id()); + } + } + ls_marker_array.markers.push_back(ls_marker); + return ls_marker_array; +} + +visualization_msgs::msg::MarkerArray visualization::laneletsBoundaryAsMarkerArray( + const lanelet::ConstLanelets & lanelets, const std_msgs::msg::ColorRGBA c, + const bool viz_centerline, const std::string & additional_namespace) +{ + double lss = 0.1; // line string size + double lss_center = std::max(lss * 0.1, 0.02); + + std::unordered_set added; + visualization_msgs::msg::Marker left_line_strip, right_line_strip, center_line_strip; + visualization::initLineStringMarker( + &left_line_strip, "map", additional_namespace + "left_lane_bound", c); + visualization::initLineStringMarker( + &right_line_strip, "map", additional_namespace + "right_lane_bound", c); + visualization::initLineStringMarker( + ¢er_line_strip, "map", additional_namespace + "center_lane_line", c); + + for (auto li = lanelets.begin(); li != lanelets.end(); li++) { + lanelet::ConstLanelet lll = *li; + + lanelet::ConstLineString3d left_ls = lll.leftBound(); + lanelet::ConstLineString3d right_ls = lll.rightBound(); + lanelet::ConstLineString3d center_ls = lll.centerline(); + + if (!exists(added, left_ls.id())) { + visualization::pushLineStringMarker(&left_line_strip, left_ls, c, lss); + added.insert(left_ls.id()); + } + if (!exists(added, right_ls.id())) { + visualization::pushLineStringMarker(&right_line_strip, right_ls, c, lss); + added.insert(right_ls.id()); + } + if (viz_centerline && !exists(added, center_ls.id())) { + visualization::pushLineStringMarker(¢er_line_strip, center_ls, c, lss_center); + added.insert(center_ls.id()); + } + } + + visualization_msgs::msg::MarkerArray marker_array; + if (!left_line_strip.points.empty()) { + marker_array.markers.push_back(left_line_strip); + } + if (!right_line_strip.points.empty()) { + marker_array.markers.push_back(right_line_strip); + } + if (!center_line_strip.points.empty()) { + marker_array.markers.push_back(center_line_strip); + } + return marker_array; +} + +visualization_msgs::msg::MarkerArray visualization::trafficLightsAsTriangleMarkerArray( + const std::vector tl_reg_elems, const std_msgs::msg::ColorRGBA c, + const rclcpp::Duration duration, const double scale) +{ + // convert to to an array of linestrings and publish as marker array using + // existing function + + std::vector line_strings; + visualization_msgs::msg::Marker marker; + visualization::initTrafficLightTriangleMarker(&marker, "traffic_light_triangle", duration); + + for (auto tli = tl_reg_elems.begin(); tli != tl_reg_elems.end(); tli++) { + lanelet::TrafficLightConstPtr tl = *tli; + lanelet::LineString3d ls; + + auto lights = tl->trafficLights(); + for (auto lsp : lights) { + if (lsp.isLineString()) { // traffic lights can either polygons or linestrings + lanelet::ConstLineString3d ls = static_cast(lsp); + visualization::pushTrafficLightTriangleMarker(&marker, ls, c, scale); + } + } + } + + visualization_msgs::msg::MarkerArray marker_array; + marker_array.markers.push_back(marker); + return marker_array; +} + +visualization_msgs::msg::MarkerArray visualization::laneletsAsTriangleMarkerArray( + const std::string ns, const lanelet::ConstLanelets & lanelets, const std_msgs::msg::ColorRGBA c) +{ + visualization_msgs::msg::MarkerArray marker_array; + visualization_msgs::msg::Marker marker; + + if (lanelets.empty()) { + return marker_array; + } + + marker.header.frame_id = "map"; + marker.header.stamp = rclcpp::Time(); + marker.frame_locked = true; + marker.ns = ns; + marker.id = 0; + marker.type = visualization_msgs::msg::Marker::TRIANGLE_LIST; + marker.lifetime = rclcpp::Duration(0, 0); + marker.pose.position.x = 0.0; // p.x(); + marker.pose.position.y = 0.0; // p.y(); + marker.pose.position.z = 0.0; // p.z(); + marker.pose.orientation.x = 0.0; + marker.pose.orientation.y = 0.0; + marker.pose.orientation.z = 0.0; + marker.pose.orientation.w = 1.0; + marker.scale.x = 1.0; + marker.scale.y = 1.0; + marker.scale.z = 1.0; + marker.color.r = 1.0f; + marker.color.g = 1.0f; + marker.color.b = 1.0f; + marker.color.a = 0.999; + + for (auto ll : lanelets) { + std::vector triangles; + lanelet2Triangle(ll, &triangles); + + for (auto tri : triangles) { + geometry_msgs::msg::Point tri0[3]; + + for (int i = 0; i < 3; i++) { + utils::conversion::toGeomMsgPt(tri.points[i], &tri0[i]); + + marker.points.push_back(tri0[i]); + marker.colors.push_back(c); + } + } + } + if (!marker.points.empty()) { + marker_array.markers.push_back(marker); + } + + return marker_array; +} + +void visualization::initTrafficLightTriangleMarker( + visualization_msgs::msg::Marker * marker, const std::string ns, const rclcpp::Duration duration) +{ + if (marker == nullptr) { + std::cerr << __FUNCTION__ << ": marker is null pointer!" << std::endl; + return; + } + + marker->header.frame_id = "map"; + marker->header.stamp = rclcpp::Time(); + marker->frame_locked = true; + marker->ns = ns; + marker->id = 0; + marker->type = visualization_msgs::msg::Marker::TRIANGLE_LIST; + marker->lifetime = duration; + + marker->pose.position.x = 0.0; // p.x(); + marker->pose.position.y = 0.0; // p.y(); + marker->pose.position.z = 0.0; // p.z(); + marker->pose.orientation.x = 0.0; + marker->pose.orientation.y = 0.0; + marker->pose.orientation.z = 0.0; + marker->pose.orientation.w = 1.0; + marker->scale.x = 1.0; + marker->scale.y = 1.0; + marker->scale.z = 1.0; + marker->color.r = 1.0f; + marker->color.g = 1.0f; + marker->color.b = 1.0f; + marker->color.a = 0.999; +} + +void visualization::pushTrafficLightTriangleMarker( + visualization_msgs::msg::Marker * marker, const lanelet::ConstLineString3d ls, + const std_msgs::msg::ColorRGBA cl, const double scale) +{ + if (marker == nullptr) { + std::cerr << __FUNCTION__ << ": marker is null pointer!" << std::endl; + return; + } + + double h = 0.7; + if (ls.hasAttribute("height")) { + lanelet::Attribute attr = ls.attribute("height"); + h = std::stod(attr.value()); + } + + // construct triangles and add to marker + + // define polygon of traffic light border + Eigen::Vector3d v[4]; + v[0] << ls.front().x(), ls.front().y(), ls.front().z(); + v[1] << ls.back().x(), ls.back().y(), ls.back().z(); + v[2] << ls.back().x(), ls.back().y(), ls.back().z() + h; + v[3] << ls.front().x(), ls.front().y(), ls.front().z() + h; + + Eigen::Vector3d c = (v[0] + v[1] + v[2] + v[3]) / 4; + + if (scale > 0.0 && scale != 1.0) { + for (int i = 0; i < 4; i++) { + v[i] = (v[i] - c) * scale + c; + } + } + geometry_msgs::msg::Point tri0[3]; + utils::conversion::toGeomMsgPt(v[0], &tri0[0]); + utils::conversion::toGeomMsgPt(v[1], &tri0[1]); + utils::conversion::toGeomMsgPt(v[2], &tri0[2]); + geometry_msgs::msg::Point tri1[3]; + utils::conversion::toGeomMsgPt(v[0], &tri1[0]); + utils::conversion::toGeomMsgPt(v[2], &tri1[1]); + utils::conversion::toGeomMsgPt(v[3], &tri1[2]); + + for (int i = 0; i < 3; i++) { + marker->points.push_back(tri0[i]); + marker->colors.push_back(cl); + } + for (int i = 0; i < 3; i++) { + marker->points.push_back(tri1[i]); + marker->colors.push_back(cl); + } +} + +void visualization::initLineStringMarker( + visualization_msgs::msg::Marker * marker, const std::string frame_id, const std::string ns, + const std_msgs::msg::ColorRGBA c) +{ + if (marker == nullptr) { + RCLCPP_ERROR_STREAM( + rclcpp::get_logger("lanelet2_extension.visualization"), + __FUNCTION__ << ": marker is null pointer!"); + return; + } + + marker->header.frame_id = frame_id; + marker->header.stamp = rclcpp::Time(); + marker->frame_locked = true; + marker->ns = ns; + marker->action = visualization_msgs::msg::Marker::ADD; + marker->type = visualization_msgs::msg::Marker::TRIANGLE_LIST; + + marker->id = 0; + marker->pose.orientation.x = 0.0; + marker->pose.orientation.y = 0.0; + marker->pose.orientation.z = 0.0; + marker->pose.orientation.w = 1.0; + marker->scale.x = 1.0; + marker->scale.y = 1.0; + marker->scale.z = 1.0; + marker->color = c; +} + +void visualization::pushLineStringMarker( + visualization_msgs::msg::Marker * marker, const lanelet::ConstLineString3d & ls, + const std_msgs::msg::ColorRGBA c, const float lss) +{ + if (marker == nullptr) { + RCLCPP_ERROR_STREAM( + rclcpp::get_logger("lanelet2_extension.visualization"), + __FUNCTION__ << ": marker is null pointer!"); + return; + } + + // fill out lane line + if (ls.size() < 2) { + RCLCPP_ERROR_STREAM( + rclcpp::get_logger("lanelet2_extension.visualization"), + __FUNCTION__ << ": marker line size is 1 or 0!"); + return; + } + for (auto i = ls.begin(); i + 1 != ls.end(); i++) { + geometry_msgs::msg::Point p; + const float heading = std::atan2((*(i + 1)).y() - (*i).y(), (*(i + 1)).x() - (*i).x()); + + const float x_offset = lss * 0.5 * std::sin(heading); + const float y_offset = lss * 0.5 * std::cos(heading); + + p.x = (*i).x() + x_offset; + p.y = (*i).y() - y_offset; + p.z = (*i).z(); + marker->points.push_back(p); + p.x = (*i).x() - x_offset; + p.y = (*i).y() + y_offset; + p.z = (*i).z(); + marker->points.push_back(p); + p.x = (*(i + 1)).x() + x_offset; + p.y = (*(i + 1)).y() - y_offset; + p.z = (*(i + 1)).z(); + marker->points.push_back(p); + marker->colors.push_back(c); + p.x = (*(i + 1)).x() + x_offset; + p.y = (*(i + 1)).y() - y_offset; + p.z = (*(i + 1)).z(); + marker->points.push_back(p); + p.x = (*(i + 1)).x() - x_offset; + p.y = (*(i + 1)).y() + y_offset; + p.z = (*(i + 1)).z(); + marker->points.push_back(p); + p.x = (*i).x() - x_offset; + p.y = (*i).y() + y_offset; + p.z = (*i).z(); + marker->points.push_back(p); + marker->colors.push_back(c); + } +} + +} // namespace lanelet diff --git a/tmp/lanelet2_extension/package.xml b/tmp/lanelet2_extension/package.xml new file mode 100644 index 00000000..06867a3c --- /dev/null +++ b/tmp/lanelet2_extension/package.xml @@ -0,0 +1,36 @@ + + + lanelet2_extension + 0.1.0 + The lanelet2_extension package contains libraries to handle Lanelet2 format data. + + mitsudome-r + + Apache License 2.0 + + ament_cmake + ament_cmake_auto + + autoware_auto_mapping_msgs + geographiclib + geometry_msgs + lanelet2_core + lanelet2_io + lanelet2_maps + lanelet2_projection + lanelet2_routing + lanelet2_traffic_rules + lanelet2_validation + pugixml-dev + rclcpp + tf2 + visualization_msgs + + ament_cmake_gtest + ament_lint_auto + autoware_lint_common + + + ament_cmake + + diff --git a/tmp/lanelet2_extension/src/sample_code.cpp b/tmp/lanelet2_extension/src/sample_code.cpp new file mode 100644 index 00000000..a8e7aa9a --- /dev/null +++ b/tmp/lanelet2_extension/src/sample_code.cpp @@ -0,0 +1,120 @@ +// Copyright 2015-2019 Autoware Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lanelet2_extension/projection/mgrs_projector.hpp" +#include "lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp" + +#include + +#include +#include +#include + +#include +#include + +void loadingAutowareOSMFile(const std::string map_file_path) +{ + lanelet::LaneletMapPtr lanelet_map; + lanelet::ErrorMessages errors; + lanelet::GPSPoint gps_point; + gps_point.lat = 49.0; + gps_point.lon = 8.4; + lanelet::Origin origin(gps_point); + lanelet::projection::UtmProjector projector(lanelet::Origin(gps_point), true, false); + + // Autoware OSM file parser is registered into lanelet2 library. + // Therefore, you can used it by just specifying "autoware_osm_handler" parser + // in load function. + lanelet_map = lanelet::load(map_file_path, "autoware_osm_handler", projector, &errors); + + // If you want to use default parser, explicitly name the parser + lanelet_map = lanelet::load(map_file_path, "osm_handler", projector, &errors); +} + +void usingMGRSProjector() +{ + // MGRS Projector projects lat/lon to x,y,z point in MGRS 100km grid. + // The origin is automatically calculated so you don't have to select any + // origin. + lanelet::projection::MGRSProjector projector; + + // Let's convert lat/lng point into mgrs xyz point. + lanelet::GPSPoint gps_point; + gps_point.lat = 49.0; + gps_point.lon = 8.4; + gps_point.ele = 0.0; + + lanelet::BasicPoint3d mgrs_point = projector.forward(gps_point); + std::cout << mgrs_point << std::endl; + + // You can get MGRS Code of projected grid. + std::string mgrs_grid = projector.getProjectedMGRSGrid(); + std::cout << mgrs_grid << std::endl; + + // You can also reverse project from MGRS point to lat/lon. + // You have to set which MGRS grid it is from or it will reuse last projected + // grid + lanelet::GPSPoint projected_gps_point = projector.reverse(mgrs_point); + std::cout << projected_gps_point.lat << " " << projected_gps_point.lon << std::endl; + lanelet::GPSPoint projected_gps_point2 = projector.reverse(mgrs_point, mgrs_grid); + std::cout << projected_gps_point2.lat << " " << projected_gps_point2.lon << " " << std::endl; +} + +void usingAutowareTrafficLight(const std::string map_file_path) +{ + lanelet::LaneletMapPtr lanelet_map; + lanelet::ErrorMessages errors; + lanelet::projection::MGRSProjector projector; + lanelet_map = lanelet::load(map_file_path, "autoware_osm_handler", projector, &errors); + + for (auto lanelet : lanelet_map->laneletLayer) { + // You can access to traffic light element as AutowareTrafficLight class + auto autoware_traffic_lights = + lanelet.regulatoryElementsAs(); + for (auto light : autoware_traffic_lights) { + // You can access to the position of each lamps(light bulb) in traffic + // light + for (auto light_bulb_string : light->lightBulbs()) { + std::cout << light_bulb_string.id() << std::endl; + } + // Since AutowareTrafficLight class is inheriting lanelet::TrafficLight + // class, you can also access to outline of traffic light by the same + // method. + for (auto light_string : light->trafficLights()) { + std::cout << light_string.id() << std::endl; + } + } + + // You can also access to same traffic light element as default TrafficLight + // class + auto traffic_lights = lanelet.regulatoryElementsAs(); + for (auto light : traffic_lights) { + for (auto light_string : light->trafficLights()) { + std::cout << light_string.id() << std::endl; + } + } + } +} + +int main(int argc, char * argv[]) +{ + rclcpp::init(argc, argv); + auto node = rclcpp::Node::make_shared("sample_code"); + const std::string map_file_path = node->declare_parameter("map_file", ""); + loadingAutowareOSMFile(map_file_path); + usingMGRSProjector(); + usingAutowareTrafficLight(map_file_path); + return 0; +} diff --git a/tmp/lanelet2_extension/src/validation.cpp b/tmp/lanelet2_extension/src/validation.cpp new file mode 100644 index 00000000..fdaadb66 --- /dev/null +++ b/tmp/lanelet2_extension/src/validation.cpp @@ -0,0 +1,174 @@ +// Copyright 2015-2019 Autoware Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lanelet2_extension/projection/mgrs_projector.hpp" +#include "lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace +{ +namespace keyword +{ +constexpr const char * Id = "id"; +constexpr const char * Osm = "osm"; +constexpr const char * Tag = "tag"; +constexpr const char * Key = "k"; +constexpr const char * Node = "node"; +constexpr const char * Elevation = "ele"; +} // namespace keyword + +void printUsage() +{ + std::cout << "Usage:" << std::endl + << "ros2 run lanelet2_extension autoware_lanelet2_validation" + " --ros-args -p map_file:=" + << std::endl; +} +} // namespace + +void validateElevationTag(const std::string filename) +{ + pugi::xml_document doc; + auto result = doc.load_file(filename.c_str()); + if (!result) { + std::cerr << result.description() << std::endl; + exit(1); + } + + auto osmNode = doc.child(keyword::Osm); + for (auto node = osmNode.child(keyword::Node); node; // NOLINT + node = node.next_sibling(keyword::Node)) { + const auto id = node.attribute(keyword::Id).as_llong(lanelet::InvalId); + if (!node.find_child_by_attribute(keyword::Tag, keyword::Key, keyword::Elevation)) { + std::cerr << "failed to find elevation tag for node: " << id << std::endl; + } + } +} + +void validateTrafficLight(const lanelet::LaneletMapPtr lanelet_map) +{ + if (!lanelet_map) { + std::cerr << "Missing map. Are you sure you set correct path for map?" << std::endl; + exit(1); + } + + for (auto lanelet : lanelet_map->laneletLayer) { + auto autoware_traffic_lights = + lanelet.regulatoryElementsAs(); + if (autoware_traffic_lights.empty()) { + continue; + } + for (auto light : autoware_traffic_lights) { + if (light->lightBulbs().size() == 0) { + std::cerr << "regulatory element traffic light " << light->id() + << " is missing optional light_bulb member. You won't be able to use region_tlr " + "node with this map" + << std::endl; + } + for (auto light_string : light->lightBulbs()) { + if (!light_string.hasAttribute("traffic_light_id")) { + std::cerr << "light_bulb " << light_string.id() << " is missing traffic_light_id tag" + << std::endl; + } + } + for (auto base_string_or_poly : light->trafficLights()) { + if (!base_string_or_poly.isLineString()) { + std::cerr + << "traffic_light " << base_string_or_poly.id() + << " is polygon, and only linestring class is currently supported for traffic lights" + << std::endl; + } + auto base_string = static_cast(base_string_or_poly); + if (!base_string.hasAttribute("height")) { + std::cerr << "traffic_light " << base_string.id() << " is missing height tag" + << std::endl; + } + } + } + } +} + +void validateTurnDirection(const lanelet::LaneletMapPtr lanelet_map) +{ + if (!lanelet_map) { + std::cerr << "Missing map. Are you sure you set correct path for map?" << std::endl; + exit(1); + } + + lanelet::traffic_rules::TrafficRulesPtr traffic_rules = + lanelet::traffic_rules::TrafficRulesFactory::create( + lanelet::Locations::Germany, lanelet::Participants::Vehicle); + lanelet::routing::RoutingGraphPtr vehicle_graph = + lanelet::routing::RoutingGraph::build(*lanelet_map, *traffic_rules); + + for (const auto & lanelet : lanelet_map->laneletLayer) { + if (!traffic_rules->canPass(lanelet)) { + continue; + } + + const auto conflicting_lanelets_or_areas = vehicle_graph->conflicting(lanelet); + if (conflicting_lanelets_or_areas.size() == 0) { + continue; + } + if (!lanelet.hasAttribute("turn_direction")) { + std::cerr + << "lanelet " << lanelet.id() + << " seems to be intersecting other lanelet, but does not have turn_direction tagging." + << std::endl; + } + } +} + +int main(int argc, char * argv[]) +{ + rclcpp::init(argc, argv); + auto node = rclcpp::Node::make_shared("autoware_lanelet_validation"); + + std::string map_path; + try { + map_path = node->declare_parameter("map_file"); + } catch (...) { + std::cerr << "failed find map_file parameter! No file to load" << std::endl; + printUsage(); + return 1; + } + lanelet::LaneletMapPtr lanelet_map; + lanelet::ErrorMessages errors; + lanelet::projection::MGRSProjector projector; + lanelet_map = lanelet::load(map_path, "autoware_osm_handler", projector, &errors); + + std::cout << "starting validation" << std::endl; + + validateElevationTag(map_path); + validateTrafficLight(lanelet_map); + validateTurnDirection(lanelet_map); + + std::cout << "finished validation" << std::endl; + + return 0; +} diff --git a/tmp/lanelet2_extension/test/src/test_message_conversion.cpp b/tmp/lanelet2_extension/test/src/test_message_conversion.cpp new file mode 100644 index 00000000..745f98c9 --- /dev/null +++ b/tmp/lanelet2_extension/test/src/test_message_conversion.cpp @@ -0,0 +1,158 @@ +// Copyright 2015-2019 Autoware Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "lanelet2_extension/utility/message_conversion.hpp" +#include "lanelet2_extension/utility/query.hpp" + +#include +#include + +using lanelet::Lanelet; +using lanelet::LineString3d; +using lanelet::Point3d; +using lanelet::utils::getId; +using lanelet::utils::conversion::toGeomMsgPt; + +class TestSuite : public ::testing::Test +{ +public: + TestSuite() : single_lanelet_map_ptr(new lanelet::LaneletMap()) + { + Point3d p1, p2, p3, p4, p5, p6, p7; + LineString3d traffic_light_base, traffic_light_bulbs, stop_line; + + p1 = Point3d(getId(), 0., 0., 0.); + p2 = Point3d(getId(), 0., 1., 0.); + + p3 = Point3d(getId(), 1., 0., 0.); + p4 = Point3d(getId(), 1., 1., 0.); + + LineString3d ls_left(getId(), {p1, p2}); // NOLINT + LineString3d ls_right(getId(), {p3, p4}); // NOLINT + + Lanelet lanelet(getId(), ls_left, ls_right); + + single_lanelet_map_ptr->add(lanelet); + } + ~TestSuite() {} + lanelet::LaneletMapPtr single_lanelet_map_ptr; + +private: +}; + +TEST_F(TestSuite, BinMsgConversion) +{ + autoware_auto_mapping_msgs::msg::HADMapBin bin_msg; + lanelet::LaneletMapPtr regenerated_map(new lanelet::LaneletMap); + + lanelet::utils::conversion::toBinMsg(single_lanelet_map_ptr, &bin_msg); + + ASSERT_NE(0U, bin_msg.data.size()) << "converted bin message does not have any data"; + + lanelet::utils::conversion::fromBinMsg(bin_msg, regenerated_map); + + auto original_lanelet = lanelet::utils::query::laneletLayer(single_lanelet_map_ptr); + auto regenerated_lanelet = lanelet::utils::query::laneletLayer(regenerated_map); + + ASSERT_EQ(original_lanelet.front().id(), regenerated_lanelet.front().id()) + << "regenerated map has different id"; +} + +TEST_F(TestSuite, ToGeomMsgPt) +{ + Point3d lanelet_pt(getId(), -0.1, 0.2, 3.0); + + geometry_msgs::msg::Point32 geom_pt32; + geom_pt32.x = -0.1; + geom_pt32.y = 0.2; + geom_pt32.z = 3.0; + + geometry_msgs::msg::Point geom_pt; + toGeomMsgPt(geom_pt32, &geom_pt); + ASSERT_FLOAT_EQ(geom_pt32.x, geom_pt.x) + << " converted value is different from original geometry_msgs::msg::Point"; + ASSERT_FLOAT_EQ(geom_pt32.y, geom_pt.y) + << " converted value is different from original geometry_msgs::msg::Point"; + ASSERT_FLOAT_EQ(geom_pt32.z, geom_pt.z) + << " converted value is different from original geometry_msgs::msg::Point"; + + geom_pt = toGeomMsgPt(geom_pt32); + ASSERT_FLOAT_EQ(geom_pt32.x, geom_pt.x) + << " converted value is different from original geometry_msgs::msg::Point"; + ASSERT_FLOAT_EQ(geom_pt32.y, geom_pt.y) + << " converted value is different from original geometry_msgs::msg::Point"; + ASSERT_FLOAT_EQ(geom_pt32.z, geom_pt.z) + << " converted value is different from original geometry_msgs::msg::Point"; + + toGeomMsgPt(lanelet_pt.basicPoint(), &geom_pt); + ASSERT_DOUBLE_EQ(lanelet_pt.basicPoint().x(), geom_pt.x) + << " converted value is different from original " + "lanelet::basicPoint"; + ASSERT_DOUBLE_EQ(lanelet_pt.basicPoint().y(), geom_pt.y) + << " converted value is different from original " + "lanelet::basicPoint"; + ASSERT_DOUBLE_EQ(lanelet_pt.basicPoint().z(), geom_pt.z) + << " converted value is different from original " + "lanelet::basicPoint"; + + geom_pt = toGeomMsgPt(lanelet_pt.basicPoint()); + ASSERT_DOUBLE_EQ(lanelet_pt.basicPoint().x(), geom_pt.x) + << " converted value is different from original " + "lanelet::basicPoint"; + ASSERT_DOUBLE_EQ(lanelet_pt.basicPoint().y(), geom_pt.y) + << " converted value is different from original " + "lanelet::basicPoint"; + ASSERT_DOUBLE_EQ(lanelet_pt.basicPoint().z(), geom_pt.z) + << " converted value is different from original " + "lanelet::basicPoint"; + + toGeomMsgPt(lanelet_pt, &geom_pt); + ASSERT_DOUBLE_EQ(lanelet_pt.x(), geom_pt.x) + << " converted value is different from original lanelet::Point3d"; + ASSERT_DOUBLE_EQ(lanelet_pt.y(), geom_pt.y) + << " converted value is different from original lanelet::Point3d"; + ASSERT_DOUBLE_EQ(lanelet_pt.z(), geom_pt.z) + << " converted value is different from original lanelet::Point3d"; + + geom_pt = toGeomMsgPt(lanelet_pt); + ASSERT_DOUBLE_EQ(lanelet_pt.x(), geom_pt.x) + << " converted value is different from original lanelet::Point3d"; + ASSERT_DOUBLE_EQ(lanelet_pt.y(), geom_pt.y) + << " converted value is different from original lanelet::Point3d"; + ASSERT_DOUBLE_EQ(lanelet_pt.z(), geom_pt.z) + << " converted value is different from original lanelet::Point3d"; + + lanelet::ConstPoint2d point_2d = lanelet::utils::to2D(lanelet_pt); + + toGeomMsgPt(point_2d, &geom_pt); + ASSERT_DOUBLE_EQ(point_2d.x(), geom_pt.x) + << " converted value is different from original lanelet::Point2d"; + ASSERT_DOUBLE_EQ(point_2d.y(), geom_pt.y) + << " converted value is different from original lanelet::Point2d"; + ASSERT_DOUBLE_EQ(0.0, geom_pt.z) + << " converted value is different from original lanelet::Point2d"; + + geom_pt = toGeomMsgPt(point_2d); + ASSERT_DOUBLE_EQ(point_2d.x(), geom_pt.x) + << " converted value is different from original lanelet::Point2d"; + ASSERT_DOUBLE_EQ(point_2d.y(), geom_pt.y) + << " converted value is different from original lanelet::Point2d"; + ASSERT_DOUBLE_EQ(0.0, geom_pt.z) + << " converted value is different from original lanelet::Point2d"; +} + +int main(int argc, char ** argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tmp/lanelet2_extension/test/src/test_projector.cpp b/tmp/lanelet2_extension/test/src/test_projector.cpp new file mode 100644 index 00000000..65304df2 --- /dev/null +++ b/tmp/lanelet2_extension/test/src/test_projector.cpp @@ -0,0 +1,82 @@ +// Copyright 2015-2019 Autoware Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lanelet2_extension/projection/mgrs_projector.hpp" + +#include +#include + +class TestSuite : public ::testing::Test +{ +public: + TestSuite() {} + ~TestSuite() {} +}; + +TEST(TestSuite, ForwardProjection) +{ + lanelet::projection::MGRSProjector projector; + // lat/lon in Tokyo + lanelet::GPSPoint gps_point; + gps_point.lat = 35.652832; + gps_point.lon = 139.839478; + gps_point.ele = 12.3456789; + lanelet::BasicPoint3d mgrs_point = projector.forward(gps_point); + + // projected z value should not change + ASSERT_DOUBLE_EQ(mgrs_point.z(), gps_point.ele) + << "Forward projected z value should be " << gps_point.ele; + + // https://www.movable-type.co.uk/scripts/latlong-utm-mgrs.html + // round the projected value to mm since the above reference only gives value + // in mm precision + ASSERT_EQ(projector.getProjectedMGRSGrid(), "54SUE") << "Projected grid should be " + << "54SUE"; + double rounded_x_mm = round(mgrs_point.x() * 1000) / 1000.0; + ASSERT_DOUBLE_EQ(rounded_x_mm, 94946.081) << "Forward projected x value should be " << 94946.081; + double rounded_y_mm = round(mgrs_point.y() * 1000) / 1000.0; + ASSERT_DOUBLE_EQ(rounded_y_mm, 46063.748) << "Forward projected y value should be " << 46063.748; +} + +TEST(TestSuite, ReverseProjection) +{ + lanelet::projection::MGRSProjector projector; + lanelet::BasicPoint3d mgrs_point; + mgrs_point.x() = 94946.0; + mgrs_point.y() = 46063.0; + mgrs_point.z() = 12.3456789; + + projector.setMGRSCode("54SUE"); + lanelet::GPSPoint gps_point = projector.reverse(mgrs_point); + + // projected z value should not change + ASSERT_DOUBLE_EQ(gps_point.ele, mgrs_point.z()) + << "Reverse projected z value should be " << mgrs_point.z(); + + // https://www.movable-type.co.uk/scripts/latlong-utm-mgrs.html + // round the projected value since the above reference only gives value up to + // precision of 1e-8 + double rounded_lat = round(gps_point.lat * 1e8) / 1e8; + ASSERT_DOUBLE_EQ(rounded_lat, 35.65282525) + << "Reverse projected latitude value should be " << 35.65282525; + double rounded_lon = round(gps_point.lon * 1e8) / 1e8; + ASSERT_DOUBLE_EQ(rounded_lon, 139.83947721) + << "Reverse projected longitude value should be " << 139.83947721; +} + +int main(int argc, char ** argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tmp/lanelet2_extension/test/src/test_query.cpp b/tmp/lanelet2_extension/test/src/test_query.cpp new file mode 100644 index 00000000..ef65772d --- /dev/null +++ b/tmp/lanelet2_extension/test/src/test_query.cpp @@ -0,0 +1,131 @@ +// Copyright 2015-2019 Autoware Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lanelet2_extension/utility/query.hpp" + +#include +#include + +using lanelet::Lanelet; +using lanelet::LineString3d; +using lanelet::LineStringOrPolygon3d; +using lanelet::Point3d; +using lanelet::Points3d; +using lanelet::utils::getId; + +class TestSuite : public ::testing::Test +{ +public: + TestSuite() : sample_map_ptr(new lanelet::LaneletMap()) + { // NOLINT + // create sample lanelets + Point3d p1, p2, p3, p4; + + p1 = Point3d(getId(), 0., 0., 0.); + p2 = Point3d(getId(), 0., 1., 0.); + + p3 = Point3d(getId(), 1., 0., 0.); + p4 = Point3d(getId(), 1., 1., 0.); + + LineString3d ls_left(getId(), {p1, p2}); // NOLINT + LineString3d ls_right(getId(), {p3, p4}); // NOLINT + + Lanelet road_lanelet(getId(), ls_left, ls_right); + road_lanelet.attributes()[lanelet::AttributeName::Subtype] = + lanelet::AttributeValueString::Road; + + Lanelet crosswalk_lanelet(getId(), ls_left, ls_right); + crosswalk_lanelet.attributes()[lanelet::AttributeName::Subtype] = + lanelet::AttributeValueString::Crosswalk; + + // create sample traffic light + Point3d p5, p6, p7, p8, p9, p10, p11, p12; + LineString3d traffic_light_base, traffic_light_bulbs, stop_line; + + p6 = Point3d(getId(), 0., 1., 4.); + p7 = Point3d(getId(), 1., 1., 4.); + + p8 = Point3d(getId(), 0., 1., 4.5); + p9 = Point3d(getId(), 0.5, 1., 4.5); + p10 = Point3d(getId(), 1., 1., 4.5); + + p11 = Point3d(getId(), 0., 0., 0.); + p12 = Point3d(getId(), 1., 0., 0.); + + traffic_light_base = LineString3d(getId(), Points3d{p6, p7}); // NOLINT + traffic_light_bulbs = LineString3d(getId(), Points3d{p8, p9, p10}); // NOLINT + stop_line = LineString3d(getId(), Points3d{p11, p12}); // NOLINT + + auto tl = lanelet::autoware::AutowareTrafficLight::make( + getId(), lanelet::AttributeMap(), {traffic_light_base}, stop_line, + {traffic_light_bulbs}); // NOLINT + + road_lanelet.addRegulatoryElement(tl); + + // add items to map + sample_map_ptr->add(road_lanelet); + sample_map_ptr->add(crosswalk_lanelet); + } + ~TestSuite() {} + + lanelet::LaneletMapPtr sample_map_ptr; + +private: +}; + +TEST_F(TestSuite, QueryLanelets) +{ + lanelet::ConstLanelets all_lanelets = lanelet::utils::query::laneletLayer(sample_map_ptr); + ASSERT_EQ(2U, all_lanelets.size()) << "failed to retrieve all lanelets"; + + lanelet::ConstLanelets subtype_lanelets = + lanelet::utils::query::subtypeLanelets(all_lanelets, lanelet::AttributeValueString::Road); + ASSERT_EQ(1U, subtype_lanelets.size()) << "failed to retrieve road lanelet by subtypeLanelets"; + + lanelet::ConstLanelets road_lanelets = lanelet::utils::query::roadLanelets(all_lanelets); + ASSERT_EQ(1U, road_lanelets.size()) << "failed to retrieve road lanelets"; + + lanelet::ConstLanelets crosswalk_lanelets = + lanelet::utils::query::crosswalkLanelets(all_lanelets); + ASSERT_EQ(1U, crosswalk_lanelets.size()) << "failed to retrieve crosswalk lanelets"; +} + +TEST_F(TestSuite, QueryTrafficLights) +{ + lanelet::ConstLanelets all_lanelets = lanelet::utils::query::laneletLayer(sample_map_ptr); + + auto traffic_lights = lanelet::utils::query::trafficLights(all_lanelets); + ASSERT_EQ(1U, traffic_lights.size()) << "failed to retrieve traffic lights"; + + auto autoware_traffic_lights = lanelet::utils::query::autowareTrafficLights(all_lanelets); + ASSERT_EQ(1U, autoware_traffic_lights.size()) << "failed to retrieve autoware traffic lights"; +} + +TEST_F(TestSuite, QueryStopLine) +{ + lanelet::ConstLanelets all_lanelets = lanelet::utils::query::laneletLayer(sample_map_ptr); + lanelet::ConstLanelets road_lanelets = lanelet::utils::query::roadLanelets(all_lanelets); + + auto stop_lines = lanelet::utils::query::stopLinesLanelets(all_lanelets); + ASSERT_EQ(1U, stop_lines.size()) << "failed to retrieve stop lines from all lanelets"; + + auto stop_lines2 = lanelet::utils::query::stopLinesLanelet(road_lanelets.front()); + ASSERT_EQ(1U, stop_lines2.size()) << "failed to retrieve stop lines from a lanelet"; +} + +int main(int argc, char ** argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tmp/lanelet2_extension/test/src/test_regulatory_elements.cpp b/tmp/lanelet2_extension/test/src/test_regulatory_elements.cpp new file mode 100644 index 00000000..5512d7e0 --- /dev/null +++ b/tmp/lanelet2_extension/test/src/test_regulatory_elements.cpp @@ -0,0 +1,124 @@ +// Copyright 2015-2019 Autoware Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp" + +#include +#include +#include +#include + +#include + +using lanelet::LineString3d; +using lanelet::LineStringOrPolygon3d; +using lanelet::Point3d; +using lanelet::Points3d; +using lanelet::utils::getId; + +namespace +{ +template +std::vector convertToVector(T item) +{ + std::vector vector = {item}; + return vector; +} +} // namespace + +class TestSuite : public ::testing::Test +{ +public: + TestSuite() {} + ~TestSuite() {} +}; + +TEST(TestSuite, FactoryConstructsTrafficLight) +{ + Point3d p1, p2, p3, p4, p5, p6, p7; + LineStringOrPolygon3d traffic_light_base; + LineString3d traffic_light_bulbs, stop_line; + + p1 = Point3d(getId(), 0., 1., 4.); + p2 = Point3d(getId(), 1., 1., 4.); + + p3 = Point3d(getId(), 0., 1., 4.5); + p4 = Point3d(getId(), 0.5, 1., 4.5); + p5 = Point3d(getId(), 1., 1., 4.5); + + p6 = Point3d(getId(), 0., 0., 0.); + p7 = Point3d(getId(), 1., 0., 0.); + + Points3d base = {p1, p2}; + Points3d bulbs = {p3, p4, p5}; + Points3d stop = {p6, p7}; + + traffic_light_base = LineString3d(getId(), base); + traffic_light_bulbs = LineString3d(getId(), bulbs); + stop_line = LineString3d(getId(), stop); + + auto tl = lanelet::autoware::AutowareTrafficLight::make( + getId(), lanelet::AttributeMap(), convertToVector(traffic_light_base), stop_line, + convertToVector(traffic_light_bulbs)); + + auto factoryTl = lanelet::RegulatoryElementFactory::create( + tl->attribute(lanelet::AttributeName::Subtype).value(), + std::const_pointer_cast(tl->constData())); + EXPECT_TRUE(!!std::dynamic_pointer_cast(factoryTl)); +} + +TEST(TestSuite, TrafficLightWorksAsExpected) +{ // NOLINT + Point3d p1, p2, p3, p4, p5, p6, p7; + LineStringOrPolygon3d traffic_light_base, traffic_light_base2; + LineString3d traffic_light_bulbs, traffic_light_bulbs2, stop_line; + + p1 = Point3d(getId(), 0., 1., 4.); + p2 = Point3d(getId(), 1., 1., 4.); + + p3 = Point3d(getId(), 0., 1., 4.5); + p4 = Point3d(getId(), 0.5, 1., 4.5); + p5 = Point3d(getId(), 1., 1., 4.5); + + p6 = Point3d(getId(), 0., 0., 0.); + p7 = Point3d(getId(), 1., 0., 0.); + + Points3d base = {p1, p2}; + Points3d bulbs = {p3, p4, p5}; + Points3d stop = {p6, p7}; + + traffic_light_base = {LineString3d(getId(), base)}; + traffic_light_base2 = {LineString3d(getId(), base)}; + traffic_light_bulbs = {LineString3d(getId(), bulbs)}; + traffic_light_bulbs2 = {LineString3d(getId(), bulbs)}; + stop_line = LineString3d(getId(), stop); + + auto tl = lanelet::autoware::AutowareTrafficLight::make( + getId(), lanelet::AttributeMap(), convertToVector(traffic_light_base), stop_line, + convertToVector(traffic_light_bulbs)); + tl->setStopLine(stop_line); + EXPECT_EQ(stop_line, tl->stopLine()); + tl->addTrafficLight(traffic_light_base2); + EXPECT_EQ(2ul, tl->trafficLights().size()); + tl->addLightBulbs(traffic_light_bulbs2); + EXPECT_EQ(2ul, tl->lightBulbs().size()); + tl->removeLightBulbs(traffic_light_bulbs); + EXPECT_EQ(1ul, tl->lightBulbs().size()); +} + +int main(int argc, char ** argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tmp/lanelet2_extension/test/src/test_utilities.cpp b/tmp/lanelet2_extension/test/src/test_utilities.cpp new file mode 100644 index 00000000..966b093c --- /dev/null +++ b/tmp/lanelet2_extension/test/src/test_utilities.cpp @@ -0,0 +1,109 @@ +// Copyright 2015-2019 Autoware Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lanelet2_extension/utility/utilities.hpp" + +#include +#include +#include + +#include + +using lanelet::Lanelet; +using lanelet::LineString3d; +using lanelet::Point3d; +using lanelet::utils::getId; + +class TestSuite : public ::testing::Test +{ +public: + TestSuite() : sample_map_ptr(new lanelet::LaneletMap()) + { // NOLINT + // create sample lanelets + Point3d p1, p2, p3, p4, p5, p6, p7, p8, p9, p10; + + p1 = Point3d(getId(), 0., 0., 0.); + p2 = Point3d(getId(), 0., 1., 0.); + p3 = Point3d(getId(), 1., 0., 0.); + p4 = Point3d(getId(), 1., 1., 0.); + + LineString3d ls_left(getId(), {p1, p2}); // NOLINT + LineString3d ls_right(getId(), {p3, p4}); // NOLINT + + p5 = Point3d(getId(), 0., 2., 0.); + p6 = Point3d(getId(), 1., 2., 0.); + + LineString3d ls_left2(getId(), {p2, p5}); // NOLINT + LineString3d ls_right2(getId(), {p4, p6}); // NOLINT + + p7 = Point3d(getId(), 0., 3., 0.); + p8 = Point3d(getId(), 1., 3., 0.); + + LineString3d ls_left3(getId(), {p5, p7}); // NOLINT + LineString3d ls_right3(getId(), {p6, p8}); // NOLINT + + p9 = Point3d(getId(), 0., 1., 0.); + p10 = Point3d(getId(), 1., 1., 0.); + + LineString3d ls_left4(getId(), {p9, p5}); // NOLINT + LineString3d ls_right4(getId(), {p10, p6}); // NOLINT + + road_lanelet = Lanelet(getId(), ls_left, ls_right); + road_lanelet.attributes()[lanelet::AttributeName::Subtype] = + lanelet::AttributeValueString::Road; + + next_lanelet = Lanelet(getId(), ls_left2, ls_right2); + next_lanelet.attributes()[lanelet::AttributeName::Subtype] = + lanelet::AttributeValueString::Road; + + next_lanelet2 = Lanelet(getId(), ls_left3, ls_right3); + next_lanelet2.attributes()[lanelet::AttributeName::Subtype] = + lanelet::AttributeValueString::Road; + + merging_lanelet = Lanelet(getId(), ls_left4, ls_right4); + merging_lanelet.attributes()[lanelet::AttributeName::Subtype] = + lanelet::AttributeValueString::Road; + + sample_map_ptr->add(road_lanelet); + sample_map_ptr->add(next_lanelet); + sample_map_ptr->add(next_lanelet2); + sample_map_ptr->add(merging_lanelet); + } + ~TestSuite() {} + + lanelet::LaneletMapPtr sample_map_ptr; + Lanelet road_lanelet; + Lanelet next_lanelet; + Lanelet next_lanelet2; + Lanelet merging_lanelet; + +private: +}; + +TEST_F(TestSuite, OverwriteLaneletsCenterline) +{ + double resolution = 5.0; + bool force_overwrite = false; + lanelet::utils::overwriteLaneletsCenterline(sample_map_ptr, resolution, force_overwrite); + + for (const auto & lanelet : sample_map_ptr->laneletLayer) { + ASSERT_TRUE(lanelet.hasCustomCenterline()) << "failed to calculate fine centerline"; + } +} + +int main(int argc, char ** argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From 65fcdf2f3fcaa82b1753c23452b2bd44853cf9e5 Mon Sep 17 00:00:00 2001 From: Daisuke Nishimatsu <42202095+wep21@users.noreply.github.com> Date: Thu, 9 Dec 2021 17:33:10 +0900 Subject: [PATCH 02/36] fix: fix isInLanelet (#770) (#151) Signed-off-by: wep21 Co-authored-by: Fumiya Watanabe --- tmp/lanelet2_extension/lib/utilities.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tmp/lanelet2_extension/lib/utilities.cpp b/tmp/lanelet2_extension/lib/utilities.cpp index ebd64ca5..072f0790 100644 --- a/tmp/lanelet2_extension/lib/utilities.cpp +++ b/tmp/lanelet2_extension/lib/utilities.cpp @@ -588,8 +588,9 @@ bool isInLanelet( const geometry_msgs::msg::Pose & current_pose, const lanelet::ConstLanelet & lanelet, const double radius) { + constexpr double eps = 1.0e-9; const lanelet::BasicPoint2d p(current_pose.position.x, current_pose.position.y); - if (boost::geometry::distance(p, lanelet.polygon2d().basicPolygon()) < radius) { + if (boost::geometry::distance(p, lanelet.polygon2d().basicPolygon()) < radius + eps) { return true; } return false; From d9a8f088bf24d8cc2c5d8e2e78b4ee5f38e94b8b Mon Sep 17 00:00:00 2001 From: Daisuke Nishimatsu <42202095+wep21@users.noreply.github.com> Date: Mon, 13 Dec 2021 13:33:47 +0900 Subject: [PATCH 03/36] fix: fix angle diff calculation of mission_planner (#772) (#152) Signed-off-by: wep21 Co-authored-by: Takayuki Murooka Co-authored-by: Takeshi Miura <57553950+1222-takeshi@users.noreply.github.com> Co-authored-by: Kenji Miyake <31987104+kenji-miyake@users.noreply.github.com> Co-authored-by: taikitanaka3 <65527974+taikitanaka3@users.noreply.github.com> --- tmp/lanelet2_extension/lib/query.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tmp/lanelet2_extension/lib/query.cpp b/tmp/lanelet2_extension/lib/query.cpp index 9cca2eb7..1e9e9ae7 100644 --- a/tmp/lanelet2_extension/lib/query.cpp +++ b/tmp/lanelet2_extension/lib/query.cpp @@ -731,10 +731,11 @@ bool query::getClosestLanelet( if (angle_diff < min_angle) { min_angle = angle_diff; *closest_lanelet_ptr = llt; - } else if ((segment_angle - pose_yaw) < 1e-04) { - min_angle = std::abs(segment_angle - pose_yaw); - *closest_lanelet_ptr = llt; } + /* else if ((segment_angle - pose_yaw) < 1e-04) { + min_angle = std::abs(segment_angle - pose_yaw); + *closest_lanelet_ptr = llt; + }*/ } } From 16cb23f809ab85b2b9c73cc4331f9f8711d138dc Mon Sep 17 00:00:00 2001 From: kyoichi-sugahara <81.s.kyo.19@gmail.com> Date: Tue, 18 Jan 2022 09:44:36 +0900 Subject: [PATCH 04/36] fix(lanelet2_extension): add guard for inner product (#256) * fix: add guard for inner product Signed-off-by: kyoichi sugahara <81.s.kyo.19@gmail.com> * ci(pre-commit): autofix Signed-off-by: kyoichi sugahara <81.s.kyo.19@gmail.com> * fix: delete unnecessary comments Signed-off-by: kyoichi sugahara <81.s.kyo.19@gmail.com> * Update map/lanelet2_extension/lib/query.cpp Co-authored-by: Hiroki OTA Signed-off-by: kyoichi sugahara <81.s.kyo.19@gmail.com> * delete equal in comparison operator Signed-off-by: kyoichi sugahara <81.s.kyo.19@gmail.com> * fix: use normilized radian for calculation of angle difference Signed-off-by: kyoichi sugahara <81.s.kyo.19@gmail.com> * ci(pre-commit): autofix Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Hiroki OTA --- tmp/lanelet2_extension/lib/query.cpp | 14 +++++--------- tmp/lanelet2_extension/package.xml | 1 + 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/tmp/lanelet2_extension/lib/query.cpp b/tmp/lanelet2_extension/lib/query.cpp index 1e9e9ae7..95ad8d5b 100644 --- a/tmp/lanelet2_extension/lib/query.cpp +++ b/tmp/lanelet2_extension/lib/query.cpp @@ -20,6 +20,7 @@ #include "lanelet2_extension/utility/utilities.hpp" #include +#include #include #include @@ -37,11 +38,10 @@ namespace { double getAngleDifference(const double angle1, const double angle2) { - Eigen::Vector2d vec1, vec2; - vec1 << std::cos(angle1), std::sin(angle1); - vec2 << std::cos(angle2), std::sin(angle2); - const double diff_angle = std::acos(vec1.dot(vec2)); - return std::fabs(diff_angle); + const double normalized_angle1 = tier4_autoware_utils::normalizeRadian(angle1); + const double normalized_angle2 = tier4_autoware_utils::normalizeRadian(angle2); + const double diff_angle = std::fabs(normalized_angle1 - normalized_angle2); + return diff_angle; } } // namespace @@ -732,10 +732,6 @@ bool query::getClosestLanelet( min_angle = angle_diff; *closest_lanelet_ptr = llt; } - /* else if ((segment_angle - pose_yaw) < 1e-04) { - min_angle = std::abs(segment_angle - pose_yaw); - *closest_lanelet_ptr = llt; - }*/ } } diff --git a/tmp/lanelet2_extension/package.xml b/tmp/lanelet2_extension/package.xml index 06867a3c..3f1af3a2 100644 --- a/tmp/lanelet2_extension/package.xml +++ b/tmp/lanelet2_extension/package.xml @@ -24,6 +24,7 @@ pugixml-dev rclcpp tf2 + tier4_autoware_utils visualization_msgs ament_cmake_gtest From 69125bec6988a55a484bd2e492c1016640d142ca Mon Sep 17 00:00:00 2001 From: taikitanaka3 <65527974+taikitanaka3@users.noreply.github.com> Date: Thu, 20 Jan 2022 09:48:36 +0900 Subject: [PATCH 05/36] fix(lanelet2_extension): fix getAngleDifference (#264) * chore(query): update and add gtest Signed-off-by: tanaka3 * ci(pre-commit): autofix * chore(query): add deprecated attribute to getAngleDifference Signed-off-by: tanaka3 * chore(query): deprecated as comment to avoid clang errror Signed-off-by: tanaka3 * Revert "chore(query): deprecated as comment to avoid clang errror" This reverts commit 28cc95d75ae55ddeef3813c91c18f0a02b95a41d. Signed-off-by: tanaka3 * Revert "chore(query): add deprecated attribute to getAngleDifference" This reverts commit 3b56ce7a91d76a029eadc190b224a295c01eb02f. Signed-off-by: tanaka3 * Revert "ci(pre-commit): autofix" This reverts commit 61e05c4718eef343f8ba198fe46e501048e0b784. Signed-off-by: tanaka3 * Revert "chore(query): update and add gtest" This reverts commit 027c4cf53883c233f28da75c633e53278ce2a1ca. Signed-off-by: tanaka3 * fix(lanelet2_extension): fix getAngleDifference Signed-off-by: tanaka3 * fix(lanelet2_extension): ref value Co-authored-by: Kenji Miyake <31987104+kenji-miyake@users.noreply.github.com> Signed-off-by: tanaka3 Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Kenji Miyake <31987104+kenji-miyake@users.noreply.github.com> --- tmp/lanelet2_extension/lib/query.cpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/tmp/lanelet2_extension/lib/query.cpp b/tmp/lanelet2_extension/lib/query.cpp index 95ad8d5b..d24ebb3a 100644 --- a/tmp/lanelet2_extension/lib/query.cpp +++ b/tmp/lanelet2_extension/lib/query.cpp @@ -34,17 +34,6 @@ #include using lanelet::utils::to2D; -namespace -{ -double getAngleDifference(const double angle1, const double angle2) -{ - const double normalized_angle1 = tier4_autoware_utils::normalizeRadian(angle1); - const double normalized_angle2 = tier4_autoware_utils::normalizeRadian(angle2); - const double diff_angle = std::fabs(normalized_angle1 - normalized_angle2); - return diff_angle; -} - -} // namespace namespace lanelet { @@ -727,7 +716,7 @@ bool query::getClosestLanelet( lanelet::ConstLineString3d segment = getClosestSegment(search_point, llt.centerline()); double segment_angle = std::atan2( segment.back().y() - segment.front().y(), segment.back().x() - segment.front().x()); - double angle_diff = getAngleDifference(segment_angle, pose_yaw); + double angle_diff = std::abs(tier4_autoware_utils::normalizeRadian(segment_angle - pose_yaw)); if (angle_diff < min_angle) { min_angle = angle_diff; *closest_lanelet_ptr = llt; From 4dafc05fdd1db7a06e6b3c4a1d6fcf05a82dfb6e Mon Sep 17 00:00:00 2001 From: taikitanaka3 <65527974+taikitanaka3@users.noreply.github.com> Date: Sat, 5 Mar 2022 16:32:58 +0900 Subject: [PATCH 06/36] feat(lanelet2_extension,map_loader): add guard_rail wall fence as lanelet tag (#478) * feat(lanelet2_extension): add guard_rails fence wall as lanelet tag Signed-off-by: tanaka3 * feat(map_loader): add visualization for partion lanelet Signed-off-by: tanaka3 --- .../include/lanelet2_extension/utility/query.hpp | 3 +++ tmp/lanelet2_extension/lib/query.cpp | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp index 2984525c..0e0b3c74 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp @@ -123,6 +123,9 @@ lanelet::ConstPolygons3d getAllObstaclePolygons( // query all parking lots in lanelet2 map lanelet::ConstPolygons3d getAllParkingLots(const lanelet::LaneletMapConstPtr & lanelet_map_ptr); +// query all partitions in lanelet2 map +lanelet::ConstLineStrings3d getAllPartitions(const lanelet::LaneletMapConstPtr & lanelet_map_ptr); + // query all pedestrian markings in lanelet2 map lanelet::ConstLineStrings3d getAllPedestrianMarkings( const lanelet::LaneletMapConstPtr & lanelet_map_ptr); diff --git a/tmp/lanelet2_extension/lib/query.cpp b/tmp/lanelet2_extension/lib/query.cpp index d24ebb3a..4eaf6f5c 100644 --- a/tmp/lanelet2_extension/lib/query.cpp +++ b/tmp/lanelet2_extension/lib/query.cpp @@ -241,6 +241,20 @@ lanelet::ConstPolygons3d query::getAllParkingLots( return parking_lots; } +lanelet::ConstLineStrings3d query::getAllPartitions( + const lanelet::LaneletMapConstPtr & lanelet_map_ptr) +{ + lanelet::ConstLineStrings3d partitions; + for (const auto & ls : lanelet_map_ptr->lineStringLayer) { + const std::string type = ls.attributeOr(lanelet::AttributeName::Type, "none"); + if ( + type.compare("guard_rail") == 0 || type.compare("fence") == 0 || type.compare("wall") == 0) { + partitions.push_back(ls); + } + } + return partitions; +} + lanelet::ConstLineStrings3d query::getAllPedestrianMarkings( const lanelet::LaneletMapConstPtr & lanelet_map_ptr) { From 084b3cb7ded1edcba5dd347bf2e8f99d89f40efb Mon Sep 17 00:00:00 2001 From: Daisuke Nishimatsu <42202095+wep21@users.noreply.github.com> Date: Thu, 21 Apr 2022 21:02:41 +0900 Subject: [PATCH 07/36] fix(tier4_autoware_utils): modify build error in rolling (#720) * fix(tier4_autoware_utils): modify build error in rolling Signed-off-by: wep21 * fix(lanelet2_extension): add target compile definition for geometry2 Signed-off-by: wep21 * fix(ekf_localizer): add target compile definition for geometry2 Signed-off-by: wep21 * fix(freespace_planning_algorithms): add target compile definition for geometry2 Signed-off-by: wep21 * fix(interpolation): add target compile definition for geometry2 Signed-off-by: wep21 * fix(freespace_planner): add target compile definition for geometry2 Signed-off-by: wep21 * fix(lane_departure_checker): add target compile definition for geometry2 Signed-off-by: wep21 * fix(map_based_prediction): add target compile definition for geometry2 Signed-off-by: wep21 * fix(ground_segmentation): add target compile definition for geometry2 Signed-off-by: wep21 * fix(motion_velocity_smoother): add target compile definition for geometry2 Signed-off-by: wep21 * fix(multi_object_tracker): add target compile definition for geometry2 Signed-off-by: wep21 * fix(trajectory_follower): add target compile definition for geometry2 Signed-off-by: wep21 * fix(control_performance_analysis): add target compile definition for geometry2 Signed-off-by: wep21 * fix(detected_object_validation): add target compile definition for geometry2 Signed-off-by: wep21 * fix(goal_distance_calculator): add target compile definition for geometry2 Signed-off-by: wep21 * fix(ndt_scan_matcher): add target compile definition for geometry2 Signed-off-by: wep21 * fix(route_handler): add target compile definition for geometry2 Signed-off-by: wep21 * fix(behavior_path_planner): add target compile definition for geometry2 Signed-off-by: wep21 * fix(mission_planner): add target compile definition for geometry2 Signed-off-by: wep21 * fix(obstacle_avoidance_planner): add target compile definition for geometry2 Signed-off-by: wep21 * fix(obstacle_stop_planner): add target compile definition for geometry2 Signed-off-by: wep21 * fix(obstacle_collision_checker): add target compile definition for geometry2 Signed-off-by: wep21 * fix(shape_estimation): add target compile definition for geometry2 Signed-off-by: wep21 * fix(behavior_velocity_planner): add target compile definition for geometry2 Signed-off-by: wep21 * fix(path_distance_calculator): add target compile definition for geometry2 Signed-off-by: wep21 * fix(detection_by_tracker): add target compile definition for geometry2 Signed-off-by: wep21 * fix(surround_obstacle_checker): add target compile definition for geometry2 Signed-off-by: wep21 * fix(probabilistic_occupancy_grid_map): add target compile definition for geometry2 Signed-off-by: wep21 * fix(tier4_debug_tools): add target compile definition for geometry2 Signed-off-by: wep21 * fix(tier4_vehicle_rviz_plugin): add target compile definition for geometry2 Signed-off-by: wep21 * fix(pure_pursuit): add target compile definition for geometry2 Signed-off-by: wep21 * fix(trajectory_follower_nodes): add target compile definition for geometry2 Signed-off-by: wep21 * fix(occupancy_grid_map_outlier_filter): add target compile definition for geometry2 Signed-off-by: wep21 * fix(traffic_light_map_based_detector): add target compile definition for geometry2 Signed-off-by: wep21 * fix(planning_error_monitor): add target compile definition for geometry2 Signed-off-by: wep21 * fix(planning_evaluator): add target compile definition for geometry2 Signed-off-by: wep21 * fix(lidar_centerpoint): add target compile definition for geometry2 Signed-off-by: wep21 --- tmp/lanelet2_extension/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tmp/lanelet2_extension/CMakeLists.txt b/tmp/lanelet2_extension/CMakeLists.txt index c755d45e..a59b95ba 100644 --- a/tmp/lanelet2_extension/CMakeLists.txt +++ b/tmp/lanelet2_extension/CMakeLists.txt @@ -57,6 +57,13 @@ target_link_libraries(lanelet2_extension_lib ${GeographicLib_LIBRARIES} ) +# workaround to allow deprecated header to build on both galactic and rolling +if(${tf2_geometry_msgs_VERSION} VERSION_LESS 0.18.0) + target_compile_definitions(lanelet2_extension_lib PUBLIC + USE_TF2_GEOMETRY_MSGS_DEPRECATED_HEADER + ) +endif() + ament_auto_add_executable(lanelet2_extension_sample src/sample_code.cpp) add_dependencies(lanelet2_extension_sample lanelet2_extension_lib) target_link_libraries(lanelet2_extension_sample From 50e7c07e046b56984c050d16f4578326dc20e5e0 Mon Sep 17 00:00:00 2001 From: Kenji Miyake <31987104+kenji-miyake@users.noreply.github.com> Date: Tue, 3 May 2022 19:31:19 +0900 Subject: [PATCH 08/36] style: fix format of package.xml (#844) Signed-off-by: Kenji Miyake --- tmp/lanelet2_extension/package.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tmp/lanelet2_extension/package.xml b/tmp/lanelet2_extension/package.xml index 3f1af3a2..bd5ae7fa 100644 --- a/tmp/lanelet2_extension/package.xml +++ b/tmp/lanelet2_extension/package.xml @@ -1,11 +1,10 @@ + lanelet2_extension 0.1.0 The lanelet2_extension package contains libraries to handle Lanelet2 format data. - mitsudome-r - Apache License 2.0 ament_cmake From f45520e4782cf18de7df5ea0ab20a02320d43ba4 Mon Sep 17 00:00:00 2001 From: Kenji Miyake <31987104+kenji-miyake@users.noreply.github.com> Date: Wed, 4 May 2022 16:30:26 +0900 Subject: [PATCH 09/36] chore: remove bad chars (#845) Signed-off-by: Kenji Miyake --- tmp/lanelet2_extension/docs/extra_lanelet_subtypes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tmp/lanelet2_extension/docs/extra_lanelet_subtypes.md b/tmp/lanelet2_extension/docs/extra_lanelet_subtypes.md index 3632b07f..b7b84eb9 100644 --- a/tmp/lanelet2_extension/docs/extra_lanelet_subtypes.md +++ b/tmp/lanelet2_extension/docs/extra_lanelet_subtypes.md @@ -6,7 +6,7 @@ The subtypes for this lanelet classify the outer lanes adjacent to the driving l ### Road shoulder subtype -- refers: lanelet with subtype attribute. Subtype explains what the type of roadside it represents. If there is an area outside of this roadside lane that is open to traffic, such as a sidewalk or bike lane, select the road_shoulder subtype. +- refers: lanelet with subtype attribute. Subtype explains what the type of roadside it represents. If there is an area outside of this roadside lane that is open to traffic, such as a sidewalk or bike lane, select the road_shoulder subtype. ![Road shoulder](road_shoulder.svg) @@ -26,7 +26,7 @@ Sample road shoulder in .osm format is shown below: ### Pedestrian lane subtype -- refers: lanelet with subtype attribute. Subtype explains what the type of roadside it represents. If there are no passable areas outside of this roadside lane, select the pedestrian_lane subtype. +- refers: lanelet with subtype attribute. Subtype explains what the type of roadside it represents. If there are no passable areas outside of this roadside lane, select the pedestrian_lane subtype. ![Pedestrian lane](pedestrian_lane.svg) From 80389fcc858adc845e8cced67665bf0c6a894ffd Mon Sep 17 00:00:00 2001 From: Kenji Miyake <31987104+kenji-miyake@users.noreply.github.com> Date: Thu, 5 May 2022 19:00:51 +0900 Subject: [PATCH 10/36] refactor: use autoware cmake (#849) * remove autoware_auto_cmake Signed-off-by: Kenji Miyake * add build_depend of autoware_cmake Signed-off-by: Kenji Miyake * use autoware_cmake in CMakeLists.txt Signed-off-by: Kenji Miyake * fix bugs Signed-off-by: Kenji Miyake * fix cmake lint errors Signed-off-by: Kenji Miyake --- tmp/lanelet2_extension/CMakeLists.txt | 17 ++--------------- tmp/lanelet2_extension/package.xml | 3 ++- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/tmp/lanelet2_extension/CMakeLists.txt b/tmp/lanelet2_extension/CMakeLists.txt index a59b95ba..999f181a 100644 --- a/tmp/lanelet2_extension/CMakeLists.txt +++ b/tmp/lanelet2_extension/CMakeLists.txt @@ -1,18 +1,8 @@ cmake_minimum_required(VERSION 3.5) project(lanelet2_extension) -if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 14) - set(CMAKE_CXX_STANDARD_REQUIRED ON) - set(CMAKE_CXX_EXTENSIONS OFF) -endif() - -if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - add_compile_options(-Wall -Wextra -Wpedantic -Werror) -endif() - -find_package(ament_cmake_auto REQUIRED) -ament_auto_find_build_dependencies() +find_package(autoware_cmake REQUIRED) +autoware_package() find_package(PkgConfig) find_path(GeographicLib_INCLUDE_DIR GeographicLib/Config.h @@ -79,7 +69,6 @@ target_link_libraries(autoware_lanelet2_validation ) if(BUILD_TESTING) - find_package(ament_cmake_gtest REQUIRED) ament_add_gtest(message_conversion-test test/src/test_message_conversion.cpp) target_link_libraries(message_conversion-test lanelet2_extension_lib) ament_add_gtest(projector-test test/src/test_projector.cpp) @@ -90,8 +79,6 @@ if(BUILD_TESTING) target_link_libraries(regulatory_elements-test lanelet2_extension_lib) ament_add_gtest(utilities-test test/src/test_utilities.cpp) target_link_libraries(utilities-test lanelet2_extension_lib) - find_package(ament_lint_auto REQUIRED) - ament_lint_auto_find_test_dependencies() endif() ament_auto_package() diff --git a/tmp/lanelet2_extension/package.xml b/tmp/lanelet2_extension/package.xml index bd5ae7fa..e817b06f 100644 --- a/tmp/lanelet2_extension/package.xml +++ b/tmp/lanelet2_extension/package.xml @@ -7,9 +7,10 @@ mitsudome-r Apache License 2.0 - ament_cmake ament_cmake_auto + autoware_cmake + autoware_auto_mapping_msgs geographiclib geometry_msgs From a12c599290a8e5f5cdedec8a3c4cb504c0ae7f26 Mon Sep 17 00:00:00 2001 From: Kenji Miyake <31987104+kenji-miyake@users.noreply.github.com> Date: Fri, 6 May 2022 00:53:41 +0900 Subject: [PATCH 11/36] refactor: simplify Rolling support (#854) Signed-off-by: Kenji Miyake --- tmp/lanelet2_extension/CMakeLists.txt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tmp/lanelet2_extension/CMakeLists.txt b/tmp/lanelet2_extension/CMakeLists.txt index 999f181a..9ec02cf6 100644 --- a/tmp/lanelet2_extension/CMakeLists.txt +++ b/tmp/lanelet2_extension/CMakeLists.txt @@ -47,13 +47,6 @@ target_link_libraries(lanelet2_extension_lib ${GeographicLib_LIBRARIES} ) -# workaround to allow deprecated header to build on both galactic and rolling -if(${tf2_geometry_msgs_VERSION} VERSION_LESS 0.18.0) - target_compile_definitions(lanelet2_extension_lib PUBLIC - USE_TF2_GEOMETRY_MSGS_DEPRECATED_HEADER - ) -endif() - ament_auto_add_executable(lanelet2_extension_sample src/sample_code.cpp) add_dependencies(lanelet2_extension_sample lanelet2_extension_lib) target_link_libraries(lanelet2_extension_sample From a1ad65df8948a85a7b4144dcfb64e2757215a353 Mon Sep 17 00:00:00 2001 From: Kenji Miyake <31987104+kenji-miyake@users.noreply.github.com> Date: Fri, 6 May 2022 02:49:27 +0900 Subject: [PATCH 12/36] chore: upgrade cmake_minimum_required to 3.14 (#856) Signed-off-by: Kenji Miyake --- tmp/lanelet2_extension/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmp/lanelet2_extension/CMakeLists.txt b/tmp/lanelet2_extension/CMakeLists.txt index 9ec02cf6..d256029e 100644 --- a/tmp/lanelet2_extension/CMakeLists.txt +++ b/tmp/lanelet2_extension/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.14) project(lanelet2_extension) find_package(autoware_cmake REQUIRED) From ebce3b1d91f731364ae88ff5a9e4d184b0410fae Mon Sep 17 00:00:00 2001 From: Maxime CLEMENT <78338830+maxime-clem@users.noreply.github.com> Date: Mon, 9 May 2022 21:08:53 +0900 Subject: [PATCH 13/36] feat: isolate gtests in all packages (#693) Signed-off-by: Maxime CLEMENT --- tmp/lanelet2_extension/CMakeLists.txt | 10 +++++----- tmp/lanelet2_extension/package.xml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tmp/lanelet2_extension/CMakeLists.txt b/tmp/lanelet2_extension/CMakeLists.txt index d256029e..709c7bda 100644 --- a/tmp/lanelet2_extension/CMakeLists.txt +++ b/tmp/lanelet2_extension/CMakeLists.txt @@ -62,15 +62,15 @@ target_link_libraries(autoware_lanelet2_validation ) if(BUILD_TESTING) - ament_add_gtest(message_conversion-test test/src/test_message_conversion.cpp) + ament_add_ros_isolated_gtest(message_conversion-test test/src/test_message_conversion.cpp) target_link_libraries(message_conversion-test lanelet2_extension_lib) - ament_add_gtest(projector-test test/src/test_projector.cpp) + ament_add_ros_isolated_gtest(projector-test test/src/test_projector.cpp) target_link_libraries(projector-test lanelet2_extension_lib) - ament_add_gtest(query-test test/src/test_query.cpp) + ament_add_ros_isolated_gtest(query-test test/src/test_query.cpp) target_link_libraries(query-test lanelet2_extension_lib) - ament_add_gtest(regulatory_elements-test test/src/test_regulatory_elements.cpp) + ament_add_ros_isolated_gtest(regulatory_elements-test test/src/test_regulatory_elements.cpp) target_link_libraries(regulatory_elements-test lanelet2_extension_lib) - ament_add_gtest(utilities-test test/src/test_utilities.cpp) + ament_add_ros_isolated_gtest(utilities-test test/src/test_utilities.cpp) target_link_libraries(utilities-test lanelet2_extension_lib) endif() diff --git a/tmp/lanelet2_extension/package.xml b/tmp/lanelet2_extension/package.xml index e817b06f..d46d05a7 100644 --- a/tmp/lanelet2_extension/package.xml +++ b/tmp/lanelet2_extension/package.xml @@ -27,7 +27,7 @@ tier4_autoware_utils visualization_msgs - ament_cmake_gtest + ament_cmake_ros ament_lint_auto autoware_lint_common From 553483b94d5684ce3bb99aeecb67715e8c927fdb Mon Sep 17 00:00:00 2001 From: Takayuki Murooka Date: Tue, 17 May 2022 22:54:46 +0900 Subject: [PATCH 14/36] feat(behavior_path_planner): weakened noise filtering of drivable area (#838) * feat(behavior_path_planner): Weakened noise filtering of drivable area Signed-off-by: Takayuki Murooka * fix lanelet's longitudinal disconnection Signed-off-by: Takayuki Murooka * add comments of erode/dilate process Signed-off-by: Takayuki Murooka --- tmp/lanelet2_extension/lib/utilities.cpp | 34 ++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/tmp/lanelet2_extension/lib/utilities.cpp b/tmp/lanelet2_extension/lib/utilities.cpp index 072f0790..8983d9d4 100644 --- a/tmp/lanelet2_extension/lib/utilities.cpp +++ b/tmp/lanelet2_extension/lib/utilities.cpp @@ -315,8 +315,38 @@ lanelet::ConstLanelet getExpandedLanelet( // Note: The lanelet::geometry::offset throws exception when the undesired inversion is found. // Use offsetNoThrow until the logic is updated to handle the inversion. // TODO(Horibe) update - const auto & expanded_left_bound_2d = offsetNoThrow(orig_left_bound_2d, left_offset); - const auto & expanded_right_bound_2d = offsetNoThrow(orig_right_bound_2d, right_offset); + auto expanded_left_bound_2d = offsetNoThrow(orig_left_bound_2d, left_offset); + auto expanded_right_bound_2d = offsetNoThrow(orig_right_bound_2d, right_offset); + + // Note: modify front and back points so that the successive lanelets will not have any + // longitudinal space between them. + { // front + const double diff_x = orig_right_bound_2d.front().x() - orig_left_bound_2d.front().x(); + const double diff_y = orig_right_bound_2d.front().y() - orig_left_bound_2d.front().y(); + const double theta = std::atan2(diff_y, diff_x); + expanded_right_bound_2d.front().x() = + orig_right_bound_2d.front().x() - right_offset * std::cos(theta); + expanded_right_bound_2d.front().y() = + orig_right_bound_2d.front().y() - right_offset * std::sin(theta); + expanded_left_bound_2d.front().x() = + orig_left_bound_2d.front().x() - left_offset * std::cos(theta); + expanded_left_bound_2d.front().y() = + orig_left_bound_2d.front().y() - left_offset * std::sin(theta); + } + { // back + const double diff_x = orig_right_bound_2d.back().x() - orig_left_bound_2d.back().x(); + const double diff_y = orig_right_bound_2d.back().y() - orig_left_bound_2d.back().y(); + const double theta = std::atan2(diff_y, diff_x); + expanded_right_bound_2d.back().x() = + orig_right_bound_2d.back().x() - right_offset * std::cos(theta); + expanded_right_bound_2d.back().y() = + orig_right_bound_2d.back().y() - right_offset * std::sin(theta); + expanded_left_bound_2d.back().x() = + orig_left_bound_2d.back().x() - left_offset * std::cos(theta); + expanded_left_bound_2d.back().y() = + orig_left_bound_2d.back().y() - left_offset * std::sin(theta); + } + rclcpp::Clock clock{RCL_ROS_TIME}; try { checkForInversion(orig_left_bound_2d, expanded_left_bound_2d, left_offset); From 1d11eaadaba77703b8a86e5a73c445f747219460 Mon Sep 17 00:00:00 2001 From: Hiroki OTA Date: Thu, 19 May 2022 10:17:06 +0900 Subject: [PATCH 15/36] feat: visualize lane boundaries (#923) * feat: visualize lane boundaries * fix: start_bound * ci(pre-commit): autofix Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- tmp/lanelet2_extension/lib/visualization.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tmp/lanelet2_extension/lib/visualization.cpp b/tmp/lanelet2_extension/lib/visualization.cpp index fc9f0686..a95a9aa1 100644 --- a/tmp/lanelet2_extension/lib/visualization.cpp +++ b/tmp/lanelet2_extension/lib/visualization.cpp @@ -878,11 +878,14 @@ visualization_msgs::msg::MarkerArray visualization::laneletsBoundaryAsMarkerArra double lss_center = std::max(lss * 0.1, 0.02); std::unordered_set added; - visualization_msgs::msg::Marker left_line_strip, right_line_strip, center_line_strip; + visualization_msgs::msg::Marker left_line_strip, right_line_strip, start_bound_line_strip, + center_line_strip; visualization::initLineStringMarker( &left_line_strip, "map", additional_namespace + "left_lane_bound", c); visualization::initLineStringMarker( &right_line_strip, "map", additional_namespace + "right_lane_bound", c); + visualization::initLineStringMarker( + &start_bound_line_strip, "map", additional_namespace + "lane_start_bound", c); visualization::initLineStringMarker( ¢er_line_strip, "map", additional_namespace + "center_lane_line", c); @@ -892,6 +895,11 @@ visualization_msgs::msg::MarkerArray visualization::laneletsBoundaryAsMarkerArra lanelet::ConstLineString3d left_ls = lll.leftBound(); lanelet::ConstLineString3d right_ls = lll.rightBound(); lanelet::ConstLineString3d center_ls = lll.centerline(); + lanelet::LineString3d start_bound_ls(lanelet::utils::getId()); + start_bound_ls.push_back(lanelet::Point3d( + lanelet::utils::getId(), left_ls.front().x(), left_ls.front().y(), left_ls.front().z())); + start_bound_ls.push_back(lanelet::Point3d( + lanelet::utils::getId(), right_ls.front().x(), right_ls.front().y(), right_ls.front().z())); if (!exists(added, left_ls.id())) { visualization::pushLineStringMarker(&left_line_strip, left_ls, c, lss); @@ -901,6 +909,10 @@ visualization_msgs::msg::MarkerArray visualization::laneletsBoundaryAsMarkerArra visualization::pushLineStringMarker(&right_line_strip, right_ls, c, lss); added.insert(right_ls.id()); } + if (!exists(added, start_bound_ls.id())) { + visualization::pushLineStringMarker(&start_bound_line_strip, start_bound_ls, c, lss); + added.insert(start_bound_ls.id()); + } if (viz_centerline && !exists(added, center_ls.id())) { visualization::pushLineStringMarker(¢er_line_strip, center_ls, c, lss_center); added.insert(center_ls.id()); @@ -917,6 +929,9 @@ visualization_msgs::msg::MarkerArray visualization::laneletsBoundaryAsMarkerArra if (!center_line_strip.points.empty()) { marker_array.markers.push_back(center_line_strip); } + if (!start_bound_line_strip.points.empty()) { + marker_array.markers.push_back(start_bound_line_strip); + } return marker_array; } From 02829bf980e9cd931f3e7a6c7d7145898b0f9667 Mon Sep 17 00:00:00 2001 From: Hiroki OTA Date: Fri, 20 May 2022 15:36:48 +0900 Subject: [PATCH 16/36] feat(map_loader): visualize center line by points (#931) * feat: visualize center line points * fix: delete space * feat: visualize center line by arrow * revert insertMarkerArray * fix: delete space --- .../visualization/visualization.hpp | 10 ++++ tmp/lanelet2_extension/lib/visualization.cpp | 56 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp index 9d21ac69..026a78ea 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp @@ -83,6 +83,16 @@ void pushLineStringMarker( visualization_msgs::msg::Marker * marker, const lanelet::ConstLineString3d & ls, const std_msgs::msg::ColorRGBA c, const float lss = 0.1); +/** + * [pushArrowMarkerArray pushes marker to visualize arrows] + * @param marker_array [output marker array message] + * @param ls [input linestring] + * @param c [color of the marker] + */ +void pushArrowMarkerArray( + visualization_msgs::msg::MarkerArray * marker_array, const lanelet::ConstLineString3d & ls, + const std::string frame_id, const std::string ns, const std_msgs::msg::ColorRGBA c); + /** * [initTrafficLightTriangleMarker initializes marker to visualize shape of traffic * lights] diff --git a/tmp/lanelet2_extension/lib/visualization.cpp b/tmp/lanelet2_extension/lib/visualization.cpp index a95a9aa1..b846382c 100644 --- a/tmp/lanelet2_extension/lib/visualization.cpp +++ b/tmp/lanelet2_extension/lib/visualization.cpp @@ -21,6 +21,8 @@ #include "lanelet2_extension/utility/utilities.hpp" #include +#include +#include #include #include @@ -880,6 +882,7 @@ visualization_msgs::msg::MarkerArray visualization::laneletsBoundaryAsMarkerArra std::unordered_set added; visualization_msgs::msg::Marker left_line_strip, right_line_strip, start_bound_line_strip, center_line_strip; + visualization_msgs::msg::MarkerArray center_line_arrows; visualization::initLineStringMarker( &left_line_strip, "map", additional_namespace + "left_lane_bound", c); visualization::initLineStringMarker( @@ -915,6 +918,8 @@ visualization_msgs::msg::MarkerArray visualization::laneletsBoundaryAsMarkerArra } if (viz_centerline && !exists(added, center_ls.id())) { visualization::pushLineStringMarker(¢er_line_strip, center_ls, c, lss_center); + visualization::pushArrowMarkerArray( + ¢er_line_arrows, center_ls, "map", additional_namespace + "center_line_arrows", c); added.insert(center_ls.id()); } } @@ -932,6 +937,9 @@ visualization_msgs::msg::MarkerArray visualization::laneletsBoundaryAsMarkerArra if (!start_bound_line_strip.points.empty()) { marker_array.markers.push_back(start_bound_line_strip); } + marker_array.markers.insert( + marker_array.markers.end(), center_line_arrows.markers.begin(), + center_line_arrows.markers.end()); return marker_array; } @@ -1183,4 +1191,52 @@ void visualization::pushLineStringMarker( } } +void visualization::pushArrowMarkerArray( + visualization_msgs::msg::MarkerArray * marker_array, const lanelet::ConstLineString3d & ls, + const std::string frame_id, const std::string ns, const std_msgs::msg::ColorRGBA c) +{ + if (marker_array == nullptr) { + RCLCPP_ERROR_STREAM( + rclcpp::get_logger("lanelet2_extension.visualization"), + __FUNCTION__ << ": marker_array is null pointer!"); + return; + } + + // fill out lane line + if (ls.size() < 2) { + RCLCPP_ERROR_STREAM( + rclcpp::get_logger("lanelet2_extension.visualization"), + __FUNCTION__ << ": marker line size is 1 or 0!"); + return; + } + + visualization_msgs::msg::Marker marker; + marker.header.frame_id = frame_id; + marker.header.stamp = rclcpp::Time(); + marker.frame_locked = true; + marker.ns = ns; + marker.action = visualization_msgs::msg::Marker::ADD; + marker.type = visualization_msgs::msg::Marker::ARROW; + + int32_t start_index = !marker_array->markers.empty() ? marker_array->markers.back().id : 0; + for (auto i = ls.begin(); i + 1 != ls.end(); i++) { + const auto index = i - ls.begin(); + marker.id = start_index + index + 1; + const auto curr_point = + geometry_msgs::build().x((*i).x()).y((*i).y()).z((*i).z()); + const auto next_point = geometry_msgs::build() + .x((*(i + 1)).x()) + .y((*(i + 1)).y()) + .z((*(i + 1)).z()); + marker.pose.position = curr_point; + + const double yaw = tier4_autoware_utils::calcAzimuthAngle(curr_point, next_point); + marker.pose.orientation = tier4_autoware_utils::createQuaternionFromYaw(yaw); + marker.scale = tier4_autoware_utils::createMarkerScale(0.3, 0.1, 0.1); + marker.color = c; + + marker_array->markers.push_back(marker); + } +} + } // namespace lanelet From 726294183e24c84c97695f0b6237398e1c2dc844 Mon Sep 17 00:00:00 2001 From: Hiroki OTA Date: Sun, 5 Jun 2022 22:09:15 +0900 Subject: [PATCH 17/36] fix(lanelet2_extension): fix low FPS when visualizing center_line_arrows at a large map (#1032) * fix(lanelet2_extension): fix low FPS when visualizing center_line_arrows at a large map Signed-off-by: h-ohta * fix: fix size * feat: change arrow size --- .../visualization/visualization.hpp | 21 +++- tmp/lanelet2_extension/lib/visualization.cpp | 98 ++++++++++++------- 2 files changed, 77 insertions(+), 42 deletions(-) diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp index 026a78ea..682f2c3d 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp @@ -84,14 +84,25 @@ void pushLineStringMarker( const std_msgs::msg::ColorRGBA c, const float lss = 0.1); /** - * [pushArrowMarkerArray pushes marker to visualize arrows] - * @param marker_array [output marker array message] + * [initArrowsMarker initializes marker to visualize arrows with TRIANGLE_LIST] + * @param marker [output marker message] + * @param frame_id [frame id of the marker] + * @param ns [namespace of the marker] + * @param c [color of the marker] + */ +void initArrowsMarker( + visualization_msgs::msg::Marker * marker, const std::string frame_id, const std::string ns, + const std_msgs::msg::ColorRGBA c); + +/** + * [pushArrowsMarker pushes marker to visualize arrows with TRIANGLE_LIST] + * @param marker [output marker message] * @param ls [input linestring] * @param c [color of the marker] */ -void pushArrowMarkerArray( - visualization_msgs::msg::MarkerArray * marker_array, const lanelet::ConstLineString3d & ls, - const std::string frame_id, const std::string ns, const std_msgs::msg::ColorRGBA c); +void pushArrowsMarker( + visualization_msgs::msg::Marker * marker, const lanelet::ConstLineString3d & ls, + const std_msgs::msg::ColorRGBA c); /** * [initTrafficLightTriangleMarker initializes marker to visualize shape of traffic diff --git a/tmp/lanelet2_extension/lib/visualization.cpp b/tmp/lanelet2_extension/lib/visualization.cpp index b846382c..da7fe490 100644 --- a/tmp/lanelet2_extension/lib/visualization.cpp +++ b/tmp/lanelet2_extension/lib/visualization.cpp @@ -881,8 +881,7 @@ visualization_msgs::msg::MarkerArray visualization::laneletsBoundaryAsMarkerArra std::unordered_set added; visualization_msgs::msg::Marker left_line_strip, right_line_strip, start_bound_line_strip, - center_line_strip; - visualization_msgs::msg::MarkerArray center_line_arrows; + center_line_strip, center_arrows; visualization::initLineStringMarker( &left_line_strip, "map", additional_namespace + "left_lane_bound", c); visualization::initLineStringMarker( @@ -891,6 +890,8 @@ visualization_msgs::msg::MarkerArray visualization::laneletsBoundaryAsMarkerArra &start_bound_line_strip, "map", additional_namespace + "lane_start_bound", c); visualization::initLineStringMarker( ¢er_line_strip, "map", additional_namespace + "center_lane_line", c); + visualization::initArrowsMarker( + ¢er_arrows, "map", additional_namespace + "center_line_arrows", c); for (auto li = lanelets.begin(); li != lanelets.end(); li++) { lanelet::ConstLanelet lll = *li; @@ -918,8 +919,7 @@ visualization_msgs::msg::MarkerArray visualization::laneletsBoundaryAsMarkerArra } if (viz_centerline && !exists(added, center_ls.id())) { visualization::pushLineStringMarker(¢er_line_strip, center_ls, c, lss_center); - visualization::pushArrowMarkerArray( - ¢er_line_arrows, center_ls, "map", additional_namespace + "center_line_arrows", c); + visualization::pushArrowsMarker(¢er_arrows, center_ls, c); added.insert(center_ls.id()); } } @@ -937,9 +937,9 @@ visualization_msgs::msg::MarkerArray visualization::laneletsBoundaryAsMarkerArra if (!start_bound_line_strip.points.empty()) { marker_array.markers.push_back(start_bound_line_strip); } - marker_array.markers.insert( - marker_array.markers.end(), center_line_arrows.markers.begin(), - center_line_arrows.markers.end()); + if (!center_arrows.points.empty()) { + marker_array.markers.push_back(center_arrows); + } return marker_array; } @@ -1191,14 +1191,43 @@ void visualization::pushLineStringMarker( } } -void visualization::pushArrowMarkerArray( - visualization_msgs::msg::MarkerArray * marker_array, const lanelet::ConstLineString3d & ls, - const std::string frame_id, const std::string ns, const std_msgs::msg::ColorRGBA c) +void visualization::initArrowsMarker( + visualization_msgs::msg::Marker * marker, const std::string frame_id, const std::string ns, + const std_msgs::msg::ColorRGBA c) +{ + if (marker == nullptr) { + RCLCPP_ERROR_STREAM( + rclcpp::get_logger("lanelet2_extension.visualization"), + __FUNCTION__ << ": marker is null pointer!"); + return; + } + + marker->header.frame_id = frame_id; + marker->header.stamp = rclcpp::Time(); + marker->frame_locked = true; + marker->ns = ns; + marker->action = visualization_msgs::msg::Marker::ADD; + marker->type = visualization_msgs::msg::Marker::TRIANGLE_LIST; + + marker->id = 0; + marker->pose.orientation.x = 0.0; + marker->pose.orientation.y = 0.0; + marker->pose.orientation.z = 0.0; + marker->pose.orientation.w = 1.0; + marker->scale.x = 1.0; + marker->scale.y = 1.0; + marker->scale.z = 1.0; + marker->color = c; +} + +void visualization::pushArrowsMarker( + visualization_msgs::msg::Marker * marker, const lanelet::ConstLineString3d & ls, + const std_msgs::msg::ColorRGBA c) { - if (marker_array == nullptr) { + if (marker == nullptr) { RCLCPP_ERROR_STREAM( rclcpp::get_logger("lanelet2_extension.visualization"), - __FUNCTION__ << ": marker_array is null pointer!"); + __FUNCTION__ << ": marker is null pointer!"); return; } @@ -1209,33 +1238,28 @@ void visualization::pushArrowMarkerArray( __FUNCTION__ << ": marker line size is 1 or 0!"); return; } - - visualization_msgs::msg::Marker marker; - marker.header.frame_id = frame_id; - marker.header.stamp = rclcpp::Time(); - marker.frame_locked = true; - marker.ns = ns; - marker.action = visualization_msgs::msg::Marker::ADD; - marker.type = visualization_msgs::msg::Marker::ARROW; - - int32_t start_index = !marker_array->markers.empty() ? marker_array->markers.back().id : 0; for (auto i = ls.begin(); i + 1 != ls.end(); i++) { - const auto index = i - ls.begin(); - marker.id = start_index + index + 1; - const auto curr_point = - geometry_msgs::build().x((*i).x()).y((*i).y()).z((*i).z()); - const auto next_point = geometry_msgs::build() - .x((*(i + 1)).x()) - .y((*(i + 1)).y()) - .z((*(i + 1)).z()); - marker.pose.position = curr_point; - - const double yaw = tier4_autoware_utils::calcAzimuthAngle(curr_point, next_point); - marker.pose.orientation = tier4_autoware_utils::createQuaternionFromYaw(yaw); - marker.scale = tier4_autoware_utils::createMarkerScale(0.3, 0.1, 0.1); - marker.color = c; + const float heading = std::atan2((*(i + 1)).y() - (*i).y(), (*(i + 1)).x() - (*i).x()); - marker_array->markers.push_back(marker); + const float sin_offset = std::sin(heading); + const float cos_offset = std::cos(heading); + const double width = 0.3; + const double height = 1.0; + + geometry_msgs::msg::Point p; + p.x = (*i).x() + sin_offset * width; + p.y = (*i).y() - cos_offset * width; + p.z = (*i).z(); + marker->points.push_back(p); + p.x = (*i).x() - sin_offset * width; + p.y = (*i).y() + cos_offset * width; + p.z = (*i).z(); + marker->points.push_back(p); + p.x = (*i).x() + cos_offset * height; + p.y = (*i).y() + sin_offset * height; + p.z = (*i).z(); + marker->points.push_back(p); + marker->colors.push_back(c); } } From dd61e884095f37bc376e21e01832a70e5872d4c9 Mon Sep 17 00:00:00 2001 From: Takayuki Murooka Date: Tue, 7 Jun 2022 10:45:40 +0900 Subject: [PATCH 18/36] fix(lanelet2_extension): remove unnecessary error message (#1043) Signed-off-by: Takayuki Murooka --- tmp/lanelet2_extension/lib/utilities.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tmp/lanelet2_extension/lib/utilities.cpp b/tmp/lanelet2_extension/lib/utilities.cpp index 8983d9d4..450da120 100644 --- a/tmp/lanelet2_extension/lib/utilities.cpp +++ b/tmp/lanelet2_extension/lib/utilities.cpp @@ -318,6 +318,17 @@ lanelet::ConstLanelet getExpandedLanelet( auto expanded_left_bound_2d = offsetNoThrow(orig_left_bound_2d, left_offset); auto expanded_right_bound_2d = offsetNoThrow(orig_right_bound_2d, right_offset); + rclcpp::Clock clock{RCL_ROS_TIME}; + try { + checkForInversion(orig_left_bound_2d, expanded_left_bound_2d, left_offset); + checkForInversion(orig_right_bound_2d, expanded_right_bound_2d, right_offset); + } catch (const lanelet::GeometryError & e) { + RCLCPP_ERROR_THROTTLE( + rclcpp::get_logger("lanelet2_extension"), clock, 1000, + "Fail to expand lanelet. output may be undesired. Lanelet points interval in map data could " + "be too narrow."); + } + // Note: modify front and back points so that the successive lanelets will not have any // longitudinal space between them. { // front @@ -347,17 +358,6 @@ lanelet::ConstLanelet getExpandedLanelet( orig_left_bound_2d.back().y() - left_offset * std::sin(theta); } - rclcpp::Clock clock{RCL_ROS_TIME}; - try { - checkForInversion(orig_left_bound_2d, expanded_left_bound_2d, left_offset); - checkForInversion(orig_right_bound_2d, expanded_right_bound_2d, right_offset); - } catch (const lanelet::GeometryError & e) { - RCLCPP_ERROR_THROTTLE( - rclcpp::get_logger("lanelet2_extension"), clock, 1000, - "Fail to expand lanelet. output may be undesired. Lanelet points interval in map data could " - "be too narrow."); - } - const auto toPoints3d = [](const lanelet::BasicLineString2d & ls2d, const double z) { lanelet::Points3d output; for (const auto & pt : ls2d) { From 00385344562007cfc41eeeb5fcd63ba06f8a69cf Mon Sep 17 00:00:00 2001 From: Takeshi Miura <57553950+1222-takeshi@users.noreply.github.com> Date: Thu, 9 Jun 2022 13:11:38 +0900 Subject: [PATCH 19/36] feat(intersection): add conflicting area with margin debug (#1021) * add detection area margin debug Signed-off-by: 1222-takeshi * extention lanelet in intersection function * feat: add conflicting area with margin Signed-off-by: 1222-takeshi * fix(intersection_module): remove unnecessary comment Signed-off-by: 1222-takeshi * fix check collision Signed-off-by: 1222-takeshi * fix(intersection_module): remove unnecessary diff Signed-off-by: 1222-takeshi * ci(pre-commit): autofix * fix(intersection_module): fix expand lane only right bound Signed-off-by: 1222-takeshi * fix(intersection_module): remove calc of detection area to object distance Signed-off-by: 1222-takeshi * ci(pre-commit): autofix * fix(intersection_module): split lane extentions Signed-off-by: 1222-takeshi * ci(pre-commit): autofix * refactor: lanelet::utils::resamplePoints -> resamplePoints Signed-off-by: 1222-takeshi * feat: add right and left margin parameter Signed-off-by: 1222-takeshi Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../lanelet2_extension/utility/utilities.hpp | 4 ++ tmp/lanelet2_extension/lib/utilities.cpp | 61 ++++++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp index 2a5d48de..6737da3e 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp @@ -35,6 +35,10 @@ lanelet::LineString3d generateFineCenterline( const lanelet::ConstLanelet & lanelet_obj, const double resolution = 5.0); lanelet::ConstLineString3d getCenterlineWithOffset( const lanelet::ConstLanelet & lanelet_obj, const double offset, const double resolution = 5.0); +lanelet::ConstLineString3d getRightBoundWithOffset( + const lanelet::ConstLanelet & lanelet_obj, const double offset, const double resolution = 5.0); +lanelet::ConstLineString3d getLeftBoundWithOffset( + const lanelet::ConstLanelet & lanelet_obj, const double offset, const double resolution = 5.0); lanelet::ConstLanelet getExpandedLanelet( const lanelet::ConstLanelet & lanelet_obj, const double left_offset, const double right_offset); diff --git a/tmp/lanelet2_extension/lib/utilities.cpp b/tmp/lanelet2_extension/lib/utilities.cpp index 450da120..6a274488 100644 --- a/tmp/lanelet2_extension/lib/utilities.cpp +++ b/tmp/lanelet2_extension/lib/utilities.cpp @@ -282,7 +282,7 @@ lanelet::ConstLineString3d getCenterlineWithOffset( const int num_segments = std::max(static_cast(ceil(longer_distance / resolution)), 1); // Resample points - const auto left_points = lanelet::utils::resamplePoints(lanelet_obj.leftBound(), num_segments); + const auto left_points = resamplePoints(lanelet_obj.leftBound(), num_segments); const auto right_points = resamplePoints(lanelet_obj.rightBound(), num_segments); // Create centerline @@ -303,6 +303,65 @@ lanelet::ConstLineString3d getCenterlineWithOffset( return static_cast(centerline); } +lanelet::ConstLineString3d getRightBoundWithOffset( + const lanelet::ConstLanelet & lanelet_obj, const double offset, const double resolution) +{ + // Get length of longer border + const double left_length = lanelet::geometry::length(lanelet_obj.leftBound()); + const double right_length = lanelet::geometry::length(lanelet_obj.rightBound()); + const double longer_distance = (left_length > right_length) ? left_length : right_length; + const int num_segments = std::max(static_cast(ceil(longer_distance / resolution)), 1); + + // Resample points + const auto left_points = resamplePoints(lanelet_obj.leftBound(), num_segments); + const auto right_points = resamplePoints(lanelet_obj.rightBound(), num_segments); + + // Create centerline + lanelet::LineString3d rightBound(lanelet::utils::getId()); + for (int i = 0; i < num_segments + 1; i++) { + // Add ID for the average point of left and right + const auto vec_left_2_right = (right_points.at(i) - left_points.at(i)).normalized(); + + const auto offset_right_basic_point = right_points.at(i) + vec_left_2_right * offset; + + const lanelet::Point3d rightBound_point( + lanelet::utils::getId(), offset_right_basic_point.x(), offset_right_basic_point.y(), + offset_right_basic_point.z()); + rightBound.push_back(rightBound_point); + } + return static_cast(rightBound); +} + +lanelet::ConstLineString3d getLeftBoundWithOffset( + const lanelet::ConstLanelet & lanelet_obj, const double offset, const double resolution) +{ + // Get length of longer border + const double left_length = lanelet::geometry::length(lanelet_obj.leftBound()); + const double right_length = lanelet::geometry::length(lanelet_obj.rightBound()); + const double longer_distance = (left_length > right_length) ? left_length : right_length; + const int num_segments = std::max(static_cast(ceil(longer_distance / resolution)), 1); + + // Resample points + const auto left_points = resamplePoints(lanelet_obj.leftBound(), num_segments); + const auto right_points = resamplePoints(lanelet_obj.rightBound(), num_segments); + + // Create centerline + lanelet::LineString3d leftBound(lanelet::utils::getId()); + for (int i = 0; i < num_segments + 1; i++) { + // Add ID for the average point of left and right + + const auto vec_right_2_left = (left_points.at(i) - right_points.at(i)).normalized(); + + const auto offset_left_basic_point = left_points.at(i) + vec_right_2_left * offset; + + const lanelet::Point3d leftBound_point( + lanelet::utils::getId(), offset_left_basic_point.x(), offset_left_basic_point.y(), + offset_left_basic_point.z()); + leftBound.push_back(leftBound_point); + } + return static_cast(leftBound); +} + lanelet::ConstLanelet getExpandedLanelet( const lanelet::ConstLanelet & lanelet_obj, const double left_offset, const double right_offset) { From 8b8615414b0ed19d8dea2fd2f80e019c6a98b379 Mon Sep 17 00:00:00 2001 From: NorahXiong <103234047+NorahXiong@users.noreply.github.com> Date: Wed, 15 Jun 2022 01:21:21 +0800 Subject: [PATCH 20/36] fix: is_in_parking_lot check method (#992) Signed-off-by: NorahXiong --- .../include/lanelet2_extension/utility/query.hpp | 5 +++++ tmp/lanelet2_extension/lib/query.cpp | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp index 0e0b3c74..84620d53 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp @@ -160,6 +160,11 @@ lanelet::ConstLanelets getLinkedLanelets( bool getLinkedParkingLot( const lanelet::ConstLanelet & lanelet, const lanelet::ConstPolygons3d & all_parking_lots, lanelet::ConstPolygon3d * linked_parking_lot); +// get linked parking lot from current pose of ego car +bool getLinkedParkingLot( + const lanelet::BasicPoint2d & current_position, const lanelet::ConstPolygons3d & all_parking_lots, + lanelet::ConstPolygon3d * linked_parking_lot); +// get linked parking lot from parking space bool getLinkedParkingLot( const lanelet::ConstLineString3d & parking_space, const lanelet::ConstPolygons3d & all_parking_lots, lanelet::ConstPolygon3d * linked_parking_lot); diff --git a/tmp/lanelet2_extension/lib/query.cpp b/tmp/lanelet2_extension/lib/query.cpp index 4eaf6f5c..a6bb9d42 100644 --- a/tmp/lanelet2_extension/lib/query.cpp +++ b/tmp/lanelet2_extension/lib/query.cpp @@ -450,6 +450,22 @@ bool query::getLinkedParkingLot( return false; } +// get overlapping parking lot +bool query::getLinkedParkingLot( + const lanelet::BasicPoint2d & current_position, const lanelet::ConstPolygons3d & all_parking_lots, + lanelet::ConstPolygon3d * linked_parking_lot) +{ + for (const auto & parking_lot : all_parking_lots) { + const double distance = + boost::geometry::distance(current_position, to2D(parking_lot).basicPolygon()); + if (distance < std::numeric_limits::epsilon()) { + *linked_parking_lot = parking_lot; + return true; + } + } + return false; +} + // get overlapping parking lot bool query::getLinkedParkingLot( const lanelet::ConstLineString3d & parking_space, From b5649c2a9c3c221fa85f459a5042c15fedaaa51b Mon Sep 17 00:00:00 2001 From: Kosuke Takeuchi Date: Wed, 15 Jun 2022 21:28:15 +0900 Subject: [PATCH 21/36] fix(behavior_velocity_planner): use only forward path for launching module (#1028) * fix(behavior_velocity_planner): use only foward path for launching module Signed-off-by: kosuke55 * Make functions common Signed-off-by: kosuke55 * Fix precommit include_what_you_use Signed-off-by: kosuke55 * Move functions to utils Signed-off-by: kosuke55 --- .../lanelet2_extension/utility/query.hpp | 4 ++++ tmp/lanelet2_extension/lib/query.cpp | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp index 84620d53..63bf8d17 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp @@ -227,6 +227,10 @@ bool getClosestLanelet( const ConstLanelets & lanelets, const geometry_msgs::msg::Pose & search_pose, ConstLanelet * closest_lanelet_ptr); +bool getCurrentLanelets( + const ConstLanelets & lanelets, const geometry_msgs::msg::Pose & search_pose, + ConstLanelets * current_lanelets_ptr); + /** * [getSucceedingLaneletSequences retrieves a sequence of lanelets after the given lanelet. * The total length of retrieved lanelet sequence at least given length. Returned lanelet sequence diff --git a/tmp/lanelet2_extension/lib/query.cpp b/tmp/lanelet2_extension/lib/query.cpp index a6bb9d42..a24c1b95 100644 --- a/tmp/lanelet2_extension/lib/query.cpp +++ b/tmp/lanelet2_extension/lib/query.cpp @@ -757,6 +757,30 @@ bool query::getClosestLanelet( return found; } +bool query::getCurrentLanelets( + const ConstLanelets & lanelets, const geometry_msgs::msg::Pose & search_pose, + ConstLanelets * current_lanelets_ptr) +{ + if (current_lanelets_ptr == nullptr) { + std::cerr << "argument closest_lanelet_ptr is null! Failed to find closest lanelet" + << std::endl; + return false; + } + + if (lanelets.empty()) { + return false; + } + + lanelet::BasicPoint2d search_point(search_pose.position.x, search_pose.position.y); + for (const auto & llt : lanelets) { + if (!lanelet::geometry::inside(llt, search_point)) { + current_lanelets_ptr->push_back(llt); + } + } + + return !current_lanelets_ptr->empty(); // return found +} + std::vector> getSucceedingLaneletSequencesRecursive( const routing::RoutingGraphPtr & graph, const lanelet::ConstLanelet & lanelet, const double length) From a168ed3d684400fb5e556da19e3296714a7c95ff Mon Sep 17 00:00:00 2001 From: Shumpei Wakabayashi <42209144+shmpwk@users.noreply.github.com> Date: Thu, 23 Jun 2022 19:39:08 +0900 Subject: [PATCH 22/36] feat(lanelet2_extension): add route checker (#1149) * feat(lanelet2_extension): add route checker Signed-off-by: Shumpei Wakabayashi * chore: move pkg from tier4_autoware_utils to lanelet2_extension Signed-off-by: Shumpei Wakabayashi * fix: test Signed-off-by: Shumpei Wakabayashi * ci(pre-commit): autofix * chore: add comment Signed-off-by: Shumpei Wakabayashi * ci(pre-commit): autofix * chore: rm unused pkg Signed-off-by: Shumpei Wakabayashi * Update map/lanelet2_extension/test/src/test_route_checker.cpp Co-authored-by: Kenji Miyake <31987104+kenji-miyake@users.noreply.github.com> * Update map/lanelet2_extension/test/src/test_route_checker.cpp Co-authored-by: Kenji Miyake <31987104+kenji-miyake@users.noreply.github.com> * chore: update code style Signed-off-by: Shumpei Wakabayashi * ci(pre-commit): autofix * fix: change interface Signed-off-by: Shumpei Wakabayashi * ci(pre-commit): autofix * fix: change style Signed-off-by: Shumpei Wakabayashi * ci(pre-commit): autofix Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Kenji Miyake <31987104+kenji-miyake@users.noreply.github.com> --- tmp/lanelet2_extension/CMakeLists.txt | 3 + .../utility/route_checker.hpp | 32 +++++++ tmp/lanelet2_extension/lib/route_checker.cpp | 44 +++++++++ tmp/lanelet2_extension/package.xml | 1 + .../test/src/test_route_checker.cpp | 94 +++++++++++++++++++ 5 files changed, 174 insertions(+) create mode 100644 tmp/lanelet2_extension/include/lanelet2_extension/utility/route_checker.hpp create mode 100644 tmp/lanelet2_extension/lib/route_checker.cpp create mode 100644 tmp/lanelet2_extension/test/src/test_route_checker.cpp diff --git a/tmp/lanelet2_extension/CMakeLists.txt b/tmp/lanelet2_extension/CMakeLists.txt index 709c7bda..a0db7fe7 100644 --- a/tmp/lanelet2_extension/CMakeLists.txt +++ b/tmp/lanelet2_extension/CMakeLists.txt @@ -42,6 +42,7 @@ ament_auto_add_library(lanelet2_extension_lib SHARED lib/utilities.cpp lib/virtual_traffic_light.cpp lib/visualization.cpp + lib/route_checker.cpp ) target_link_libraries(lanelet2_extension_lib ${GeographicLib_LIBRARIES} @@ -72,6 +73,8 @@ if(BUILD_TESTING) target_link_libraries(regulatory_elements-test lanelet2_extension_lib) ament_add_ros_isolated_gtest(utilities-test test/src/test_utilities.cpp) target_link_libraries(utilities-test lanelet2_extension_lib) + ament_add_ros_isolated_gtest(route-test test/src/test_route_checker.cpp) + target_link_libraries(route-test lanelet2_extension_lib) endif() ament_auto_package() diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/utility/route_checker.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/utility/route_checker.hpp new file mode 100644 index 00000000..8ab73e8e --- /dev/null +++ b/tmp/lanelet2_extension/include/lanelet2_extension/utility/route_checker.hpp @@ -0,0 +1,32 @@ +// Copyright 2022 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LANELET2_EXTENSION__UTILITY__ROUTE_CHECKER_HPP_ +#define LANELET2_EXTENSION__UTILITY__ROUTE_CHECKER_HPP_ + +#include +#include + +#include +#include + +namespace lanelet::utils::route +{ +using autoware_auto_mapping_msgs::msg::HADMapBin; +using autoware_auto_planning_msgs::msg::HADMapRoute; + +bool isRouteValid(const HADMapRoute route, const lanelet::LaneletMapPtr lanelet_map_ptr_); +} // namespace lanelet::utils::route + +#endif // LANELET2_EXTENSION__UTILITY__ROUTE_CHECKER_HPP_ diff --git a/tmp/lanelet2_extension/lib/route_checker.cpp b/tmp/lanelet2_extension/lib/route_checker.cpp new file mode 100644 index 00000000..c86fae96 --- /dev/null +++ b/tmp/lanelet2_extension/lib/route_checker.cpp @@ -0,0 +1,44 @@ +// Copyright 2022 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lanelet2_extension/utility/route_checker.hpp" + +#include + +namespace lanelet +{ +namespace utils +{ +bool route::isRouteValid(const HADMapRoute route_msg, const lanelet::LaneletMapPtr lanelet_map_ptr_) +{ + for (const auto & route_section : route_msg.segments) { + for (const auto & primitive : route_section.primitives) { + const auto id = primitive.id; + try { + lanelet_map_ptr_->laneletLayer.get(id); + } catch (const std::exception & e) { + std::cerr + << e.what() + << ". Maybe the loaded route was created on a different Map from the current one. " + "Try to load the other Route again." + << std::endl; + return false; + } + } + } + return true; +} + +} // namespace utils +} // namespace lanelet diff --git a/tmp/lanelet2_extension/package.xml b/tmp/lanelet2_extension/package.xml index d46d05a7..e85e9243 100644 --- a/tmp/lanelet2_extension/package.xml +++ b/tmp/lanelet2_extension/package.xml @@ -12,6 +12,7 @@ autoware_cmake autoware_auto_mapping_msgs + autoware_auto_planning_msgs geographiclib geometry_msgs lanelet2_core diff --git a/tmp/lanelet2_extension/test/src/test_route_checker.cpp b/tmp/lanelet2_extension/test/src/test_route_checker.cpp new file mode 100644 index 00000000..e3dc6bdd --- /dev/null +++ b/tmp/lanelet2_extension/test/src/test_route_checker.cpp @@ -0,0 +1,94 @@ +// Copyright 2022 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lanelet2_extension/utility/message_conversion.hpp" +#include "lanelet2_extension/utility/route_checker.hpp" + +#include +#include + +#include + +using lanelet::Lanelet; +using lanelet::LineString3d; +using lanelet::Point3d; +using lanelet::utils::getId; + +class TestSuite : public ::testing::Test +{ +public: + TestSuite() : sample_map_ptr(new lanelet::LaneletMap()) + { + // create sample lanelets + const Point3d p1(getId(), 0.0, 0.0, 0.0); + const Point3d p2(getId(), 0.0, 1.0, 0.0); + + const LineString3d ls_left(getId(), {p1, p2}); + + const Point3d p3(getId(), 1.0, 0.0, 0.0); + const Point3d p4(getId(), 1.0, 0.0, 0.0); + + const LineString3d ls_right(getId(), {p3, p4}); + + const Lanelet lanelet(getId(), ls_left, ls_right); + + sample_map_ptr->add(lanelet); + + // create sample routes + autoware_auto_mapping_msgs::msg::MapPrimitive map_primitive; + autoware_auto_mapping_msgs::msg::HADMapSegment map_segment1; + autoware_auto_mapping_msgs::msg::HADMapSegment map_segment2; + + for (size_t i = 0; i < 2; i++) { + map_primitive.id = lanelet.id(); + map_segment1.primitives.push_back(map_primitive); + map_primitive.id = ls_left.id(); + map_segment2.primitives.push_back(map_primitive); + } + sample_route1.segments.push_back(map_segment1); + sample_route2.segments.push_back(map_segment2); + } + ~TestSuite() {} + + lanelet::LaneletMapPtr sample_map_ptr; + autoware_auto_planning_msgs::msg::HADMapRoute sample_route1; // valid route + autoware_auto_planning_msgs::msg::HADMapRoute sample_route2; // invalid route + +private: +}; + +TEST_F(TestSuite, isRouteValid) +{ + autoware_auto_mapping_msgs::msg::HADMapBin bin_msg; + + const auto route_ptr1 = + std::make_shared(sample_route1); + const auto route_ptr2 = + std::make_shared(sample_route2); + + // toBinMsg is tested at test_message_conversion.cpp + lanelet::utils::conversion::toBinMsg(sample_map_ptr, &bin_msg); + + ASSERT_TRUE(lanelet::utils::route::isRouteValid(*route_ptr1, sample_map_ptr)) + << "The route should be valid, which should be created on the same map as the current one"; + ASSERT_FALSE(lanelet::utils::route::isRouteValid(*route_ptr2, sample_map_ptr)) + << "The route should be invalid, which should be created on the different map from the current " + "one"; +} + +int main(int argc, char ** argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From 2dab5c3e2b9dd11bad075fb0474110b4e2c7b29f Mon Sep 17 00:00:00 2001 From: Shumpei Wakabayashi <42209144+shmpwk@users.noreply.github.com> Date: Fri, 1 Jul 2022 16:31:31 +0900 Subject: [PATCH 23/36] docs(lanelet2_extention): add route cheker and update launching args (#1205) * docs(lanelet2_extention): update lanelet2 validation Signed-off-by: Shumpei Wakabayashi * docs(lanelet2_extention): add route checker Signed-off-by: Shumpei Wakabayashi --- tmp/lanelet2_extension/README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tmp/lanelet2_extension/README.md b/tmp/lanelet2_extension/README.md index 6da971b7..af88db61 100644 --- a/tmp/lanelet2_extension/README.md +++ b/tmp/lanelet2_extension/README.md @@ -61,6 +61,12 @@ e.g. crosswalks, trafficlights, stoplines This module contains other useful functions related to Lanelet. e.g. matching waypoint with lanelets +#### Route Checker + +This module contains a function to check the loading route is valid or not. +If it is invalid, puts warning without dying. +The case valid is when the route is created on the same map as the current one. + ### Visualization Visualization contains functions to convert lanelet objects into visualization marker messages. @@ -83,5 +89,5 @@ This node checks if an .osm file follows the Autoware version of Lanelet2 format You can check by running: ```sh -ros2 run lanelet2_extension autoware_lanelet2_validation _map_file:= +ros2 run lanelet2_extension autoware_lanelet2_validation --ros-args -p map_file:= ``` From 2442675e3b322c4a10aaad39a2e0a5b9291bbb7c Mon Sep 17 00:00:00 2001 From: Kenji Miyake Date: Tue, 5 Jul 2022 14:43:58 +0900 Subject: [PATCH 24/36] docs: update README Signed-off-by: Kenji Miyake --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index db5f8106..fb07757a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # autoware_common Commonly referenced ROS library packages for [Autoware](https://github.com/autowarefoundation/autoware). + +Note that the packages under `tmp/` may introduce breaking changes and is planned to be replaced by re-designed packages in the future. From 1a1efa0e149b3b92460e77acc358597f9db15710 Mon Sep 17 00:00:00 2001 From: Kenji Miyake Date: Mon, 1 Aug 2022 09:19:10 +0900 Subject: [PATCH 25/36] refactor Signed-off-by: Kenji Miyake --- build_depends.repos | 4 ++++ tmp/lanelet2_extension/lib/query.cpp | 9 +++++++-- tmp/lanelet2_extension/lib/visualization.cpp | 2 -- tmp/lanelet2_extension/package.xml | 5 ++--- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/build_depends.repos b/build_depends.repos index 56f46b6f..4b86fbe3 100644 --- a/build_depends.repos +++ b/build_depends.repos @@ -1 +1,5 @@ repositories: + tmp/autoware_auto_msgs: + type: git + url: https://github.com/tier4/autoware_auto_msgs.git + version: tier4/main diff --git a/tmp/lanelet2_extension/lib/query.cpp b/tmp/lanelet2_extension/lib/query.cpp index a24c1b95..f4b95fcf 100644 --- a/tmp/lanelet2_extension/lib/query.cpp +++ b/tmp/lanelet2_extension/lib/query.cpp @@ -20,11 +20,16 @@ #include "lanelet2_extension/utility/utilities.hpp" #include -#include +#include #include #include #include +#ifdef ROS_DISTRO_GALACTIC +#include "tf2_geometry_msgs/tf2_geometry_msgs.h" +#else +#include "tf2_geometry_msgs/tf2_geometry_msgs.hpp" +#endif #include #include @@ -746,7 +751,7 @@ bool query::getClosestLanelet( lanelet::ConstLineString3d segment = getClosestSegment(search_point, llt.centerline()); double segment_angle = std::atan2( segment.back().y() - segment.front().y(), segment.back().x() - segment.front().x()); - double angle_diff = std::abs(tier4_autoware_utils::normalizeRadian(segment_angle - pose_yaw)); + double angle_diff = std::abs(autoware_utils::normalize_radian(segment_angle - pose_yaw)); if (angle_diff < min_angle) { min_angle = angle_diff; *closest_lanelet_ptr = llt; diff --git a/tmp/lanelet2_extension/lib/visualization.cpp b/tmp/lanelet2_extension/lib/visualization.cpp index da7fe490..c1feaa40 100644 --- a/tmp/lanelet2_extension/lib/visualization.cpp +++ b/tmp/lanelet2_extension/lib/visualization.cpp @@ -21,8 +21,6 @@ #include "lanelet2_extension/utility/utilities.hpp" #include -#include -#include #include #include diff --git a/tmp/lanelet2_extension/package.xml b/tmp/lanelet2_extension/package.xml index e85e9243..6b194d71 100644 --- a/tmp/lanelet2_extension/package.xml +++ b/tmp/lanelet2_extension/package.xml @@ -25,12 +25,11 @@ pugixml-dev rclcpp tf2 - tier4_autoware_utils + tf2_geometry_msgs + autoware_utils visualization_msgs ament_cmake_ros - ament_lint_auto - autoware_lint_common ament_cmake From 76be068c1a13a9238a0da8be6cc1e7469277ae30 Mon Sep 17 00:00:00 2001 From: Kenji Miyake Date: Mon, 1 Aug 2022 13:18:31 +0900 Subject: [PATCH 26/36] fix Clang-Tidy warnings Signed-off-by: Kenji Miyake --- .../io/autoware_osm_parser.hpp | 9 +- .../projection/mgrs_projector.hpp | 4 +- .../autoware_traffic_light.hpp | 14 +- .../regulatory_elements/no_stopping_area.hpp | 7 +- .../regulatory_elements/road_marking.hpp | 2 +- .../virtual_traffic_light.hpp | 13 +- .../utility/message_conversion.hpp | 10 +- .../lanelet2_extension/utility/query.hpp | 30 ++-- .../utility/route_checker.hpp | 2 +- .../lanelet2_extension/utility/utilities.hpp | 7 +- .../visualization/visualization.hpp | 57 +++---- .../lib/autoware_osm_parser.cpp | 5 +- .../lib/autoware_traffic_light.cpp | 13 +- tmp/lanelet2_extension/lib/detection_area.cpp | 4 - .../lib/message_conversion.cpp | 12 +- tmp/lanelet2_extension/lib/mgrs_projector.cpp | 10 +- .../lib/no_stopping_area.cpp | 11 +- tmp/lanelet2_extension/lib/query.cpp | 98 +++++------ tmp/lanelet2_extension/lib/road_marking.cpp | 11 +- tmp/lanelet2_extension/lib/route_checker.cpp | 10 +- tmp/lanelet2_extension/lib/utilities.cpp | 22 +-- .../lib/virtual_traffic_light.cpp | 11 +- tmp/lanelet2_extension/lib/visualization.cpp | 160 +++++++++--------- tmp/lanelet2_extension/package.xml | 2 +- tmp/lanelet2_extension/src/sample_code.cpp | 21 +-- tmp/lanelet2_extension/src/validation.cpp | 19 +-- .../test/src/test_message_conversion.cpp | 25 ++- .../test/src/test_projector.cpp | 13 +- .../test/src/test_query.cpp | 35 ++-- .../test/src/test_regulatory_elements.cpp | 38 +++-- .../test/src/test_route_checker.cpp | 7 +- .../test/src/test_utilities.cpp | 36 ++-- 32 files changed, 331 insertions(+), 387 deletions(-) diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/io/autoware_osm_parser.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/io/autoware_osm_parser.hpp index 3dd7ce63..b1f15453 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/io/autoware_osm_parser.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/io/autoware_osm_parser.hpp @@ -22,9 +22,7 @@ #include #include -namespace lanelet -{ -namespace io_handlers +namespace lanelet::io_handlers { class AutowareOsmParser : public OsmParser { @@ -40,7 +38,7 @@ class AutowareOsmParser : public OsmParser * @return [returns LaneletMap] */ std::unique_ptr parse( - const std::string & filename, ErrorMessages & errors) const; // NOLINT + const std::string & filename, ErrorMessages & errors) const override; /** * [parseVersions parses MetaInfo tags from osm file] @@ -56,7 +54,6 @@ class AutowareOsmParser : public OsmParser static constexpr const char * name() { return "autoware_osm_handler"; } }; -} // namespace io_handlers -} // namespace lanelet +} // namespace lanelet::io_handlers #endif // LANELET2_EXTENSION__IO__AUTOWARE_OSM_PARSER_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp index 402162b8..0cb92abf 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp @@ -33,7 +33,7 @@ namespace projection class MGRSProjector : public Projector { public: - explicit MGRSProjector(Origin origin = Origin({0.0, 0.0})); // NOLINT + explicit MGRSProjector(Origin origin = Origin({0.0, 0.0})); /** * [MGRSProjector::forward projects gps lat/lon to MGRS 100km grid] @@ -66,7 +66,7 @@ class MGRSProjector : public Projector * @param mgrs_code [MGRS grid code] * @return [projected point in WGS84] */ - GPSPoint reverse(const BasicPoint3d & mgrs_point, const std::string & mgrs_code) const; + static GPSPoint reverse(const BasicPoint3d & mgrs_point, const std::string & mgrs_code); /** * [MGRSProjector::setMGRSCode sets MGRS code used for reverse projection] diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp index 6b385663..ca3fd7e0 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp @@ -23,9 +23,7 @@ #include #include -namespace lanelet -{ -namespace autoware +namespace lanelet::autoware { struct AutowareRoleNameString { @@ -83,14 +81,6 @@ class AutowareTrafficLight : public lanelet::TrafficLight }; static lanelet::RegisterRegulatoryElement regAutowareTraffic; -// moved to lanelet2_extension/lib/autoware_traffic_light.cpp to avoid multiple -// definition errors -/* -#if __cplusplus < 201703L -constexpr char AutowareTrafficLight::RuleName[]; // instantiate string in -cpp file #endif -*/ -} // namespace autoware -} // namespace lanelet +} // namespace lanelet::autoware #endif // LANELET2_EXTENSION__REGULATORY_ELEMENTS__AUTOWARE_TRAFFIC_LIGHT_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/no_stopping_area.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/no_stopping_area.hpp index 9e2ca0c7..1f1ec77f 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/no_stopping_area.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/no_stopping_area.hpp @@ -21,9 +21,7 @@ #include #include -namespace lanelet -{ -namespace autoware +namespace lanelet::autoware { class NoStoppingArea : public lanelet::RegulatoryElement { @@ -87,7 +85,6 @@ class NoStoppingArea : public lanelet::RegulatoryElement }; static lanelet::RegisterRegulatoryElement regNoStoppingArea; -} // namespace autoware -} // namespace lanelet +} // namespace lanelet::autoware #endif // LANELET2_EXTENSION__REGULATORY_ELEMENTS__NO_STOPPING_AREA_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/road_marking.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/road_marking.hpp index 62b719d2..f1808b2c 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/road_marking.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/road_marking.hpp @@ -60,7 +60,7 @@ class RoadMarking : public lanelet::RegulatoryElement // the following lines are required so that lanelet2 can create this object // when loading a map with this regulatory element friend class lanelet::RegisterRegulatoryElement; - RoadMarking(Id id, const AttributeMap & attributes, const LineString3d & roadMarking); + RoadMarking(Id id, const AttributeMap & attributes, const LineString3d & road_marking); explicit RoadMarking(const lanelet::RegulatoryElementDataPtr & data); }; static lanelet::RegisterRegulatoryElement regRoadMarking; diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/virtual_traffic_light.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/virtual_traffic_light.hpp index d4049953..4779a75b 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/virtual_traffic_light.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/virtual_traffic_light.hpp @@ -38,12 +38,12 @@ class VirtualTrafficLight : public lanelet::RegulatoryElement return Ptr{new VirtualTrafficLight(id, attributes, virtual_traffic_light)}; } - ConstLineString3d getVirtualTrafficLight() const + [[nodiscard]] ConstLineString3d getVirtualTrafficLight() const { return getParameters(RoleName::Refers).front(); } - Optional getStopLine() const + [[nodiscard]] Optional getStopLine() const { const auto vec = getParameters(RoleName::RefLine); if (vec.empty()) { @@ -52,12 +52,15 @@ class VirtualTrafficLight : public lanelet::RegulatoryElement return vec.front(); } - ConstLineString3d getStartLine() const + [[nodiscard]] ConstLineString3d getStartLine() const { return getParameters("start_line").front(); } - ConstLineStrings3d getEndLines() const { return getParameters("end_line"); } + [[nodiscard]] ConstLineStrings3d getEndLines() const + { + return getParameters("end_line"); + } private: // the following lines are required so that lanelet2 can create this object @@ -65,7 +68,7 @@ class VirtualTrafficLight : public lanelet::RegulatoryElement friend class lanelet::RegisterRegulatoryElement; VirtualTrafficLight( - const Id id, const AttributeMap & attributes, const LineString3d & virtualTrafficLight); + const Id id, const AttributeMap & attributes, const LineString3d & virtual_traffic_light); explicit VirtualTrafficLight(const lanelet::RegulatoryElementDataPtr & data); }; diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/utility/message_conversion.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/utility/message_conversion.hpp index a1886b86..b82efbab 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/utility/message_conversion.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/utility/message_conversion.hpp @@ -26,11 +26,7 @@ #include #include -namespace lanelet -{ -namespace utils -{ -namespace conversion +namespace lanelet::utils::conversion { /** * [toBinMsg converts lanelet2 map to ROS message. Similar implementation to @@ -93,8 +89,6 @@ void toGeomMsgPoly( */ void toGeomMsgPt32(const Eigen::Vector3d & src, geometry_msgs::msg::Point32 * dst); -} // namespace conversion -} // namespace utils -} // namespace lanelet +} // namespace lanelet::utils::conversion #endif // LANELET2_EXTENSION__UTILITY__MESSAGE_CONVERSION_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp index 63bf8d17..9faaa190 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp @@ -41,11 +41,7 @@ using DetectionAreaConstPtr = std::shared_ptr; } // namespace lanelet -namespace lanelet -{ -namespace utils -{ -namespace query +namespace lanelet::utils::query { /** * [laneletLayer converts laneletLayer into lanelet vector] @@ -61,35 +57,35 @@ lanelet::ConstLanelets laneletLayer(const lanelet::LaneletMapConstPtr & ll_Map); * lanelet::AttributeValueString::Road)] * @return [lanelets with given subtype] */ -lanelet::ConstLanelets subtypeLanelets(const lanelet::ConstLanelets lls, const char subtype[]); +lanelet::ConstLanelets subtypeLanelets(const lanelet::ConstLanelets & lls, const char subtype[]); /** * [crosswalkLanelets extracts crosswalk lanelets] * @param lls [input lanelets with various subtypes] * @return [crosswalk lanelets] */ -lanelet::ConstLanelets crosswalkLanelets(const lanelet::ConstLanelets lls); -lanelet::ConstLanelets walkwayLanelets(const lanelet::ConstLanelets lls); +lanelet::ConstLanelets crosswalkLanelets(const lanelet::ConstLanelets & lls); +lanelet::ConstLanelets walkwayLanelets(const lanelet::ConstLanelets & lls); /** * [roadLanelets extracts road lanelets] * @param lls [input lanelets with subtype road] * @return [road lanelets] */ -lanelet::ConstLanelets roadLanelets(const lanelet::ConstLanelets lls); +lanelet::ConstLanelets roadLanelets(const lanelet::ConstLanelets & lls); /** * [shoulderLanelets extracts shoulder lanelets] * @param lls [input lanelets with subtype shoulder] * @return [shoulder lanelets] */ -lanelet::ConstLanelets shoulderLanelets(const lanelet::ConstLanelets lls); +lanelet::ConstLanelets shoulderLanelets(const lanelet::ConstLanelets & lls); /** * [trafficLights extracts Traffic Light regulatory element from lanelets] * @param lanelets [input lanelets] * @return [traffic light that are associated with input lanelets] */ -std::vector trafficLights(const lanelet::ConstLanelets lanelets); +std::vector trafficLights(const lanelet::ConstLanelets & lanelets); /** * [autowareTrafficLights extracts Autoware Traffic Light regulatory element @@ -99,7 +95,7 @@ std::vector trafficLights(const lanelet::ConstLan * lanelets] */ std::vector autowareTrafficLights( - const lanelet::ConstLanelets lanelets); + const lanelet::ConstLanelets & lanelets); /** * [detectionAreas extracts Detection Area regulatory elements from lanelets] @@ -182,7 +178,7 @@ lanelet::ConstLanelets getLinkedLanelets( * @param lanelets [input lanelets] * @return [stop lines that are associated with input lanelets] */ -std::vector stopLinesLanelets(const lanelet::ConstLanelets lanelets); +std::vector stopLinesLanelets(const lanelet::ConstLanelets & lanelets); /** * [stopLinesLanelet extracts stop lines that are associated with a given @@ -190,7 +186,7 @@ std::vector stopLinesLanelets(const lanelet::ConstLa * @param ll [input lanelet] * @return [stop lines that are associated with input lanelet] */ -std::vector stopLinesLanelet(const lanelet::ConstLanelet ll); +std::vector stopLinesLanelet(const lanelet::ConstLanelet & ll); /** * [stopSignStopLines extracts stoplines that are associated with stop signs] @@ -199,7 +195,7 @@ std::vector stopLinesLanelet(const lanelet::ConstLan * @return [array of stoplines] */ std::vector stopSignStopLines( - const lanelet::ConstLanelets lanelets, const std::string & stop_sign_id = "stop_sign"); + const lanelet::ConstLanelets & lanelets, const std::string & stop_sign_id = "stop_sign"); ConstLanelets getLaneletsWithinRange( const lanelet::ConstLanelets & lanelets, const lanelet::BasicPoint2d & search_point, @@ -257,8 +253,6 @@ std::vector getPrecedingLaneletSequences( const routing::RoutingGraphPtr & graph, const lanelet::ConstLanelet & lanelet, const double length, const lanelet::ConstLanelets & exclude_lanelets = {}); -} // namespace query -} // namespace utils -} // namespace lanelet +} // namespace lanelet::utils::query #endif // LANELET2_EXTENSION__UTILITY__QUERY_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/utility/route_checker.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/utility/route_checker.hpp index 8ab73e8e..3f4f267f 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/utility/route_checker.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/utility/route_checker.hpp @@ -26,7 +26,7 @@ namespace lanelet::utils::route using autoware_auto_mapping_msgs::msg::HADMapBin; using autoware_auto_planning_msgs::msg::HADMapRoute; -bool isRouteValid(const HADMapRoute route, const lanelet::LaneletMapPtr lanelet_map_ptr_); +bool isRouteValid(const HADMapRoute & route, const lanelet::LaneletMapPtr lanelet_map_ptr_); } // namespace lanelet::utils::route #endif // LANELET2_EXTENSION__UTILITY__ROUTE_CHECKER_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp index 6737da3e..9e365f16 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp @@ -27,9 +27,7 @@ #include -namespace lanelet -{ -namespace utils +namespace lanelet::utils { lanelet::LineString3d generateFineCenterline( const lanelet::ConstLanelet & lanelet_obj, const double resolution = 5.0); @@ -82,7 +80,6 @@ bool isInLanelet( const geometry_msgs::msg::Pose & current_pose, const lanelet::ConstLanelet & lanelet, const double radius = 0.0); -} // namespace utils -} // namespace lanelet +} // namespace lanelet::utils #endif // LANELET2_EXTENSION__UTILITY__UTILITIES_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp index 682f2c3d..1ad6057b 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp @@ -32,9 +32,7 @@ #include #include -namespace lanelet -{ -namespace visualization +namespace lanelet::visualization { /** * [lanelet2Triangle converts lanelet into vector of triangles. Used for @@ -69,8 +67,8 @@ void lanelet2Polygon(const lanelet::ConstLanelet & ll, geometry_msgs::msg::Polyg * @param c [color of the marker] */ void initLineStringMarker( - visualization_msgs::msg::Marker * marker, const std::string frame_id, const std::string ns, - const std_msgs::msg::ColorRGBA c); + visualization_msgs::msg::Marker * marker, const std::string & frame_id, const std::string & ns, + const std_msgs::msg::ColorRGBA & c); /** * [pushLineStringMarker pushes marker vertices to visualize shape of linestring] @@ -81,7 +79,7 @@ void initLineStringMarker( */ void pushLineStringMarker( visualization_msgs::msg::Marker * marker, const lanelet::ConstLineString3d & ls, - const std_msgs::msg::ColorRGBA c, const float lss = 0.1); + const std_msgs::msg::ColorRGBA & c, const float lss = 0.1); /** * [initArrowsMarker initializes marker to visualize arrows with TRIANGLE_LIST] @@ -91,8 +89,8 @@ void pushLineStringMarker( * @param c [color of the marker] */ void initArrowsMarker( - visualization_msgs::msg::Marker * marker, const std::string frame_id, const std::string ns, - const std_msgs::msg::ColorRGBA c); + visualization_msgs::msg::Marker * marker, const std::string & frame_id, const std::string & ns, + const std_msgs::msg::ColorRGBA & c); /** * [pushArrowsMarker pushes marker to visualize arrows with TRIANGLE_LIST] @@ -102,7 +100,7 @@ void initArrowsMarker( */ void pushArrowsMarker( visualization_msgs::msg::Marker * marker, const lanelet::ConstLineString3d & ls, - const std_msgs::msg::ColorRGBA c); + const std_msgs::msg::ColorRGBA & c); /** * [initTrafficLightTriangleMarker initializes marker to visualize shape of traffic @@ -112,8 +110,8 @@ void pushArrowsMarker( * @param duration [lifetime of the marker] */ void initTrafficLightTriangleMarker( - visualization_msgs::msg::Marker * marker, const std::string ns, - const rclcpp::Duration duration = rclcpp::Duration(0, 0)); + visualization_msgs::msg::Marker * marker, const std::string & ns, + const rclcpp::Duration & duration = rclcpp::Duration(0, 0)); /** * [pushTrafficLightTriangleMarker pushes marker vertices to visualize shape of traffic @@ -124,8 +122,8 @@ void initTrafficLightTriangleMarker( * @param scale [scale of the marker] */ void pushTrafficLightTriangleMarker( - visualization_msgs::msg::Marker * marker, const lanelet::ConstLineString3d ls, - const std_msgs::msg::ColorRGBA cl, const double scale = 1.0); + visualization_msgs::msg::Marker * marker, const lanelet::ConstLineString3d & ls, + const std_msgs::msg::ColorRGBA & cl, const double scale = 1.0); /** * [laneletsBoundaryAsMarkerArray create marker array to visualize shape of @@ -136,7 +134,7 @@ void pushTrafficLightTriangleMarker( * @return [created marker array] */ visualization_msgs::msg::MarkerArray laneletsBoundaryAsMarkerArray( - const lanelet::ConstLanelets & lanelets, const std_msgs::msg::ColorRGBA c, + const lanelet::ConstLanelets & lanelets, const std_msgs::msg::ColorRGBA & c, const bool viz_centerline, const std::string & additional_namespace = ""); /** * [laneletsAsTriangleMarkerArray create marker array to visualize shape of the @@ -147,7 +145,8 @@ visualization_msgs::msg::MarkerArray laneletsBoundaryAsMarkerArray( * @return [created marker] */ visualization_msgs::msg::MarkerArray laneletsAsTriangleMarkerArray( - const std::string ns, const lanelet::ConstLanelets & lanelets, const std_msgs::msg::ColorRGBA c); + const std::string & ns, const lanelet::ConstLanelets & lanelets, + const std_msgs::msg::ColorRGBA & c); /** * [laneletDirectionAsMarkerArray create marker array to visualize direction of @@ -156,7 +155,7 @@ visualization_msgs::msg::MarkerArray laneletsAsTriangleMarkerArray( * @return [created marker array] */ visualization_msgs::msg::MarkerArray laneletDirectionAsMarkerArray( - const lanelet::ConstLanelets lanelets, const std::string & additional_namespace = ""); + const lanelet::ConstLanelets & lanelets, const std::string & additional_namespace = ""); /** * [lineStringsAsMarkerArray creates marker array to visualize shape of @@ -168,8 +167,8 @@ visualization_msgs::msg::MarkerArray laneletDirectionAsMarkerArray( * @return [created marker array] */ visualization_msgs::msg::MarkerArray lineStringsAsMarkerArray( - const std::vector line_strings, const std::string name_space, - const std_msgs::msg::ColorRGBA c, const double lss); + const std::vector & line_strings, const std::string & name_space, + const std_msgs::msg::ColorRGBA & c, const double lss); /** * [autowareTrafficLightsAsMarkerArray creates marker array to visualize traffic @@ -180,8 +179,8 @@ visualization_msgs::msg::MarkerArray lineStringsAsMarkerArray( * @return [created marker array] */ visualization_msgs::msg::MarkerArray autowareTrafficLightsAsMarkerArray( - const std::vector tl_reg_elems, - const std_msgs::msg::ColorRGBA c, const rclcpp::Duration duration = rclcpp::Duration(0, 0), + const std::vector & tl_reg_elems, + const std_msgs::msg::ColorRGBA & c, const rclcpp::Duration & duration = rclcpp::Duration(0, 0), const double scale = 1.0); /** @@ -193,8 +192,8 @@ visualization_msgs::msg::MarkerArray autowareTrafficLightsAsMarkerArray( * @return [created marker array] */ visualization_msgs::msg::MarkerArray generateTrafficLightIdMaker( - const std::vector tl_reg_elems, - const std_msgs::msg::ColorRGBA c, const rclcpp::Duration duration = rclcpp::Duration(0, 0), + const std::vector & tl_reg_elems, + const std_msgs::msg::ColorRGBA & c, const rclcpp::Duration & duration = rclcpp::Duration(0, 0), const double scale = 1.0); /** @@ -206,8 +205,9 @@ visualization_msgs::msg::MarkerArray generateTrafficLightIdMaker( * @return [created marker array] */ visualization_msgs::msg::MarkerArray trafficLightsAsTriangleMarkerArray( - const std::vector tl_reg_elems, const std_msgs::msg::ColorRGBA c, - const rclcpp::Duration duration = rclcpp::Duration(0, 0), const double scale = 1.0); + const std::vector & tl_reg_elems, + const std_msgs::msg::ColorRGBA & c, const rclcpp::Duration & duration = rclcpp::Duration(0, 0), + const double scale = 1.0); /** * [detectionAreasAsMarkerArray creates marker array to visualize detection areas] @@ -217,7 +217,7 @@ visualization_msgs::msg::MarkerArray trafficLightsAsTriangleMarkerArray( */ visualization_msgs::msg::MarkerArray detectionAreasAsMarkerArray( const std::vector & da_reg_elems, - const std_msgs::msg::ColorRGBA c, const rclcpp::Duration duration = rclcpp::Duration(0, 0)); + const std_msgs::msg::ColorRGBA & c, const rclcpp::Duration & duration = rclcpp::Duration(0, 0)); /** * [noStoppingAreasAsMarkerArray creates marker array to visualize detection areas] @@ -227,7 +227,7 @@ visualization_msgs::msg::MarkerArray detectionAreasAsMarkerArray( */ visualization_msgs::msg::MarkerArray noStoppingAreasAsMarkerArray( const std::vector & no_reg_elems, - const std_msgs::msg::ColorRGBA c, const rclcpp::Duration duration = rclcpp::Duration(0, 0)); + const std_msgs::msg::ColorRGBA & c, const rclcpp::Duration & duration = rclcpp::Duration(0, 0)); /** * [pedestrianMarkingsAsMarkerArray creates marker array to visualize pedestrian markings] @@ -262,13 +262,12 @@ visualization_msgs::msg::MarkerArray parkingSpacesAsMarkerArray( * @return visualization_msgs::msg::MarkerArray */ visualization_msgs::msg::MarkerArray generateLaneletIdMarker( - const lanelet::ConstLanelets road_lanelets, const std_msgs::msg::ColorRGBA c, + const lanelet::ConstLanelets & road_lanelets, const std_msgs::msg::ColorRGBA & c, const double scale = 0.5); visualization_msgs::msg::MarkerArray obstaclePolygonsAsMarkerArray( const lanelet::ConstPolygons3d & obstacle_polygons, const std_msgs::msg::ColorRGBA & c); -} // namespace visualization -} // namespace lanelet +} // namespace lanelet::visualization #endif // LANELET2_EXTENSION__VISUALIZATION__VISUALIZATION_HPP_ diff --git a/tmp/lanelet2_extension/lib/autoware_osm_parser.cpp b/tmp/lanelet2_extension/lib/autoware_osm_parser.cpp index 984147a4..5e656491 100644 --- a/tmp/lanelet2_extension/lib/autoware_osm_parser.cpp +++ b/tmp/lanelet2_extension/lib/autoware_osm_parser.cpp @@ -45,7 +45,8 @@ std::unique_ptr AutowareOsmParser::parse( // rerun align function in just in case for (Lanelet & lanelet : map->laneletLayer) { - LineString3d new_left, new_right; + LineString3d new_left; + LineString3d new_right; std::tie(new_left, new_right) = geometry::align(lanelet.leftBound(), lanelet.rightBound()); lanelet.setLeftBound(new_left); lanelet.setRightBound(new_right); @@ -57,7 +58,7 @@ std::unique_ptr AutowareOsmParser::parse( namespace { RegisterParser regParser; -} +} // namespace void AutowareOsmParser::parseVersions( const std::string & filename, std::string * format_version, std::string * map_version) diff --git a/tmp/lanelet2_extension/lib/autoware_traffic_light.cpp b/tmp/lanelet2_extension/lib/autoware_traffic_light.cpp index d7c7b503..7b12274b 100644 --- a/tmp/lanelet2_extension/lib/autoware_traffic_light.cpp +++ b/tmp/lanelet2_extension/lib/autoware_traffic_light.cpp @@ -25,9 +25,7 @@ #include #include -namespace lanelet -{ -namespace autoware +namespace lanelet::autoware { namespace { @@ -110,8 +108,6 @@ LineStringsOrPolygons3d getLsOrPoly(const RuleParameterMap & paramsMap, RoleName } } // namespace -constexpr const char AutowareRoleNameString::LightBulbs[]; - AutowareTrafficLight::AutowareTrafficLight(const RegulatoryElementDataPtr & data) : TrafficLight(data) { @@ -143,9 +139,4 @@ bool AutowareTrafficLight::removeLightBulbs(const LineStringOrPolygon3d & primit primitive.asRuleParameter(), ¶meters().find(AutowareRoleNameString::LightBulbs)->second); } -#if __cplusplus < 201703L -constexpr char AutowareTrafficLight::RuleName[]; // instantiate string in cpp file -#endif - -} // namespace autoware -} // namespace lanelet +} // namespace lanelet::autoware diff --git a/tmp/lanelet2_extension/lib/detection_area.cpp b/tmp/lanelet2_extension/lib/detection_area.cpp index b9cf5650..763974c3 100644 --- a/tmp/lanelet2_extension/lib/detection_area.cpp +++ b/tmp/lanelet2_extension/lib/detection_area.cpp @@ -149,9 +149,5 @@ void DetectionArea::setStopLine(const LineString3d & stopLine) void DetectionArea::removeStopLine() { parameters()[RoleName::RefLine] = {}; } -#if __cplusplus < 201703L -constexpr char DetectionArea::RuleName[]; // instantiate string in cpp file -#endif - } // namespace autoware } // namespace lanelet diff --git a/tmp/lanelet2_extension/lib/message_conversion.cpp b/tmp/lanelet2_extension/lib/message_conversion.cpp index bc58e403..c4bf4067 100644 --- a/tmp/lanelet2_extension/lib/message_conversion.cpp +++ b/tmp/lanelet2_extension/lib/message_conversion.cpp @@ -33,11 +33,7 @@ #include #include -namespace lanelet -{ -namespace utils -{ -namespace conversion +namespace lanelet::utils::conversion { void toBinMsg(const lanelet::LaneletMapPtr & map, autoware_auto_mapping_msgs::msg::HADMapBin * msg) { @@ -72,7 +68,7 @@ void fromBinMsg(const autoware_auto_mapping_msgs::msg::HADMapBin & msg, lanelet: ss << data_str; boost::archive::binary_iarchive oa(ss); oa >> *map; - lanelet::Id id_counter; + lanelet::Id id_counter = 0; oa >> id_counter; lanelet::utils::registerId(id_counter); // *map = std::move(laneletMap); @@ -189,6 +185,4 @@ void toGeomMsgPoly(const lanelet::ConstPolygon3d & ll_poly, geometry_msgs::msg:: } } -} // namespace conversion -} // namespace utils -} // namespace lanelet +} // namespace lanelet::utils::conversion diff --git a/tmp/lanelet2_extension/lib/mgrs_projector.cpp b/tmp/lanelet2_extension/lib/mgrs_projector.cpp index 3caa85af..a45478f5 100644 --- a/tmp/lanelet2_extension/lib/mgrs_projector.cpp +++ b/tmp/lanelet2_extension/lib/mgrs_projector.cpp @@ -22,9 +22,7 @@ #include #include -namespace lanelet -{ -namespace projection +namespace lanelet::projection { MGRSProjector::MGRSProjector(Origin origin) : Projector(origin) {} @@ -83,8 +81,7 @@ GPSPoint MGRSProjector::reverse(const BasicPoint3d & mgrs_point) const return gps; } -GPSPoint MGRSProjector::reverse( - const BasicPoint3d & mgrs_point, const std::string & mgrs_code) const +GPSPoint MGRSProjector::reverse(const BasicPoint3d & mgrs_point, const std::string & mgrs_code) { GPSPoint gps{0., 0., mgrs_point.z()}; BasicPoint3d utm_point{0., 0., gps.ele}; @@ -126,5 +123,4 @@ void MGRSProjector::setMGRSCode(const GPSPoint & gps, const int precision) setMGRSCode(mgrs_code); } -} // namespace projection -} // namespace lanelet +} // namespace lanelet::projection diff --git a/tmp/lanelet2_extension/lib/no_stopping_area.cpp b/tmp/lanelet2_extension/lib/no_stopping_area.cpp index 517da9fb..95a96b1f 100644 --- a/tmp/lanelet2_extension/lib/no_stopping_area.cpp +++ b/tmp/lanelet2_extension/lib/no_stopping_area.cpp @@ -23,9 +23,7 @@ #include #include -namespace lanelet -{ -namespace autoware +namespace lanelet::autoware { namespace { @@ -149,9 +147,4 @@ void NoStoppingArea::setStopLine(const LineString3d & stopLine) void NoStoppingArea::removeStopLine() { parameters()[RoleName::RefLine] = {}; } -#if __cplusplus < 201703L -constexpr char NoStoppingArea::RuleName[]; // instantiate string in cpp file -#endif - -} // namespace autoware -} // namespace lanelet +} // namespace lanelet::autoware diff --git a/tmp/lanelet2_extension/lib/query.cpp b/tmp/lanelet2_extension/lib/query.cpp index f4b95fcf..1d8ab3a7 100644 --- a/tmp/lanelet2_extension/lib/query.cpp +++ b/tmp/lanelet2_extension/lib/query.cpp @@ -40,9 +40,7 @@ using lanelet::utils::to2D; -namespace lanelet -{ -namespace utils +namespace lanelet::utils { // returns all lanelets in laneletLayer - don't know how to convert // PrimitiveLayer -> std::vector @@ -54,23 +52,21 @@ lanelet::ConstLanelets query::laneletLayer(const lanelet::LaneletMapConstPtr & l return lanelets; } - for (auto li = ll_map->laneletLayer.begin(); li != ll_map->laneletLayer.end(); li++) { - lanelets.push_back(*li); + for (const auto & li : ll_map->laneletLayer) { + lanelets.push_back(li); } return lanelets; } lanelet::ConstLanelets query::subtypeLanelets( - const lanelet::ConstLanelets lls, const char subtype[]) + const lanelet::ConstLanelets & lls, const char subtype[]) { lanelet::ConstLanelets subtype_lanelets; - for (auto li = lls.begin(); li != lls.end(); li++) { - lanelet::ConstLanelet ll = *li; - + for (const auto & ll : lls) { if (ll.hasAttribute(lanelet::AttributeName::Subtype)) { - lanelet::Attribute attr = ll.attribute(lanelet::AttributeName::Subtype); + const lanelet::Attribute & attr = ll.attribute(lanelet::AttributeName::Subtype); if (attr.value() == subtype) { subtype_lanelets.push_back(ll); } @@ -80,43 +76,41 @@ lanelet::ConstLanelets query::subtypeLanelets( return subtype_lanelets; } -lanelet::ConstLanelets query::crosswalkLanelets(const lanelet::ConstLanelets lls) +lanelet::ConstLanelets query::crosswalkLanelets(const lanelet::ConstLanelets & lls) { return query::subtypeLanelets(lls, lanelet::AttributeValueString::Crosswalk); } -lanelet::ConstLanelets query::walkwayLanelets(const lanelet::ConstLanelets lls) +lanelet::ConstLanelets query::walkwayLanelets(const lanelet::ConstLanelets & lls) { return query::subtypeLanelets(lls, lanelet::AttributeValueString::Walkway); } -lanelet::ConstLanelets query::roadLanelets(const lanelet::ConstLanelets lls) +lanelet::ConstLanelets query::roadLanelets(const lanelet::ConstLanelets & lls) { return query::subtypeLanelets(lls, lanelet::AttributeValueString::Road); } -lanelet::ConstLanelets query::shoulderLanelets(const lanelet::ConstLanelets lls) +lanelet::ConstLanelets query::shoulderLanelets(const lanelet::ConstLanelets & lls) { return query::subtypeLanelets(lls, "road_shoulder"); } std::vector query::trafficLights( - const lanelet::ConstLanelets lanelets) + const lanelet::ConstLanelets & lanelets) { std::vector tl_reg_elems; - for (auto i = lanelets.begin(); i != lanelets.end(); i++) { - lanelet::ConstLanelet ll = *i; + for (const auto & ll : lanelets) { std::vector ll_tl_re = ll.regulatoryElementsAs(); // insert unique tl into array - for (auto tli = ll_tl_re.begin(); tli != ll_tl_re.end(); tli++) { - lanelet::TrafficLightConstPtr tl_ptr = *tli; + for (const auto & tl_ptr : ll_tl_re) { lanelet::Id id = tl_ptr->id(); bool unique_id = true; - for (auto ii = tl_reg_elems.begin(); ii != tl_reg_elems.end(); ii++) { - if (id == (*ii)->id()) { + for (const auto & tl_reg_elem : tl_reg_elems) { + if (id == tl_reg_elem->id()) { unique_id = false; break; } @@ -130,23 +124,21 @@ std::vector query::trafficLights( } std::vector query::autowareTrafficLights( - const lanelet::ConstLanelets lanelets) + const lanelet::ConstLanelets & lanelets) { std::vector tl_reg_elems; - for (auto i = lanelets.begin(); i != lanelets.end(); i++) { - lanelet::ConstLanelet ll = *i; + for (const auto & ll : lanelets) { std::vector ll_tl_re = ll.regulatoryElementsAs(); // insert unique tl into array - for (auto tli = ll_tl_re.begin(); tli != ll_tl_re.end(); tli++) { - lanelet::AutowareTrafficLightConstPtr tl_ptr = *tli; + for (const auto & tl_ptr : ll_tl_re) { lanelet::Id id = tl_ptr->id(); bool unique_id = true; - for (auto ii = tl_reg_elems.begin(); ii != tl_reg_elems.end(); ii++) { - if (id == (*ii)->id()) { + for (const auto & tl_reg_elem : tl_reg_elems) { + if (id == tl_reg_elem->id()) { unique_id = false; break; } @@ -165,8 +157,7 @@ std::vector query::detectionAreas( { std::vector da_reg_elems; - for (auto i = lanelets.begin(); i != lanelets.end(); i++) { - lanelet::ConstLanelet ll = *i; + for (const auto & ll : lanelets) { std::vector ll_da_re = ll.regulatoryElementsAs(); @@ -175,8 +166,8 @@ std::vector query::detectionAreas( lanelet::Id id = da_ptr->id(); bool unique_id = true; - for (auto ii = da_reg_elems.begin(); ii != da_reg_elems.end(); ii++) { - if (id == (*ii)->id()) { + for (const auto & da_reg_elem : da_reg_elems) { + if (id == da_reg_elem->id()) { unique_id = false; break; } @@ -195,8 +186,7 @@ std::vector query::noStoppingAreas( { std::vector no_reg_elems; - for (auto i = lanelets.begin(); i != lanelets.end(); i++) { - lanelet::ConstLanelet ll = *i; + for (const auto & ll : lanelets) { std::vector ll_no_re = ll.regulatoryElementsAs(); @@ -205,8 +195,8 @@ std::vector query::noStoppingAreas( lanelet::Id id = no_ptr->id(); bool unique_id = true; - for (auto ii = no_reg_elems.begin(); ii != no_reg_elems.end(); ii++) { - if (id == (*ii)->id()) { + for (const auto & no_reg_elem : no_reg_elems) { + if (id == no_reg_elem->id()) { unique_id = false; break; } @@ -226,7 +216,7 @@ lanelet::ConstPolygons3d query::getAllObstaclePolygons( lanelet::ConstPolygons3d obstacle_polygons; for (const auto & poly : lanelet_map_ptr->polygonLayer) { const std::string type = poly.attributeOr(lanelet::AttributeName::Type, "none"); - if (type.compare("obstacle") == 0) { + if (type == "obstacle") { obstacle_polygons.push_back(poly); } } @@ -239,7 +229,7 @@ lanelet::ConstPolygons3d query::getAllParkingLots( lanelet::ConstPolygons3d parking_lots; for (const auto & poly : lanelet_map_ptr->polygonLayer) { const std::string type = poly.attributeOr(lanelet::AttributeName::Type, "none"); - if (type.compare("parking_lot") == 0) { + if (type == "parking_lot") { parking_lots.push_back(poly); } } @@ -252,8 +242,7 @@ lanelet::ConstLineStrings3d query::getAllPartitions( lanelet::ConstLineStrings3d partitions; for (const auto & ls : lanelet_map_ptr->lineStringLayer) { const std::string type = ls.attributeOr(lanelet::AttributeName::Type, "none"); - if ( - type.compare("guard_rail") == 0 || type.compare("fence") == 0 || type.compare("wall") == 0) { + if (type == "guard_rail" || type == "fence" || type == "wall") { partitions.push_back(ls); } } @@ -266,7 +255,7 @@ lanelet::ConstLineStrings3d query::getAllPedestrianMarkings( lanelet::ConstLineStrings3d pedestrian_markings; for (const auto & ls : lanelet_map_ptr->lineStringLayer) { const std::string type = ls.attributeOr(lanelet::AttributeName::Type, "none"); - if (type.compare("pedestrian_marking") == 0) { + if (type == "pedestrian_marking") { pedestrian_markings.push_back(ls); } } @@ -279,7 +268,7 @@ lanelet::ConstLineStrings3d query::getAllParkingSpaces( lanelet::ConstLineStrings3d parking_spaces; for (const auto & ls : lanelet_map_ptr->lineStringLayer) { const std::string type = ls.attributeOr(lanelet::AttributeName::Type, "none"); - if (type.compare("parking_space") == 0) { + if (type == "parking_space") { parking_spaces.push_back(ls); } } @@ -504,13 +493,13 @@ lanelet::ConstLineStrings3d query::getLinkedParkingSpaces( // return all stop lines and ref lines from a given set of lanelets std::vector query::stopLinesLanelets( - const lanelet::ConstLanelets lanelets) + const lanelet::ConstLanelets & lanelets) { std::vector stoplines; - for (auto lli = lanelets.begin(); lli != lanelets.end(); lli++) { + for (const auto & ll : lanelets) { std::vector ll_stoplines; - ll_stoplines = query::stopLinesLanelet(*lli); + ll_stoplines = query::stopLinesLanelet(ll); stoplines.insert(stoplines.end(), ll_stoplines.begin(), ll_stoplines.end()); } @@ -518,7 +507,7 @@ std::vector query::stopLinesLanelets( } // return all stop and ref lines from a given lanelet -std::vector query::stopLinesLanelet(const lanelet::ConstLanelet ll) +std::vector query::stopLinesLanelet(const lanelet::ConstLanelet & ll) { std::vector stoplines; @@ -526,7 +515,7 @@ std::vector query::stopLinesLanelet(const lanelet::C std::vector> right_of_way_reg_elems = ll.regulatoryElementsAs(); - if (right_of_way_reg_elems.size() > 0) { + if (!right_of_way_reg_elems.empty()) { // lanelet has a right of way elem element for (auto j = right_of_way_reg_elems.begin(); j < right_of_way_reg_elems.end(); j++) { if ((*j)->getManeuver(ll) == lanelet::ManeuverType::Yield) { @@ -543,7 +532,7 @@ std::vector query::stopLinesLanelet(const lanelet::C std::vector> traffic_light_reg_elems = ll.regulatoryElementsAs(); - if (traffic_light_reg_elems.size() > 0) { + if (!traffic_light_reg_elems.empty()) { // lanelet has a traffic light elem element for (auto j = traffic_light_reg_elems.begin(); j < traffic_light_reg_elems.end(); j++) { lanelet::Optional traffic_light_stopline_opt = (*j)->stopLine(); @@ -556,12 +545,12 @@ std::vector query::stopLinesLanelet(const lanelet::C std::vector> traffic_sign_reg_elems = ll.regulatoryElementsAs(); - if (traffic_sign_reg_elems.size() > 0) { + if (!traffic_sign_reg_elems.empty()) { // lanelet has a traffic sign reg elem - can have multiple ref lines (but // stop sign shod have 1 for (auto j = traffic_sign_reg_elems.begin(); j < traffic_sign_reg_elems.end(); j++) { lanelet::ConstLineStrings3d traffic_sign_stoplines = (*j)->refLines(); - if (traffic_sign_stoplines.size() > 0) { + if (!traffic_sign_stoplines.empty()) { stoplines.push_back(traffic_sign_stoplines.front()); } } @@ -570,7 +559,7 @@ std::vector query::stopLinesLanelet(const lanelet::C } std::vector query::stopSignStopLines( - const lanelet::ConstLanelets lanelets, const std::string & stop_sign_id) + const lanelet::ConstLanelets & lanelets, const std::string & stop_sign_id) { std::vector stoplines; @@ -581,7 +570,7 @@ std::vector query::stopSignStopLines( std::vector> traffic_sign_reg_elems = ll.regulatoryElementsAs(); - if (traffic_sign_reg_elems.size() > 0) { + if (!traffic_sign_reg_elems.empty()) { // lanelet has a traffic sign reg elem - can have multiple ref lines (but // stop sign shod have 1 for (const auto & ts : traffic_sign_reg_elems) { @@ -593,7 +582,7 @@ std::vector query::stopSignStopLines( lanelet::ConstLineStrings3d traffic_sign_stoplines = ts->refLines(); // only add new items - if (traffic_sign_stoplines.size() > 0) { + if (!traffic_sign_stoplines.empty()) { auto id = traffic_sign_stoplines.front().id(); if (checklist.find(id) == checklist.end()) { checklist.insert(id); @@ -888,5 +877,4 @@ std::vector query::getPrecedingLaneletSequences( return lanelet_sequences_vec; } -} // namespace utils -} // namespace lanelet +} // namespace lanelet::utils diff --git a/tmp/lanelet2_extension/lib/road_marking.cpp b/tmp/lanelet2_extension/lib/road_marking.cpp index a63d74d2..eadf2cc2 100644 --- a/tmp/lanelet2_extension/lib/road_marking.cpp +++ b/tmp/lanelet2_extension/lib/road_marking.cpp @@ -23,9 +23,7 @@ #include #include -namespace lanelet -{ -namespace autoware +namespace lanelet::autoware { namespace { @@ -72,9 +70,4 @@ void RoadMarking::setRoadMarking(const LineString3d & road_marking) void RoadMarking::removeRoadMarking() { parameters()[RoleName::Refers] = {}; } -#if __cplusplus < 201703L -constexpr char RoadMarking::RuleName[]; // instantiate string in cpp file -#endif - -} // namespace autoware -} // namespace lanelet +} // namespace lanelet::autoware diff --git a/tmp/lanelet2_extension/lib/route_checker.cpp b/tmp/lanelet2_extension/lib/route_checker.cpp index c86fae96..fc5889ab 100644 --- a/tmp/lanelet2_extension/lib/route_checker.cpp +++ b/tmp/lanelet2_extension/lib/route_checker.cpp @@ -16,11 +16,10 @@ #include -namespace lanelet +namespace lanelet::utils { -namespace utils -{ -bool route::isRouteValid(const HADMapRoute route_msg, const lanelet::LaneletMapPtr lanelet_map_ptr_) +bool route::isRouteValid( + const HADMapRoute & route_msg, const lanelet::LaneletMapPtr lanelet_map_ptr_) { for (const auto & route_section : route_msg.segments) { for (const auto & primitive : route_section.primitives) { @@ -40,5 +39,4 @@ bool route::isRouteValid(const HADMapRoute route_msg, const lanelet::LaneletMapP return true; } -} // namespace utils -} // namespace lanelet +} // namespace lanelet::utils diff --git a/tmp/lanelet2_extension/lib/utilities.cpp b/tmp/lanelet2_extension/lib/utilities.cpp index 6a274488..ff9bccb7 100644 --- a/tmp/lanelet2_extension/lib/utilities.cpp +++ b/tmp/lanelet2_extension/lib/utilities.cpp @@ -31,9 +31,7 @@ #include #include -namespace lanelet -{ -namespace utils +namespace lanelet::utils { namespace { @@ -55,7 +53,7 @@ namespace [[maybe_unused]] void getContactingLanelets( const lanelet::LaneletMapPtr lanelet_map, const lanelet::traffic_rules::TrafficRulesPtr traffic_rules, - const lanelet::BasicPoint2d search_point, std::vector * contacting_lanelet_ids) + const lanelet::BasicPoint2d & search_point, std::vector * contacting_lanelet_ids) { if (!lanelet_map) { std::cerr << "No lanelet map is set!" << std::endl; @@ -162,7 +160,7 @@ std::vector resamplePoints( back_point + (direction_vector * (target_length - back_length) / segment_length); // Add to list - resampled_points.push_back(target_point); + resampled_points.emplace_back(target_point); } return resampled_points; @@ -222,9 +220,11 @@ lanelet::LineString3d getLineStringFromArcLength( return lanelet::LineString3d(lanelet::InvalId, points); } -lanelet::ConstLanelet combineLanelets(const lanelet::ConstLanelets lanelets) +lanelet::ConstLanelet combineLanelets(const lanelet::ConstLanelets & lanelets) { - lanelet::Points3d lefts, rights, centers; + lanelet::Points3d lefts; + lanelet::Points3d rights; + lanelet::Points3d centers; for (const auto & llt : lanelets) { for (const auto & pt : llt.leftBound()) { lefts.push_back(lanelet::Point3d(pt)); @@ -679,11 +679,7 @@ bool isInLanelet( { constexpr double eps = 1.0e-9; const lanelet::BasicPoint2d p(current_pose.position.x, current_pose.position.y); - if (boost::geometry::distance(p, lanelet.polygon2d().basicPolygon()) < radius + eps) { - return true; - } - return false; + return boost::geometry::distance(p, lanelet.polygon2d().basicPolygon()) < radius + eps; } -} // namespace utils -} // namespace lanelet +} // namespace lanelet::utils diff --git a/tmp/lanelet2_extension/lib/virtual_traffic_light.cpp b/tmp/lanelet2_extension/lib/virtual_traffic_light.cpp index 9d7fdc6f..60dfebed 100644 --- a/tmp/lanelet2_extension/lib/virtual_traffic_light.cpp +++ b/tmp/lanelet2_extension/lib/virtual_traffic_light.cpp @@ -23,9 +23,7 @@ #include #include -namespace lanelet -{ -namespace autoware +namespace lanelet::autoware { namespace { @@ -60,9 +58,4 @@ VirtualTrafficLight::VirtualTrafficLight( { } -#if __cplusplus < 201703L -constexpr char VirtualTrafficLight::RuleName[]; // instantiate string in cpp file -#endif - -} // namespace autoware -} // namespace lanelet +} // namespace lanelet::autoware diff --git a/tmp/lanelet2_extension/lib/visualization.cpp b/tmp/lanelet2_extension/lib/visualization.cpp index c1feaa40..2e8e65bc 100644 --- a/tmp/lanelet2_extension/lib/visualization.cpp +++ b/tmp/lanelet2_extension/lib/visualization.cpp @@ -39,7 +39,7 @@ bool exists(const std::unordered_set & set, const T & element) } void adjacentPoints( - const int i, const int N, const geometry_msgs::msg::Polygon poly, + const int i, const int N, const geometry_msgs::msg::Polygon & poly, geometry_msgs::msg::Point32 * p0, geometry_msgs::msg::Point32 * p1, geometry_msgs::msg::Point32 * p2) { @@ -69,26 +69,20 @@ void adjacentPoints( } bool isAttributeValue( - const lanelet::ConstPoint3d p, const std::string attr_str, const std::string value_str) + const lanelet::ConstPoint3d & p, const std::string & attr_str, const std::string & value_str) { - lanelet::Attribute attr = p.attribute(attr_str); - if (attr.value().compare(value_str) == 0) { - return true; - } - return false; + const lanelet::Attribute & attr = p.attribute(attr_str); + return attr.value() == value_str; } bool isLaneletAttributeValue( - const lanelet::ConstLanelet ll, const std::string attr_str, const std::string value_str) + const lanelet::ConstLanelet & ll, const std::string & attr_str, const std::string & value_str) { - lanelet::Attribute attr = ll.attribute(attr_str); - if (attr.value().compare(value_str) == 0) { - return true; - } - return false; + const lanelet::Attribute & attr = ll.attribute(attr_str); + return attr.value() == value_str; } -void initLightMarker(visualization_msgs::msg::Marker * marker, const std::string ns) +void initLightMarker(visualization_msgs::msg::Marker * marker, const std::string & ns) { if (marker == nullptr) { std::cerr << __FUNCTION__ << ": marker is null pointer!" << std::endl; @@ -109,7 +103,7 @@ void initLightMarker(visualization_msgs::msg::Marker * marker, const std::string marker->scale.z = s; } -bool inputLightMarker(visualization_msgs::msg::Marker * marker, lanelet::ConstPoint3d p) +bool inputLightMarker(visualization_msgs::msg::Marker * marker, const lanelet::ConstPoint3d & p) { if (marker == nullptr) { std::cerr << __FUNCTION__ << ": marker is null pointer!" << std::endl; @@ -149,7 +143,7 @@ bool inputLightMarker(visualization_msgs::msg::Marker * marker, lanelet::ConstPo return true; } -void initLaneletDirectionMarker(visualization_msgs::msg::Marker * marker, const std::string ns) +void initLaneletDirectionMarker(visualization_msgs::msg::Marker * marker, const std::string & ns) { if (marker == nullptr) { std::cerr << __FUNCTION__ << ": marker is null pointer!" << std::endl; @@ -183,7 +177,7 @@ void initLaneletDirectionMarker(visualization_msgs::msg::Marker * marker, const } void pushLaneletDirectionMarker( - visualization_msgs::msg::Marker * marker, const lanelet::ConstLanelet ll) + visualization_msgs::msg::Marker * marker, const lanelet::ConstLanelet & ll) { if (marker == nullptr) { std::cerr << __FUNCTION__ << ": marker is null pointer!" << std::endl; @@ -201,7 +195,8 @@ void pushLaneletDirectionMarker( pt[2].y() = 0.0; pt[2].z() = 0; - lanelet::BasicPoint3d pc, pc2; + lanelet::BasicPoint3d pc; + lanelet::BasicPoint3d pc2; lanelet::ConstLineString3d center_ls = ll.centerline(); lanelet::Attribute attr = ll.attribute("turn_direction"); @@ -377,7 +372,9 @@ void visualization::polygon2Triangle( std::vector is_acute_angle; is_acute_angle.assign(N, false); for (int i = 0; i < N; i++) { - geometry_msgs::msg::Point32 p0, p1, p2; + geometry_msgs::msg::Point32 p0; + geometry_msgs::msg::Point32 p1; + geometry_msgs::msg::Point32 p2; adjacentPoints(i, N, poly, &p0, &p1, &p2); is_acute_angle.at(i) = isAcuteAngle(p0, p1, p2); @@ -388,9 +385,11 @@ void visualization::polygon2Triangle( int clipped_vertex = -1; for (int i = 0; i < N; i++) { - bool theta = is_acute_angle.at(i); - if (theta == true) { - geometry_msgs::msg::Point32 p0, p1, p2; + const bool theta = is_acute_angle.at(i); + if (theta) { + geometry_msgs::msg::Point32 p0; + geometry_msgs::msg::Point32 p1; + geometry_msgs::msg::Point32 p2; adjacentPoints(i, N, poly, &p0, &p1, &p2); int j_begin = (i + 2) % N; @@ -471,14 +470,13 @@ void visualization::lanelet2Polygon( } visualization_msgs::msg::MarkerArray visualization::laneletDirectionAsMarkerArray( - const lanelet::ConstLanelets lanelets, const std::string & additional_namespace) + const lanelet::ConstLanelets & lanelets, const std::string & additional_namespace) { visualization_msgs::msg::MarkerArray marker_array; visualization_msgs::msg::Marker marker; initLaneletDirectionMarker(&marker, additional_namespace + "lanelet direction"); - for (auto lli = lanelets.begin(); lli != lanelets.end(); lli++) { - lanelet::ConstLanelet ll = *lli; + for (const auto & ll : lanelets) { if (ll.hasAttribute(std::string("turn_direction"))) { pushLaneletDirectionMarker(&marker, ll); } @@ -491,8 +489,8 @@ visualization_msgs::msg::MarkerArray visualization::laneletDirectionAsMarkerArra } visualization_msgs::msg::MarkerArray visualization::autowareTrafficLightsAsMarkerArray( - const std::vector tl_reg_elems, - const std_msgs::msg::ColorRGBA c, const rclcpp::Duration duration, const double scale) + const std::vector & tl_reg_elems, + const std_msgs::msg::ColorRGBA & c, const rclcpp::Duration & duration, const double scale) { visualization_msgs::msg::MarkerArray tl_marker_array; if (tl_reg_elems.empty()) { @@ -503,10 +501,7 @@ visualization_msgs::msg::MarkerArray visualization::autowareTrafficLightsAsMarke initLightMarker(&marker_sph, "traffic_light"); visualization::initTrafficLightTriangleMarker(&marker_tri, "traffic_light_triangle", duration); - for (auto tli = tl_reg_elems.begin(); tli != tl_reg_elems.end(); tli++) { - lanelet::ConstLineStrings3d light_bulbs; - lanelet::AutowareTrafficLightConstPtr tl = *tli; - + for (const auto & tl : tl_reg_elems) { const auto lights = tl->trafficLights(); for (const auto & lsp : lights) { if (lsp.isLineString()) { // traffic lights can either polygons or linestrings @@ -517,10 +512,10 @@ visualization_msgs::msg::MarkerArray visualization::autowareTrafficLightsAsMarke tl_marker_array.markers.push_back(marker_tri); - light_bulbs = tl->lightBulbs(); - for (auto ls : light_bulbs) { + lanelet::ConstLineStrings3d light_bulbs = tl->lightBulbs(); + for (const auto & ls : light_bulbs) { lanelet::ConstLineString3d l = static_cast(ls); - for (auto pt : l) { + for (const auto & pt : l) { if (pt.hasAttribute("color")) { if (inputLightMarker(&marker_sph, pt)) { tl_marker_array.markers.push_back(marker_sph); @@ -534,15 +529,12 @@ visualization_msgs::msg::MarkerArray visualization::autowareTrafficLightsAsMarke } visualization_msgs::msg::MarkerArray visualization::generateTrafficLightIdMaker( - const std::vector tl_reg_elems, - const std_msgs::msg::ColorRGBA c, const rclcpp::Duration duration, const double scale) + const std::vector & tl_reg_elems, + const std_msgs::msg::ColorRGBA & c, const rclcpp::Duration & duration, const double scale) { visualization_msgs::msg::MarkerArray tl_id_marker_array; - for (auto tli = tl_reg_elems.begin(); tli != tl_reg_elems.end(); tli++) { - lanelet::ConstLineStrings3d light_bulbs; - lanelet::AutowareTrafficLightConstPtr tl = *tli; - + for (const auto & tl : tl_reg_elems) { const auto lights = tl->trafficLights(); for (const auto & lsp : lights) { if (lsp.isLineString()) { // traffic lights can either polygons or @@ -554,9 +546,9 @@ visualization_msgs::msg::MarkerArray visualization::generateTrafficLightIdMaker( marker.header.stamp = rclcpp::Time(); marker.ns = "traffic_light_id"; marker.id = ls.id(); - marker.type = marker.TEXT_VIEW_FACING; + marker.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING; marker.lifetime = duration; - marker.action = marker.ADD; + marker.action = visualization_msgs::msg::Marker::ADD; marker.pose.position.x = (ls.front().x() + ls.back().x()) / 2; marker.pose.position.y = (ls.front().y() + ls.back().y()) / 2; marker.pose.position.z = ls.front().z() + 1.0; @@ -578,7 +570,7 @@ visualization_msgs::msg::MarkerArray visualization::generateTrafficLightIdMaker( visualization_msgs::msg::MarkerArray visualization::detectionAreasAsMarkerArray( const std::vector & da_reg_elems, - const std_msgs::msg::ColorRGBA c, const rclcpp::Duration duration) + const std_msgs::msg::ColorRGBA & c, const rclcpp::Duration & duration) { visualization_msgs::msg::MarkerArray marker_array; visualization_msgs::msg::Marker marker; @@ -653,7 +645,7 @@ visualization_msgs::msg::MarkerArray visualization::detectionAreasAsMarkerArray( visualization_msgs::msg::MarkerArray visualization::noStoppingAreasAsMarkerArray( const std::vector & no_reg_elems, - const std_msgs::msg::ColorRGBA c, const rclcpp::Duration duration) + const std_msgs::msg::ColorRGBA & c, const rclcpp::Duration & duration) { visualization_msgs::msg::MarkerArray marker_array; visualization_msgs::msg::Marker marker; @@ -798,7 +790,8 @@ visualization_msgs::msg::MarkerArray visualization::parkingSpacesAsMarkerArray( } visualization_msgs::msg::MarkerArray visualization::generateLaneletIdMarker( - const lanelet::ConstLanelets road_lanelets, const std_msgs::msg::ColorRGBA c, const double scale) + const lanelet::ConstLanelets & road_lanelets, const std_msgs::msg::ColorRGBA & c, + const double scale) { visualization_msgs::msg::MarkerArray markers; for (const auto & ll : road_lanelets) { @@ -807,8 +800,8 @@ visualization_msgs::msg::MarkerArray visualization::generateLaneletIdMarker( marker.header.stamp = rclcpp::Clock().now(); marker.ns = "lanelet_id"; marker.id = ll.id(); - marker.type = marker.TEXT_VIEW_FACING; - marker.action = marker.ADD; + marker.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING; + marker.action = visualization_msgs::msg::Marker::ADD; const auto centerline = ll.centerline(); const size_t target_position_index = centerline.size() / 2; const auto target_position = centerline[target_position_index]; @@ -848,8 +841,8 @@ visualization_msgs::msg::MarkerArray visualization::obstaclePolygonsAsMarkerArra } visualization_msgs::msg::MarkerArray visualization::lineStringsAsMarkerArray( - const std::vector line_strings, const std::string name_space, - const std_msgs::msg::ColorRGBA c, const double lss) + const std::vector & line_strings, const std::string & name_space, + const std_msgs::msg::ColorRGBA & c, const double lss) { visualization_msgs::msg::MarkerArray ls_marker_array; if (line_strings.empty()) { @@ -859,8 +852,7 @@ visualization_msgs::msg::MarkerArray visualization::lineStringsAsMarkerArray( visualization_msgs::msg::Marker ls_marker; visualization::initLineStringMarker(&ls_marker, "map", name_space, c); - for (auto i = line_strings.begin(); i != line_strings.end(); i++) { - const lanelet::ConstLineString3d & ls = *i; + for (const auto & ls : line_strings) { if (!exists(added, ls.id())) { visualization::pushLineStringMarker(&ls_marker, ls, c, lss); added.insert(ls.id()); @@ -871,15 +863,18 @@ visualization_msgs::msg::MarkerArray visualization::lineStringsAsMarkerArray( } visualization_msgs::msg::MarkerArray visualization::laneletsBoundaryAsMarkerArray( - const lanelet::ConstLanelets & lanelets, const std_msgs::msg::ColorRGBA c, + const lanelet::ConstLanelets & lanelets, const std_msgs::msg::ColorRGBA & c, const bool viz_centerline, const std::string & additional_namespace) { - double lss = 0.1; // line string size - double lss_center = std::max(lss * 0.1, 0.02); + const float lss = 0.1; // line string size + const float lss_center = std::max(lss * 0.1, 0.02); std::unordered_set added; - visualization_msgs::msg::Marker left_line_strip, right_line_strip, start_bound_line_strip, - center_line_strip, center_arrows; + visualization_msgs::msg::Marker left_line_strip; + visualization_msgs::msg::Marker right_line_strip; + visualization_msgs::msg::Marker start_bound_line_strip; + visualization_msgs::msg::Marker center_line_strip; + visualization_msgs::msg::Marker center_arrows; visualization::initLineStringMarker( &left_line_strip, "map", additional_namespace + "left_lane_bound", c); visualization::initLineStringMarker( @@ -891,9 +886,7 @@ visualization_msgs::msg::MarkerArray visualization::laneletsBoundaryAsMarkerArra visualization::initArrowsMarker( ¢er_arrows, "map", additional_namespace + "center_line_arrows", c); - for (auto li = lanelets.begin(); li != lanelets.end(); li++) { - lanelet::ConstLanelet lll = *li; - + for (const auto & lll : lanelets) { lanelet::ConstLineString3d left_ls = lll.leftBound(); lanelet::ConstLineString3d right_ls = lll.rightBound(); lanelet::ConstLineString3d center_ls = lll.centerline(); @@ -942,8 +935,8 @@ visualization_msgs::msg::MarkerArray visualization::laneletsBoundaryAsMarkerArra } visualization_msgs::msg::MarkerArray visualization::trafficLightsAsTriangleMarkerArray( - const std::vector tl_reg_elems, const std_msgs::msg::ColorRGBA c, - const rclcpp::Duration duration, const double scale) + const std::vector & tl_reg_elems, + const std_msgs::msg::ColorRGBA & c, const rclcpp::Duration & duration, const double scale) { // convert to to an array of linestrings and publish as marker array using // existing function @@ -952,12 +945,11 @@ visualization_msgs::msg::MarkerArray visualization::trafficLightsAsTriangleMarke visualization_msgs::msg::Marker marker; visualization::initTrafficLightTriangleMarker(&marker, "traffic_light_triangle", duration); - for (auto tli = tl_reg_elems.begin(); tli != tl_reg_elems.end(); tli++) { - lanelet::TrafficLightConstPtr tl = *tli; + for (const auto & tl : tl_reg_elems) { lanelet::LineString3d ls; - auto lights = tl->trafficLights(); - for (auto lsp : lights) { + const auto lights = tl->trafficLights(); + for (const auto & lsp : lights) { if (lsp.isLineString()) { // traffic lights can either polygons or linestrings lanelet::ConstLineString3d ls = static_cast(lsp); visualization::pushTrafficLightTriangleMarker(&marker, ls, c, scale); @@ -971,7 +963,8 @@ visualization_msgs::msg::MarkerArray visualization::trafficLightsAsTriangleMarke } visualization_msgs::msg::MarkerArray visualization::laneletsAsTriangleMarkerArray( - const std::string ns, const lanelet::ConstLanelets & lanelets, const std_msgs::msg::ColorRGBA c) + const std::string & ns, const lanelet::ConstLanelets & lanelets, + const std_msgs::msg::ColorRGBA & c) { visualization_msgs::msg::MarkerArray marker_array; visualization_msgs::msg::Marker marker; @@ -1002,11 +995,11 @@ visualization_msgs::msg::MarkerArray visualization::laneletsAsTriangleMarkerArra marker.color.b = 1.0f; marker.color.a = 0.999; - for (auto ll : lanelets) { + for (const auto & ll : lanelets) { std::vector triangles; lanelet2Triangle(ll, &triangles); - for (auto tri : triangles) { + for (const auto & tri : triangles) { geometry_msgs::msg::Point tri0[3]; for (int i = 0; i < 3; i++) { @@ -1025,7 +1018,8 @@ visualization_msgs::msg::MarkerArray visualization::laneletsAsTriangleMarkerArra } void visualization::initTrafficLightTriangleMarker( - visualization_msgs::msg::Marker * marker, const std::string ns, const rclcpp::Duration duration) + visualization_msgs::msg::Marker * marker, const std::string & ns, + const rclcpp::Duration & duration) { if (marker == nullptr) { std::cerr << __FUNCTION__ << ": marker is null pointer!" << std::endl; @@ -1057,8 +1051,8 @@ void visualization::initTrafficLightTriangleMarker( } void visualization::pushTrafficLightTriangleMarker( - visualization_msgs::msg::Marker * marker, const lanelet::ConstLineString3d ls, - const std_msgs::msg::ColorRGBA cl, const double scale) + visualization_msgs::msg::Marker * marker, const lanelet::ConstLineString3d & ls, + const std_msgs::msg::ColorRGBA & cl, const double scale) { if (marker == nullptr) { std::cerr << __FUNCTION__ << ": marker is null pointer!" << std::endl; @@ -1083,8 +1077,8 @@ void visualization::pushTrafficLightTriangleMarker( Eigen::Vector3d c = (v[0] + v[1] + v[2] + v[3]) / 4; if (scale > 0.0 && scale != 1.0) { - for (int i = 0; i < 4; i++) { - v[i] = (v[i] - c) * scale + c; + for (auto & i : v) { + i = (i - c) * scale + c; } } geometry_msgs::msg::Point tri0[3]; @@ -1096,19 +1090,19 @@ void visualization::pushTrafficLightTriangleMarker( utils::conversion::toGeomMsgPt(v[2], &tri1[1]); utils::conversion::toGeomMsgPt(v[3], &tri1[2]); - for (int i = 0; i < 3; i++) { - marker->points.push_back(tri0[i]); + for (const auto & i : tri0) { + marker->points.push_back(i); marker->colors.push_back(cl); } - for (int i = 0; i < 3; i++) { - marker->points.push_back(tri1[i]); + for (const auto & i : tri0) { + marker->points.push_back(i); marker->colors.push_back(cl); } } void visualization::initLineStringMarker( - visualization_msgs::msg::Marker * marker, const std::string frame_id, const std::string ns, - const std_msgs::msg::ColorRGBA c) + visualization_msgs::msg::Marker * marker, const std::string & frame_id, const std::string & ns, + const std_msgs::msg::ColorRGBA & c) { if (marker == nullptr) { RCLCPP_ERROR_STREAM( @@ -1137,7 +1131,7 @@ void visualization::initLineStringMarker( void visualization::pushLineStringMarker( visualization_msgs::msg::Marker * marker, const lanelet::ConstLineString3d & ls, - const std_msgs::msg::ColorRGBA c, const float lss) + const std_msgs::msg::ColorRGBA & c, const float lss) { if (marker == nullptr) { RCLCPP_ERROR_STREAM( @@ -1190,8 +1184,8 @@ void visualization::pushLineStringMarker( } void visualization::initArrowsMarker( - visualization_msgs::msg::Marker * marker, const std::string frame_id, const std::string ns, - const std_msgs::msg::ColorRGBA c) + visualization_msgs::msg::Marker * marker, const std::string & frame_id, const std::string & ns, + const std_msgs::msg::ColorRGBA & c) { if (marker == nullptr) { RCLCPP_ERROR_STREAM( @@ -1220,7 +1214,7 @@ void visualization::initArrowsMarker( void visualization::pushArrowsMarker( visualization_msgs::msg::Marker * marker, const lanelet::ConstLineString3d & ls, - const std_msgs::msg::ColorRGBA c) + const std_msgs::msg::ColorRGBA & c) { if (marker == nullptr) { RCLCPP_ERROR_STREAM( diff --git a/tmp/lanelet2_extension/package.xml b/tmp/lanelet2_extension/package.xml index 6b194d71..662e9a94 100644 --- a/tmp/lanelet2_extension/package.xml +++ b/tmp/lanelet2_extension/package.xml @@ -13,6 +13,7 @@ autoware_auto_mapping_msgs autoware_auto_planning_msgs + autoware_utils geographiclib geometry_msgs lanelet2_core @@ -26,7 +27,6 @@ rclcpp tf2 tf2_geometry_msgs - autoware_utils visualization_msgs ament_cmake_ros diff --git a/tmp/lanelet2_extension/src/sample_code.cpp b/tmp/lanelet2_extension/src/sample_code.cpp index a8e7aa9a..00fe92cd 100644 --- a/tmp/lanelet2_extension/src/sample_code.cpp +++ b/tmp/lanelet2_extension/src/sample_code.cpp @@ -24,7 +24,7 @@ #include #include -void loadingAutowareOSMFile(const std::string map_file_path) +void loadingAutowareOSMFile(const std::string & map_file_path) { lanelet::LaneletMapPtr lanelet_map; lanelet::ErrorMessages errors; @@ -68,31 +68,32 @@ void usingMGRSProjector() // grid lanelet::GPSPoint projected_gps_point = projector.reverse(mgrs_point); std::cout << projected_gps_point.lat << " " << projected_gps_point.lon << std::endl; - lanelet::GPSPoint projected_gps_point2 = projector.reverse(mgrs_point, mgrs_grid); + lanelet::GPSPoint projected_gps_point2 = + lanelet::projection::MGRSProjector::reverse(mgrs_point, mgrs_grid); std::cout << projected_gps_point2.lat << " " << projected_gps_point2.lon << " " << std::endl; } -void usingAutowareTrafficLight(const std::string map_file_path) +void usingAutowareTrafficLight(const std::string & map_file_path) { lanelet::LaneletMapPtr lanelet_map; lanelet::ErrorMessages errors; lanelet::projection::MGRSProjector projector; lanelet_map = lanelet::load(map_file_path, "autoware_osm_handler", projector, &errors); - for (auto lanelet : lanelet_map->laneletLayer) { + for (const auto & lanelet : lanelet_map->laneletLayer) { // You can access to traffic light element as AutowareTrafficLight class - auto autoware_traffic_lights = + const auto autoware_traffic_lights = lanelet.regulatoryElementsAs(); - for (auto light : autoware_traffic_lights) { + for (const auto & light : autoware_traffic_lights) { // You can access to the position of each lamps(light bulb) in traffic // light - for (auto light_bulb_string : light->lightBulbs()) { + for (const auto & light_bulb_string : light->lightBulbs()) { std::cout << light_bulb_string.id() << std::endl; } // Since AutowareTrafficLight class is inheriting lanelet::TrafficLight // class, you can also access to outline of traffic light by the same // method. - for (auto light_string : light->trafficLights()) { + for (const auto & light_string : light->trafficLights()) { std::cout << light_string.id() << std::endl; } } @@ -100,8 +101,8 @@ void usingAutowareTrafficLight(const std::string map_file_path) // You can also access to same traffic light element as default TrafficLight // class auto traffic_lights = lanelet.regulatoryElementsAs(); - for (auto light : traffic_lights) { - for (auto light_string : light->trafficLights()) { + for (const auto & light : traffic_lights) { + for (const auto & light_string : light->trafficLights()) { std::cout << light_string.id() << std::endl; } } diff --git a/tmp/lanelet2_extension/src/validation.cpp b/tmp/lanelet2_extension/src/validation.cpp index fdaadb66..565775f2 100644 --- a/tmp/lanelet2_extension/src/validation.cpp +++ b/tmp/lanelet2_extension/src/validation.cpp @@ -51,7 +51,7 @@ void printUsage() } } // namespace -void validateElevationTag(const std::string filename) +void validateElevationTag(const std::string & filename) { pugi::xml_document doc; auto result = doc.load_file(filename.c_str()); @@ -61,8 +61,7 @@ void validateElevationTag(const std::string filename) } auto osmNode = doc.child(keyword::Osm); - for (auto node = osmNode.child(keyword::Node); node; // NOLINT - node = node.next_sibling(keyword::Node)) { + for (auto node = osmNode.child(keyword::Node); node; node = node.next_sibling(keyword::Node)) { const auto id = node.attribute(keyword::Id).as_llong(lanelet::InvalId); if (!node.find_child_by_attribute(keyword::Tag, keyword::Key, keyword::Elevation)) { std::cerr << "failed to find elevation tag for node: " << id << std::endl; @@ -77,33 +76,33 @@ void validateTrafficLight(const lanelet::LaneletMapPtr lanelet_map) exit(1); } - for (auto lanelet : lanelet_map->laneletLayer) { + for (const auto & lanelet : lanelet_map->laneletLayer) { auto autoware_traffic_lights = lanelet.regulatoryElementsAs(); if (autoware_traffic_lights.empty()) { continue; } - for (auto light : autoware_traffic_lights) { - if (light->lightBulbs().size() == 0) { + for (const auto & light : autoware_traffic_lights) { + if (light->lightBulbs().empty()) { std::cerr << "regulatory element traffic light " << light->id() << " is missing optional light_bulb member. You won't be able to use region_tlr " "node with this map" << std::endl; } - for (auto light_string : light->lightBulbs()) { + for (const auto & light_string : light->lightBulbs()) { if (!light_string.hasAttribute("traffic_light_id")) { std::cerr << "light_bulb " << light_string.id() << " is missing traffic_light_id tag" << std::endl; } } - for (auto base_string_or_poly : light->trafficLights()) { + for (const auto & base_string_or_poly : light->trafficLights()) { if (!base_string_or_poly.isLineString()) { std::cerr << "traffic_light " << base_string_or_poly.id() << " is polygon, and only linestring class is currently supported for traffic lights" << std::endl; } - auto base_string = static_cast(base_string_or_poly); + const auto base_string = static_cast(base_string_or_poly); if (!base_string.hasAttribute("height")) { std::cerr << "traffic_light " << base_string.id() << " is missing height tag" << std::endl; @@ -132,7 +131,7 @@ void validateTurnDirection(const lanelet::LaneletMapPtr lanelet_map) } const auto conflicting_lanelets_or_areas = vehicle_graph->conflicting(lanelet); - if (conflicting_lanelets_or_areas.size() == 0) { + if (conflicting_lanelets_or_areas.empty()) { continue; } if (!lanelet.hasAttribute("turn_direction")) { diff --git a/tmp/lanelet2_extension/test/src/test_message_conversion.cpp b/tmp/lanelet2_extension/test/src/test_message_conversion.cpp index 745f98c9..15910219 100644 --- a/tmp/lanelet2_extension/test/src/test_message_conversion.cpp +++ b/tmp/lanelet2_extension/test/src/test_message_conversion.cpp @@ -15,7 +15,8 @@ #include "lanelet2_extension/utility/query.hpp" #include -#include + +#include using lanelet::Lanelet; using lanelet::LineString3d; @@ -23,12 +24,18 @@ using lanelet::Point3d; using lanelet::utils::getId; using lanelet::utils::conversion::toGeomMsgPt; -class TestSuite : public ::testing::Test +class TestSuite : public ::testing::Test // NOLINT for gtest { public: TestSuite() : single_lanelet_map_ptr(new lanelet::LaneletMap()) { - Point3d p1, p2, p3, p4, p5, p6, p7; + Point3d p1; + Point3d p2; + Point3d p3; + Point3d p4; + Point3d p5; + Point3d p6; + Point3d p7; LineString3d traffic_light_base, traffic_light_bulbs, stop_line; p1 = Point3d(getId(), 0., 0., 0.); @@ -37,20 +44,22 @@ class TestSuite : public ::testing::Test p3 = Point3d(getId(), 1., 0., 0.); p4 = Point3d(getId(), 1., 1., 0.); - LineString3d ls_left(getId(), {p1, p2}); // NOLINT - LineString3d ls_right(getId(), {p3, p4}); // NOLINT + LineString3d ls_left(getId(), {p1, p2}); + LineString3d ls_right(getId(), {p3, p4}); Lanelet lanelet(getId(), ls_left, ls_right); single_lanelet_map_ptr->add(lanelet); } - ~TestSuite() {} + + ~TestSuite() override = default; + lanelet::LaneletMapPtr single_lanelet_map_ptr; private: }; -TEST_F(TestSuite, BinMsgConversion) +TEST_F(TestSuite, BinMsgConversion) // NOLINT for gtest { autoware_auto_mapping_msgs::msg::HADMapBin bin_msg; lanelet::LaneletMapPtr regenerated_map(new lanelet::LaneletMap); @@ -68,7 +77,7 @@ TEST_F(TestSuite, BinMsgConversion) << "regenerated map has different id"; } -TEST_F(TestSuite, ToGeomMsgPt) +TEST_F(TestSuite, ToGeomMsgPt) // NOLINT for gtest { Point3d lanelet_pt(getId(), -0.1, 0.2, 3.0); diff --git a/tmp/lanelet2_extension/test/src/test_projector.cpp b/tmp/lanelet2_extension/test/src/test_projector.cpp index 65304df2..81cc5d5c 100644 --- a/tmp/lanelet2_extension/test/src/test_projector.cpp +++ b/tmp/lanelet2_extension/test/src/test_projector.cpp @@ -15,16 +15,17 @@ #include "lanelet2_extension/projection/mgrs_projector.hpp" #include -#include -class TestSuite : public ::testing::Test +#include + +class TestSuite : public ::testing::Test // NOLINT for gtest { public: - TestSuite() {} - ~TestSuite() {} + TestSuite() = default; + ~TestSuite() override = default; }; -TEST(TestSuite, ForwardProjection) +TEST(TestSuite, ForwardProjection) // NOLINT for gtest { lanelet::projection::MGRSProjector projector; // lat/lon in Tokyo @@ -49,7 +50,7 @@ TEST(TestSuite, ForwardProjection) ASSERT_DOUBLE_EQ(rounded_y_mm, 46063.748) << "Forward projected y value should be " << 46063.748; } -TEST(TestSuite, ReverseProjection) +TEST(TestSuite, ReverseProjection) // NOLINT for gtest { lanelet::projection::MGRSProjector projector; lanelet::BasicPoint3d mgrs_point; diff --git a/tmp/lanelet2_extension/test/src/test_query.cpp b/tmp/lanelet2_extension/test/src/test_query.cpp index ef65772d..b2bfa541 100644 --- a/tmp/lanelet2_extension/test/src/test_query.cpp +++ b/tmp/lanelet2_extension/test/src/test_query.cpp @@ -15,22 +15,25 @@ #include "lanelet2_extension/utility/query.hpp" #include -#include + +#include using lanelet::Lanelet; using lanelet::LineString3d; -using lanelet::LineStringOrPolygon3d; using lanelet::Point3d; using lanelet::Points3d; using lanelet::utils::getId; -class TestSuite : public ::testing::Test +class TestSuite : public ::testing::Test // NOLINT for gtest { public: TestSuite() : sample_map_ptr(new lanelet::LaneletMap()) - { // NOLINT + { // create sample lanelets - Point3d p1, p2, p3, p4; + Point3d p1; + Point3d p2; + Point3d p3; + Point3d p4; p1 = Point3d(getId(), 0., 0., 0.); p2 = Point3d(getId(), 0., 1., 0.); @@ -38,8 +41,8 @@ class TestSuite : public ::testing::Test p3 = Point3d(getId(), 1., 0., 0.); p4 = Point3d(getId(), 1., 1., 0.); - LineString3d ls_left(getId(), {p1, p2}); // NOLINT - LineString3d ls_right(getId(), {p3, p4}); // NOLINT + LineString3d ls_left(getId(), {p1, p2}); + LineString3d ls_right(getId(), {p3, p4}); Lanelet road_lanelet(getId(), ls_left, ls_right); road_lanelet.attributes()[lanelet::AttributeName::Subtype] = @@ -63,13 +66,12 @@ class TestSuite : public ::testing::Test p11 = Point3d(getId(), 0., 0., 0.); p12 = Point3d(getId(), 1., 0., 0.); - traffic_light_base = LineString3d(getId(), Points3d{p6, p7}); // NOLINT - traffic_light_bulbs = LineString3d(getId(), Points3d{p8, p9, p10}); // NOLINT - stop_line = LineString3d(getId(), Points3d{p11, p12}); // NOLINT + traffic_light_base = LineString3d(getId(), Points3d{p6, p7}); + traffic_light_bulbs = LineString3d(getId(), Points3d{p8, p9, p10}); + stop_line = LineString3d(getId(), Points3d{p11, p12}); auto tl = lanelet::autoware::AutowareTrafficLight::make( - getId(), lanelet::AttributeMap(), {traffic_light_base}, stop_line, - {traffic_light_bulbs}); // NOLINT + getId(), lanelet::AttributeMap(), {traffic_light_base}, stop_line, {traffic_light_bulbs}); road_lanelet.addRegulatoryElement(tl); @@ -77,14 +79,15 @@ class TestSuite : public ::testing::Test sample_map_ptr->add(road_lanelet); sample_map_ptr->add(crosswalk_lanelet); } - ~TestSuite() {} + + ~TestSuite() override = default; lanelet::LaneletMapPtr sample_map_ptr; private: }; -TEST_F(TestSuite, QueryLanelets) +TEST_F(TestSuite, QueryLanelets) // NOLINT for gtest { lanelet::ConstLanelets all_lanelets = lanelet::utils::query::laneletLayer(sample_map_ptr); ASSERT_EQ(2U, all_lanelets.size()) << "failed to retrieve all lanelets"; @@ -101,7 +104,7 @@ TEST_F(TestSuite, QueryLanelets) ASSERT_EQ(1U, crosswalk_lanelets.size()) << "failed to retrieve crosswalk lanelets"; } -TEST_F(TestSuite, QueryTrafficLights) +TEST_F(TestSuite, QueryTrafficLights) // NOLINT for gtest { lanelet::ConstLanelets all_lanelets = lanelet::utils::query::laneletLayer(sample_map_ptr); @@ -112,7 +115,7 @@ TEST_F(TestSuite, QueryTrafficLights) ASSERT_EQ(1U, autoware_traffic_lights.size()) << "failed to retrieve autoware traffic lights"; } -TEST_F(TestSuite, QueryStopLine) +TEST_F(TestSuite, QueryStopLine) // NOLINT for gtest { lanelet::ConstLanelets all_lanelets = lanelet::utils::query::laneletLayer(sample_map_ptr); lanelet::ConstLanelets road_lanelets = lanelet::utils::query::roadLanelets(all_lanelets); diff --git a/tmp/lanelet2_extension/test/src/test_regulatory_elements.cpp b/tmp/lanelet2_extension/test/src/test_regulatory_elements.cpp index 5512d7e0..5c8e88d4 100644 --- a/tmp/lanelet2_extension/test/src/test_regulatory_elements.cpp +++ b/tmp/lanelet2_extension/test/src/test_regulatory_elements.cpp @@ -17,8 +17,8 @@ #include #include #include -#include +#include #include using lanelet::LineString3d; @@ -37,16 +37,22 @@ std::vector convertToVector(T item) } } // namespace -class TestSuite : public ::testing::Test +class TestSuite : public ::testing::Test // NOLINT for gtest { public: - TestSuite() {} - ~TestSuite() {} + TestSuite() = default; + ~TestSuite() override = default; }; -TEST(TestSuite, FactoryConstructsTrafficLight) +TEST(TestSuite, FactoryConstructsTrafficLight) // NOLINT for gtest { - Point3d p1, p2, p3, p4, p5, p6, p7; + Point3d p1; + Point3d p2; + Point3d p3; + Point3d p4; + Point3d p5; + Point3d p6; + Point3d p7; LineStringOrPolygon3d traffic_light_base; LineString3d traffic_light_bulbs, stop_line; @@ -78,11 +84,21 @@ TEST(TestSuite, FactoryConstructsTrafficLight) EXPECT_TRUE(!!std::dynamic_pointer_cast(factoryTl)); } -TEST(TestSuite, TrafficLightWorksAsExpected) -{ // NOLINT - Point3d p1, p2, p3, p4, p5, p6, p7; - LineStringOrPolygon3d traffic_light_base, traffic_light_base2; - LineString3d traffic_light_bulbs, traffic_light_bulbs2, stop_line; +TEST(TestSuite, TrafficLightWorksAsExpected) // NOLINT for gtest +{ + Point3d p1; + Point3d p2; + Point3d p3; + Point3d p4; + Point3d p5; + Point3d p6; + Point3d p7; + + LineStringOrPolygon3d traffic_light_base; + LineStringOrPolygon3d traffic_light_base2; + LineString3d traffic_light_bulbs; + LineString3d traffic_light_bulbs2; + LineString3d stop_line; p1 = Point3d(getId(), 0., 1., 4.); p2 = Point3d(getId(), 1., 1., 4.); diff --git a/tmp/lanelet2_extension/test/src/test_route_checker.cpp b/tmp/lanelet2_extension/test/src/test_route_checker.cpp index e3dc6bdd..9c83de9d 100644 --- a/tmp/lanelet2_extension/test/src/test_route_checker.cpp +++ b/tmp/lanelet2_extension/test/src/test_route_checker.cpp @@ -25,7 +25,7 @@ using lanelet::LineString3d; using lanelet::Point3d; using lanelet::utils::getId; -class TestSuite : public ::testing::Test +class TestSuite : public ::testing::Test // NOLINT for gtest { public: TestSuite() : sample_map_ptr(new lanelet::LaneletMap()) @@ -59,7 +59,8 @@ class TestSuite : public ::testing::Test sample_route1.segments.push_back(map_segment1); sample_route2.segments.push_back(map_segment2); } - ~TestSuite() {} + + ~TestSuite() override = default; lanelet::LaneletMapPtr sample_map_ptr; autoware_auto_planning_msgs::msg::HADMapRoute sample_route1; // valid route @@ -68,7 +69,7 @@ class TestSuite : public ::testing::Test private: }; -TEST_F(TestSuite, isRouteValid) +TEST_F(TestSuite, isRouteValid) // NOLINT for gtest { autoware_auto_mapping_msgs::msg::HADMapBin bin_msg; diff --git a/tmp/lanelet2_extension/test/src/test_utilities.cpp b/tmp/lanelet2_extension/test/src/test_utilities.cpp index 966b093c..bd28a564 100644 --- a/tmp/lanelet2_extension/test/src/test_utilities.cpp +++ b/tmp/lanelet2_extension/test/src/test_utilities.cpp @@ -25,39 +25,48 @@ using lanelet::LineString3d; using lanelet::Point3d; using lanelet::utils::getId; -class TestSuite : public ::testing::Test +class TestSuite : public ::testing::Test // NOLINT for gtest { public: TestSuite() : sample_map_ptr(new lanelet::LaneletMap()) - { // NOLINT + { // create sample lanelets - Point3d p1, p2, p3, p4, p5, p6, p7, p8, p9, p10; + Point3d p1; + Point3d p2; + Point3d p3; + Point3d p4; + Point3d p5; + Point3d p6; + Point3d p7; + Point3d p8; + Point3d p9; + Point3d p10; p1 = Point3d(getId(), 0., 0., 0.); p2 = Point3d(getId(), 0., 1., 0.); p3 = Point3d(getId(), 1., 0., 0.); p4 = Point3d(getId(), 1., 1., 0.); - LineString3d ls_left(getId(), {p1, p2}); // NOLINT - LineString3d ls_right(getId(), {p3, p4}); // NOLINT + LineString3d ls_left(getId(), {p1, p2}); + LineString3d ls_right(getId(), {p3, p4}); p5 = Point3d(getId(), 0., 2., 0.); p6 = Point3d(getId(), 1., 2., 0.); - LineString3d ls_left2(getId(), {p2, p5}); // NOLINT - LineString3d ls_right2(getId(), {p4, p6}); // NOLINT + LineString3d ls_left2(getId(), {p2, p5}); + LineString3d ls_right2(getId(), {p4, p6}); p7 = Point3d(getId(), 0., 3., 0.); p8 = Point3d(getId(), 1., 3., 0.); - LineString3d ls_left3(getId(), {p5, p7}); // NOLINT - LineString3d ls_right3(getId(), {p6, p8}); // NOLINT + LineString3d ls_left3(getId(), {p5, p7}); + LineString3d ls_right3(getId(), {p6, p8}); p9 = Point3d(getId(), 0., 1., 0.); p10 = Point3d(getId(), 1., 1., 0.); - LineString3d ls_left4(getId(), {p9, p5}); // NOLINT - LineString3d ls_right4(getId(), {p10, p6}); // NOLINT + LineString3d ls_left4(getId(), {p9, p5}); + LineString3d ls_right4(getId(), {p10, p6}); road_lanelet = Lanelet(getId(), ls_left, ls_right); road_lanelet.attributes()[lanelet::AttributeName::Subtype] = @@ -80,7 +89,8 @@ class TestSuite : public ::testing::Test sample_map_ptr->add(next_lanelet2); sample_map_ptr->add(merging_lanelet); } - ~TestSuite() {} + + ~TestSuite() override = default; lanelet::LaneletMapPtr sample_map_ptr; Lanelet road_lanelet; @@ -91,7 +101,7 @@ class TestSuite : public ::testing::Test private: }; -TEST_F(TestSuite, OverwriteLaneletsCenterline) +TEST_F(TestSuite, OverwriteLaneletsCenterline) // NOLINT for gtest { double resolution = 5.0; bool force_overwrite = false; From 678aa3a1337a05c652b410e75ab44c363cefcc54 Mon Sep 17 00:00:00 2001 From: Kenji Miyake Date: Tue, 2 Aug 2022 18:37:34 +0900 Subject: [PATCH 27/36] apply diff Signed-off-by: Kenji Miyake --- .../lanelet2_extension/utility/utilities.hpp | 2 ++ tmp/lanelet2_extension/lib/query.cpp | 2 +- tmp/lanelet2_extension/lib/utilities.cpp | 30 +++++++++++++++++++ tmp/lanelet2_extension/lib/visualization.cpp | 8 ++--- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp index 9e365f16..a004f92a 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp @@ -79,6 +79,8 @@ double getLaneletAngle( bool isInLanelet( const geometry_msgs::msg::Pose & current_pose, const lanelet::ConstLanelet & lanelet, const double radius = 0.0); +geometry_msgs::msg::Pose getClosestCenterPose( + const lanelet::ConstLanelet & lanelet, const geometry_msgs::msg::Point & search_point); } // namespace lanelet::utils diff --git a/tmp/lanelet2_extension/lib/query.cpp b/tmp/lanelet2_extension/lib/query.cpp index 1d8ab3a7..993964d1 100644 --- a/tmp/lanelet2_extension/lib/query.cpp +++ b/tmp/lanelet2_extension/lib/query.cpp @@ -767,7 +767,7 @@ bool query::getCurrentLanelets( lanelet::BasicPoint2d search_point(search_pose.position.x, search_pose.position.y); for (const auto & llt : lanelets) { - if (!lanelet::geometry::inside(llt, search_point)) { + if (lanelet::geometry::inside(llt, search_point)) { current_lanelets_ptr->push_back(llt); } } diff --git a/tmp/lanelet2_extension/lib/utilities.cpp b/tmp/lanelet2_extension/lib/utilities.cpp index ff9bccb7..a62570dc 100644 --- a/tmp/lanelet2_extension/lib/utilities.cpp +++ b/tmp/lanelet2_extension/lib/utilities.cpp @@ -25,6 +25,12 @@ #include #include +#ifdef ROS_DISTRO_GALACTIC +#include +#else +#include +#endif + #include #include #include @@ -682,4 +688,28 @@ bool isInLanelet( return boost::geometry::distance(p, lanelet.polygon2d().basicPolygon()) < radius + eps; } +geometry_msgs::msg::Pose getClosestCenterPose( + const lanelet::ConstLanelet & lanelet, const geometry_msgs::msg::Point & search_point) +{ + lanelet::BasicPoint2d llt_search_point(search_point.x, search_point.y); + lanelet::ConstLineString3d segment = getClosestSegment(llt_search_point, lanelet.centerline()); + + const Eigen::Vector2d direction( + (segment.back().basicPoint2d() - segment.front().basicPoint2d()).normalized()); + const Eigen::Vector2d xf(segment.front().basicPoint2d()); + const Eigen::Vector2d x(search_point.x, search_point.y); + const Eigen::Vector2d p = xf + (x - xf).dot(direction) * direction; + + geometry_msgs::msg::Pose closest_pose; + closest_pose.position.x = p.x(); + closest_pose.position.y = p.y(); + closest_pose.position.z = search_point.z; + + const float lane_yaw = getLaneletAngle(lanelet, search_point); + tf2::Quaternion q; + q.setRPY(0, 0, lane_yaw); + closest_pose.orientation = tf2::toMsg(q); + + return closest_pose; +} } // namespace lanelet::utils diff --git a/tmp/lanelet2_extension/lib/visualization.cpp b/tmp/lanelet2_extension/lib/visualization.cpp index 2e8e65bc..65a7f0c8 100644 --- a/tmp/lanelet2_extension/lib/visualization.cpp +++ b/tmp/lanelet2_extension/lib/visualization.cpp @@ -1167,14 +1167,14 @@ void visualization::pushLineStringMarker( p.z = (*(i + 1)).z(); marker->points.push_back(p); marker->colors.push_back(c); - p.x = (*(i + 1)).x() + x_offset; - p.y = (*(i + 1)).y() - y_offset; - p.z = (*(i + 1)).z(); - marker->points.push_back(p); p.x = (*(i + 1)).x() - x_offset; p.y = (*(i + 1)).y() + y_offset; p.z = (*(i + 1)).z(); marker->points.push_back(p); + p.x = (*(i + 1)).x() + x_offset; + p.y = (*(i + 1)).y() - y_offset; + p.z = (*(i + 1)).z(); + marker->points.push_back(p); p.x = (*i).x() - x_offset; p.y = (*i).y() + y_offset; p.z = (*i).z(); From 5f9a90afc7a6221b36372ee97fc519984abc01d3 Mon Sep 17 00:00:00 2001 From: Kenji Miyake Date: Tue, 2 Aug 2022 19:42:53 +0900 Subject: [PATCH 28/36] add NOLINT Signed-off-by: Kenji Miyake --- .../include/lanelet2_extension/io/autoware_osm_parser.hpp | 4 ++++ .../include/lanelet2_extension/projection/mgrs_projector.hpp | 4 ++++ .../regulatory_elements/autoware_traffic_light.hpp | 4 ++++ .../regulatory_elements/detection_area.hpp | 4 ++++ .../regulatory_elements/no_stopping_area.hpp | 4 ++++ .../lanelet2_extension/regulatory_elements/road_marking.hpp | 4 ++++ .../regulatory_elements/virtual_traffic_light.hpp | 4 ++++ .../lanelet2_extension/utility/message_conversion.hpp | 4 ++++ .../include/lanelet2_extension/utility/query.hpp | 4 ++++ .../include/lanelet2_extension/utility/route_checker.hpp | 4 ++++ .../include/lanelet2_extension/utility/utilities.hpp | 4 ++++ .../lanelet2_extension/visualization/visualization.hpp | 4 ++++ tmp/lanelet2_extension/lib/autoware_osm_parser.cpp | 4 ++++ tmp/lanelet2_extension/lib/autoware_traffic_light.cpp | 4 ++++ tmp/lanelet2_extension/lib/detection_area.cpp | 4 ++++ tmp/lanelet2_extension/lib/message_conversion.cpp | 4 ++++ tmp/lanelet2_extension/lib/mgrs_projector.cpp | 4 ++++ tmp/lanelet2_extension/lib/no_stopping_area.cpp | 4 ++++ tmp/lanelet2_extension/lib/query.cpp | 4 ++++ tmp/lanelet2_extension/lib/road_marking.cpp | 4 ++++ tmp/lanelet2_extension/lib/route_checker.cpp | 4 ++++ tmp/lanelet2_extension/lib/utilities.cpp | 4 ++++ tmp/lanelet2_extension/lib/virtual_traffic_light.cpp | 4 ++++ tmp/lanelet2_extension/lib/visualization.cpp | 4 ++++ tmp/lanelet2_extension/src/sample_code.cpp | 4 ++++ tmp/lanelet2_extension/src/validation.cpp | 4 ++++ tmp/lanelet2_extension/test/src/test_message_conversion.cpp | 5 +++++ tmp/lanelet2_extension/test/src/test_projector.cpp | 4 ++++ tmp/lanelet2_extension/test/src/test_query.cpp | 4 ++++ tmp/lanelet2_extension/test/src/test_regulatory_elements.cpp | 4 ++++ tmp/lanelet2_extension/test/src/test_route_checker.cpp | 4 ++++ tmp/lanelet2_extension/test/src/test_utilities.cpp | 4 ++++ 32 files changed, 129 insertions(+) diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/io/autoware_osm_parser.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/io/autoware_osm_parser.hpp index b1f15453..8645ddbf 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/io/autoware_osm_parser.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/io/autoware_osm_parser.hpp @@ -17,6 +17,8 @@ #ifndef LANELET2_EXTENSION__IO__AUTOWARE_OSM_PARSER_HPP_ #define LANELET2_EXTENSION__IO__AUTOWARE_OSM_PARSER_HPP_ +// NOLINTBEGIN(readability-identifier-naming) + #include #include @@ -56,4 +58,6 @@ class AutowareOsmParser : public OsmParser } // namespace lanelet::io_handlers +// NOLINTEND(readability-identifier-naming) + #endif // LANELET2_EXTENSION__IO__AUTOWARE_OSM_PARSER_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp index 0cb92abf..9355a06e 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp @@ -17,6 +17,8 @@ #ifndef LANELET2_EXTENSION__PROJECTION__MGRS_PROJECTOR_HPP_ #define LANELET2_EXTENSION__PROJECTION__MGRS_PROJECTOR_HPP_ +// NOLINTBEGIN(readability-identifier-naming) + #include #include @@ -112,4 +114,6 @@ class MGRSProjector : public Projector } // namespace projection } // namespace lanelet +// NOLINTEND(readability-identifier-naming) + #endif // LANELET2_EXTENSION__PROJECTION__MGRS_PROJECTOR_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp index ca3fd7e0..b7aebed3 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp @@ -17,6 +17,8 @@ #ifndef LANELET2_EXTENSION__REGULATORY_ELEMENTS__AUTOWARE_TRAFFIC_LIGHT_HPP_ #define LANELET2_EXTENSION__REGULATORY_ELEMENTS__AUTOWARE_TRAFFIC_LIGHT_HPP_ +// NOLINTBEGIN(readability-identifier-naming) + #include #include @@ -83,4 +85,6 @@ static lanelet::RegisterRegulatoryElement regAutowareTraff } // namespace lanelet::autoware +// NOLINTEND(readability-identifier-naming) + #endif // LANELET2_EXTENSION__REGULATORY_ELEMENTS__AUTOWARE_TRAFFIC_LIGHT_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/detection_area.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/detection_area.hpp index e73ce32f..2100ec8a 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/detection_area.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/detection_area.hpp @@ -17,6 +17,8 @@ #ifndef LANELET2_EXTENSION__REGULATORY_ELEMENTS__DETECTION_AREA_HPP_ #define LANELET2_EXTENSION__REGULATORY_ELEMENTS__DETECTION_AREA_HPP_ +// NOLINTBEGIN(readability-identifier-naming) + #include #include @@ -92,4 +94,6 @@ static lanelet::RegisterRegulatoryElement regDetectionArea; } // namespace autoware } // namespace lanelet +// NOLINTEND(readability-identifier-naming) + #endif // LANELET2_EXTENSION__REGULATORY_ELEMENTS__DETECTION_AREA_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/no_stopping_area.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/no_stopping_area.hpp index 1f1ec77f..3e86026a 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/no_stopping_area.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/no_stopping_area.hpp @@ -15,6 +15,8 @@ #ifndef LANELET2_EXTENSION__REGULATORY_ELEMENTS__NO_STOPPING_AREA_HPP_ #define LANELET2_EXTENSION__REGULATORY_ELEMENTS__NO_STOPPING_AREA_HPP_ +// NOLINTBEGIN(readability-identifier-naming) + #include #include @@ -87,4 +89,6 @@ static lanelet::RegisterRegulatoryElement regNoStoppingArea; } // namespace lanelet::autoware +// NOLINTEND(readability-identifier-naming) + #endif // LANELET2_EXTENSION__REGULATORY_ELEMENTS__NO_STOPPING_AREA_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/road_marking.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/road_marking.hpp index f1808b2c..6b4cb599 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/road_marking.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/road_marking.hpp @@ -15,6 +15,8 @@ #ifndef LANELET2_EXTENSION__REGULATORY_ELEMENTS__ROAD_MARKING_HPP_ #define LANELET2_EXTENSION__REGULATORY_ELEMENTS__ROAD_MARKING_HPP_ +// NOLINTBEGIN(readability-identifier-naming) + #include #include @@ -68,4 +70,6 @@ static lanelet::RegisterRegulatoryElement regRoadMarking; } // namespace autoware } // namespace lanelet +// NOLINTEND(readability-identifier-naming) + #endif // LANELET2_EXTENSION__REGULATORY_ELEMENTS__ROAD_MARKING_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/virtual_traffic_light.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/virtual_traffic_light.hpp index 4779a75b..011c8e78 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/virtual_traffic_light.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/virtual_traffic_light.hpp @@ -15,6 +15,8 @@ #ifndef LANELET2_EXTENSION__REGULATORY_ELEMENTS__VIRTUAL_TRAFFIC_LIGHT_HPP_ #define LANELET2_EXTENSION__REGULATORY_ELEMENTS__VIRTUAL_TRAFFIC_LIGHT_HPP_ +// NOLINTBEGIN(readability-identifier-naming) + #include #include @@ -78,4 +80,6 @@ static lanelet::RegisterRegulatoryElement regVirtualTraffic } // namespace autoware } // namespace lanelet +// NOLINTEND(readability-identifier-naming) + #endif // LANELET2_EXTENSION__REGULATORY_ELEMENTS__VIRTUAL_TRAFFIC_LIGHT_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/utility/message_conversion.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/utility/message_conversion.hpp index b82efbab..bd128141 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/utility/message_conversion.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/utility/message_conversion.hpp @@ -17,6 +17,8 @@ #ifndef LANELET2_EXTENSION__UTILITY__MESSAGE_CONVERSION_HPP_ #define LANELET2_EXTENSION__UTILITY__MESSAGE_CONVERSION_HPP_ +// NOLINTBEGIN(readability-identifier-naming) + #include #include #include @@ -91,4 +93,6 @@ void toGeomMsgPt32(const Eigen::Vector3d & src, geometry_msgs::msg::Point32 * ds } // namespace lanelet::utils::conversion +// NOLINTEND(readability-identifier-naming) + #endif // LANELET2_EXTENSION__UTILITY__MESSAGE_CONVERSION_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp index 9faaa190..43fa5ef2 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/utility/query.hpp @@ -17,6 +17,8 @@ #ifndef LANELET2_EXTENSION__UTILITY__QUERY_HPP_ #define LANELET2_EXTENSION__UTILITY__QUERY_HPP_ +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp" #include "lanelet2_extension/regulatory_elements/detection_area.hpp" #include "lanelet2_extension/regulatory_elements/no_stopping_area.hpp" @@ -255,4 +257,6 @@ std::vector getPrecedingLaneletSequences( } // namespace lanelet::utils::query +// NOLINTEND(readability-identifier-naming) + #endif // LANELET2_EXTENSION__UTILITY__QUERY_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/utility/route_checker.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/utility/route_checker.hpp index 3f4f267f..572d9e3c 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/utility/route_checker.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/utility/route_checker.hpp @@ -15,6 +15,8 @@ #ifndef LANELET2_EXTENSION__UTILITY__ROUTE_CHECKER_HPP_ #define LANELET2_EXTENSION__UTILITY__ROUTE_CHECKER_HPP_ +// NOLINTBEGIN(readability-identifier-naming) + #include #include @@ -29,4 +31,6 @@ using autoware_auto_planning_msgs::msg::HADMapRoute; bool isRouteValid(const HADMapRoute & route, const lanelet::LaneletMapPtr lanelet_map_ptr_); } // namespace lanelet::utils::route +// NOLINTEND(readability-identifier-naming) + #endif // LANELET2_EXTENSION__UTILITY__ROUTE_CHECKER_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp index a004f92a..a31d9d69 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/utility/utilities.hpp @@ -17,6 +17,8 @@ #ifndef LANELET2_EXTENSION__UTILITY__UTILITIES_HPP_ #define LANELET2_EXTENSION__UTILITY__UTILITIES_HPP_ +// NOLINTBEGIN(readability-identifier-naming) + #include #include @@ -84,4 +86,6 @@ geometry_msgs::msg::Pose getClosestCenterPose( } // namespace lanelet::utils +// NOLINTEND(readability-identifier-naming) + #endif // LANELET2_EXTENSION__UTILITY__UTILITIES_HPP_ diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp index 1ad6057b..1eed0cac 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp @@ -17,6 +17,8 @@ #ifndef LANELET2_EXTENSION__VISUALIZATION__VISUALIZATION_HPP_ #define LANELET2_EXTENSION__VISUALIZATION__VISUALIZATION_HPP_ +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp" #include "lanelet2_extension/regulatory_elements/no_stopping_area.hpp" #include "lanelet2_extension/utility/query.hpp" @@ -270,4 +272,6 @@ visualization_msgs::msg::MarkerArray obstaclePolygonsAsMarkerArray( } // namespace lanelet::visualization +// NOLINTEND(readability-identifier-naming) + #endif // LANELET2_EXTENSION__VISUALIZATION__VISUALIZATION_HPP_ diff --git a/tmp/lanelet2_extension/lib/autoware_osm_parser.cpp b/tmp/lanelet2_extension/lib/autoware_osm_parser.cpp index 5e656491..94fba5ca 100644 --- a/tmp/lanelet2_extension/lib/autoware_osm_parser.cpp +++ b/tmp/lanelet2_extension/lib/autoware_osm_parser.cpp @@ -14,6 +14,8 @@ // // Authors: Ryohsuke Mitsudome +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/io/autoware_osm_parser.hpp" #include @@ -87,3 +89,5 @@ void AutowareOsmParser::parseVersions( } // namespace io_handlers } // namespace lanelet + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/lib/autoware_traffic_light.cpp b/tmp/lanelet2_extension/lib/autoware_traffic_light.cpp index 7b12274b..63d185b6 100644 --- a/tmp/lanelet2_extension/lib/autoware_traffic_light.cpp +++ b/tmp/lanelet2_extension/lib/autoware_traffic_light.cpp @@ -14,6 +14,8 @@ // // Authors: Ryohsuke Mitsudome +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp" #include @@ -140,3 +142,5 @@ bool AutowareTrafficLight::removeLightBulbs(const LineStringOrPolygon3d & primit } } // namespace lanelet::autoware + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/lib/detection_area.cpp b/tmp/lanelet2_extension/lib/detection_area.cpp index 763974c3..d0818886 100644 --- a/tmp/lanelet2_extension/lib/detection_area.cpp +++ b/tmp/lanelet2_extension/lib/detection_area.cpp @@ -14,6 +14,8 @@ // // Authors: Ryohsuke Mitsudome +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/regulatory_elements/detection_area.hpp" #include @@ -151,3 +153,5 @@ void DetectionArea::removeStopLine() { parameters()[RoleName::RefLine] = {}; } } // namespace autoware } // namespace lanelet + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/lib/message_conversion.cpp b/tmp/lanelet2_extension/lib/message_conversion.cpp index c4bf4067..36aec9ff 100644 --- a/tmp/lanelet2_extension/lib/message_conversion.cpp +++ b/tmp/lanelet2_extension/lib/message_conversion.cpp @@ -14,6 +14,8 @@ // // Authors: Simon Thompson, Ryohsuke Mitsudome +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/utility/message_conversion.hpp" #include "lanelet2_extension/projection/mgrs_projector.hpp" @@ -186,3 +188,5 @@ void toGeomMsgPoly(const lanelet::ConstPolygon3d & ll_poly, geometry_msgs::msg:: } } // namespace lanelet::utils::conversion + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/lib/mgrs_projector.cpp b/tmp/lanelet2_extension/lib/mgrs_projector.cpp index a45478f5..576bd525 100644 --- a/tmp/lanelet2_extension/lib/mgrs_projector.cpp +++ b/tmp/lanelet2_extension/lib/mgrs_projector.cpp @@ -14,6 +14,8 @@ // // Authors: Simon Thompson, Ryohsuke Mitsudome +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/projection/mgrs_projector.hpp" #include @@ -124,3 +126,5 @@ void MGRSProjector::setMGRSCode(const GPSPoint & gps, const int precision) } } // namespace lanelet::projection + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/lib/no_stopping_area.cpp b/tmp/lanelet2_extension/lib/no_stopping_area.cpp index 95a96b1f..3d83a03c 100644 --- a/tmp/lanelet2_extension/lib/no_stopping_area.cpp +++ b/tmp/lanelet2_extension/lib/no_stopping_area.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/regulatory_elements/no_stopping_area.hpp" #include @@ -148,3 +150,5 @@ void NoStoppingArea::setStopLine(const LineString3d & stopLine) void NoStoppingArea::removeStopLine() { parameters()[RoleName::RefLine] = {}; } } // namespace lanelet::autoware + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/lib/query.cpp b/tmp/lanelet2_extension/lib/query.cpp index 993964d1..1e5bd8b7 100644 --- a/tmp/lanelet2_extension/lib/query.cpp +++ b/tmp/lanelet2_extension/lib/query.cpp @@ -14,6 +14,8 @@ // // Authors: Simon Thompson, Ryohsuke Mitsudome +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/utility/query.hpp" #include "lanelet2_extension/utility/message_conversion.hpp" @@ -878,3 +880,5 @@ std::vector query::getPrecedingLaneletSequences( } } // namespace lanelet::utils + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/lib/road_marking.cpp b/tmp/lanelet2_extension/lib/road_marking.cpp index eadf2cc2..fd2a2023 100644 --- a/tmp/lanelet2_extension/lib/road_marking.cpp +++ b/tmp/lanelet2_extension/lib/road_marking.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/regulatory_elements/road_marking.hpp" #include @@ -71,3 +73,5 @@ void RoadMarking::setRoadMarking(const LineString3d & road_marking) void RoadMarking::removeRoadMarking() { parameters()[RoleName::Refers] = {}; } } // namespace lanelet::autoware + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/lib/route_checker.cpp b/tmp/lanelet2_extension/lib/route_checker.cpp index fc5889ab..2ba24773 100644 --- a/tmp/lanelet2_extension/lib/route_checker.cpp +++ b/tmp/lanelet2_extension/lib/route_checker.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/utility/route_checker.hpp" #include @@ -40,3 +42,5 @@ bool route::isRouteValid( } } // namespace lanelet::utils + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/lib/utilities.cpp b/tmp/lanelet2_extension/lib/utilities.cpp index a62570dc..26b44971 100644 --- a/tmp/lanelet2_extension/lib/utilities.cpp +++ b/tmp/lanelet2_extension/lib/utilities.cpp @@ -14,6 +14,8 @@ // // Authors: Kenji Miyake, Ryohsuke Mitsudome +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/utility/utilities.hpp" #include "lanelet2_extension/utility/message_conversion.hpp" @@ -713,3 +715,5 @@ geometry_msgs::msg::Pose getClosestCenterPose( return closest_pose; } } // namespace lanelet::utils + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/lib/virtual_traffic_light.cpp b/tmp/lanelet2_extension/lib/virtual_traffic_light.cpp index 60dfebed..89c586eb 100644 --- a/tmp/lanelet2_extension/lib/virtual_traffic_light.cpp +++ b/tmp/lanelet2_extension/lib/virtual_traffic_light.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/regulatory_elements/virtual_traffic_light.hpp" #include @@ -59,3 +61,5 @@ VirtualTrafficLight::VirtualTrafficLight( } } // namespace lanelet::autoware + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/lib/visualization.cpp b/tmp/lanelet2_extension/lib/visualization.cpp index 65a7f0c8..50664bb4 100644 --- a/tmp/lanelet2_extension/lib/visualization.cpp +++ b/tmp/lanelet2_extension/lib/visualization.cpp @@ -14,6 +14,8 @@ // // Authors: Simon Thompson, Ryohsuke Mitsudome +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/visualization/visualization.hpp" #include "lanelet2_extension/utility/message_conversion.hpp" @@ -1256,3 +1258,5 @@ void visualization::pushArrowsMarker( } } // namespace lanelet + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/src/sample_code.cpp b/tmp/lanelet2_extension/src/sample_code.cpp index 00fe92cd..d4595fbd 100644 --- a/tmp/lanelet2_extension/src/sample_code.cpp +++ b/tmp/lanelet2_extension/src/sample_code.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/projection/mgrs_projector.hpp" #include "lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp" @@ -119,3 +121,5 @@ int main(int argc, char * argv[]) usingAutowareTrafficLight(map_file_path); return 0; } + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/src/validation.cpp b/tmp/lanelet2_extension/src/validation.cpp index 565775f2..814824c3 100644 --- a/tmp/lanelet2_extension/src/validation.cpp +++ b/tmp/lanelet2_extension/src/validation.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/projection/mgrs_projector.hpp" #include "lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp" @@ -171,3 +173,5 @@ int main(int argc, char * argv[]) return 0; } + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/test/src/test_message_conversion.cpp b/tmp/lanelet2_extension/test/src/test_message_conversion.cpp index 15910219..2ec88ab0 100644 --- a/tmp/lanelet2_extension/test/src/test_message_conversion.cpp +++ b/tmp/lanelet2_extension/test/src/test_message_conversion.cpp @@ -11,6 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/utility/message_conversion.hpp" #include "lanelet2_extension/utility/query.hpp" @@ -165,3 +168,5 @@ int main(int argc, char ** argv) testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/test/src/test_projector.cpp b/tmp/lanelet2_extension/test/src/test_projector.cpp index 81cc5d5c..dacd0f59 100644 --- a/tmp/lanelet2_extension/test/src/test_projector.cpp +++ b/tmp/lanelet2_extension/test/src/test_projector.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/projection/mgrs_projector.hpp" #include @@ -81,3 +83,5 @@ int main(int argc, char ** argv) testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/test/src/test_query.cpp b/tmp/lanelet2_extension/test/src/test_query.cpp index b2bfa541..090f7cba 100644 --- a/tmp/lanelet2_extension/test/src/test_query.cpp +++ b/tmp/lanelet2_extension/test/src/test_query.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/utility/query.hpp" #include @@ -132,3 +134,5 @@ int main(int argc, char ** argv) testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/test/src/test_regulatory_elements.cpp b/tmp/lanelet2_extension/test/src/test_regulatory_elements.cpp index 5c8e88d4..dce43181 100644 --- a/tmp/lanelet2_extension/test/src/test_regulatory_elements.cpp +++ b/tmp/lanelet2_extension/test/src/test_regulatory_elements.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp" #include @@ -138,3 +140,5 @@ int main(int argc, char ** argv) testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/test/src/test_route_checker.cpp b/tmp/lanelet2_extension/test/src/test_route_checker.cpp index 9c83de9d..1f4d6a89 100644 --- a/tmp/lanelet2_extension/test/src/test_route_checker.cpp +++ b/tmp/lanelet2_extension/test/src/test_route_checker.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/utility/message_conversion.hpp" #include "lanelet2_extension/utility/route_checker.hpp" @@ -93,3 +95,5 @@ int main(int argc, char ** argv) testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } + +// NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/test/src/test_utilities.cpp b/tmp/lanelet2_extension/test/src/test_utilities.cpp index bd28a564..10103d55 100644 --- a/tmp/lanelet2_extension/test/src/test_utilities.cpp +++ b/tmp/lanelet2_extension/test/src/test_utilities.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +// NOLINTBEGIN(readability-identifier-naming) + #include "lanelet2_extension/utility/utilities.hpp" #include @@ -117,3 +119,5 @@ int main(int argc, char ** argv) testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } + +// NOLINTEND(readability-identifier-naming) From 26fd06d47867595572be3ab0dffbbd7d3521bbaf Mon Sep 17 00:00:00 2001 From: Kenji Miyake Date: Tue, 2 Aug 2022 20:03:38 +0900 Subject: [PATCH 29/36] remove autoware_auto_msgs from reverse_depends/build_depends.repos Signed-off-by: Kenji Miyake --- .github/workflows/build-and-test-with-reverse-depends.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-and-test-with-reverse-depends.yaml b/.github/workflows/build-and-test-with-reverse-depends.yaml index 73c45b56..97190d46 100644 --- a/.github/workflows/build-and-test-with-reverse-depends.yaml +++ b/.github/workflows/build-and-test-with-reverse-depends.yaml @@ -30,6 +30,7 @@ jobs: - name: Import depends of reverse depends run: | yq -i 'del(.repositories.* | select(.url == "https://github.com/autowarefoundation/autoware_common.git"))' reverse_depends/build_depends.repos + yq -i 'del(.repositories.* | select(.url == "https://github.com/tier4/autoware_auto_msgs.git"))' reverse_depends/build_depends.repos vcs import reverse_depends < reverse_depends/build_depends.repos - name: Build From a053a0b10e5d17c2130665eec90813fde297c332 Mon Sep 17 00:00:00 2001 From: Kenji Miyake Date: Tue, 2 Aug 2022 20:04:07 +0900 Subject: [PATCH 30/36] remove reverse_depends/map/lanelet2_extension Signed-off-by: Kenji Miyake --- .github/workflows/build-and-test-with-reverse-depends.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-and-test-with-reverse-depends.yaml b/.github/workflows/build-and-test-with-reverse-depends.yaml index 97190d46..6c1c949f 100644 --- a/.github/workflows/build-and-test-with-reverse-depends.yaml +++ b/.github/workflows/build-and-test-with-reverse-depends.yaml @@ -32,6 +32,7 @@ jobs: yq -i 'del(.repositories.* | select(.url == "https://github.com/autowarefoundation/autoware_common.git"))' reverse_depends/build_depends.repos yq -i 'del(.repositories.* | select(.url == "https://github.com/tier4/autoware_auto_msgs.git"))' reverse_depends/build_depends.repos vcs import reverse_depends < reverse_depends/build_depends.repos + rm -rf reverse_depends/map/lanelet2_extension - name: Build if: ${{ steps.get-self-packages.outputs.self-packages != '' }} From ec2ea18096f86fa8e0af26f3223d874bec7d9a41 Mon Sep 17 00:00:00 2001 From: Kenji Miyake <31987104+kenji-miyake@users.noreply.github.com> Date: Tue, 2 Aug 2022 20:22:37 +0900 Subject: [PATCH 31/36] fix typo --- .../docs/extra_regulatory_elements.md | 2 +- .../projection/mgrs_projector.hpp | 2 +- tmp/lanelet2_extension/lib/mgrs_projector.cpp | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tmp/lanelet2_extension/docs/extra_regulatory_elements.md b/tmp/lanelet2_extension/docs/extra_regulatory_elements.md index 041ec036..3dae51bb 100644 --- a/tmp/lanelet2_extension/docs/extra_regulatory_elements.md +++ b/tmp/lanelet2_extension/docs/extra_regulatory_elements.md @@ -5,7 +5,7 @@ This regulatory element specifies region of interest which vehicle must pay attention whenever it is driving along the associated lanelet. When there are any obstacle in the detection area, vehicle must stop at specified stopline. - refers: refers to detection area polygon. There could be multiple detection areas registered to a single regulatory element. -- refline: refers to stop line of the detection area +- ref_line: refers to stop line of the detection area ![Detection area](detection_area.png) diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp index 9355a06e..29f589b1 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp @@ -45,7 +45,7 @@ class MGRSProjector : public Projector BasicPoint3d forward(const GPSPoint & gps) const override; /** - * [MGRSProjector::forward projects gpgs lat/lon to MGRS xyz coordinate] + * [MGRSProjector::forward projects gps lat/lon to MGRS xyz coordinate] * @param gps [point with latitude longitude information] * @param precision [resolution of MGRS Grid 0=100km, 1=10km, 2=1km, 3=100m, * 4=10m, 5=1m] diff --git a/tmp/lanelet2_extension/lib/mgrs_projector.cpp b/tmp/lanelet2_extension/lib/mgrs_projector.cpp index 576bd525..d4e1ce79 100644 --- a/tmp/lanelet2_extension/lib/mgrs_projector.cpp +++ b/tmp/lanelet2_extension/lib/mgrs_projector.cpp @@ -41,13 +41,13 @@ BasicPoint3d MGRSProjector::forward(const GPSPoint & gps, const int precision) c BasicPoint3d mgrs_point{0., 0., gps.ele}; BasicPoint3d utm_point{0., 0., gps.ele}; int zone{}; - bool northp{}; + bool is_north{}; std::string mgrs_code; try { - GeographicLib::UTMUPS::Forward(gps.lat, gps.lon, zone, northp, utm_point.x(), utm_point.y()); + GeographicLib::UTMUPS::Forward(gps.lat, gps.lon, zone, is_north, utm_point.x(), utm_point.y()); GeographicLib::MGRS::Forward( - zone, northp, utm_point.x(), utm_point.y(), gps.lat, precision, mgrs_code); + zone, is_north, utm_point.x(), utm_point.y(), gps.lat, precision, mgrs_code); } catch (const GeographicLib::GeographicErr & err) { std::cerr << err.what() << std::endl; return mgrs_point; @@ -90,13 +90,13 @@ GPSPoint MGRSProjector::reverse(const BasicPoint3d & mgrs_point, const std::stri int zone{}; int prec{}; - bool northp{}; + bool is_north{}; try { GeographicLib::MGRS::Reverse( - mgrs_code, zone, northp, utm_point.x(), utm_point.y(), prec, false); + mgrs_code, zone, is_north, utm_point.x(), utm_point.y(), prec, false); utm_point.x() += fmod(mgrs_point.x(), pow(10, 5 - prec)); utm_point.y() += fmod(mgrs_point.y(), pow(10, 5 - prec)); - GeographicLib::UTMUPS::Reverse(zone, northp, utm_point.x(), utm_point.y(), gps.lat, gps.lon); + GeographicLib::UTMUPS::Reverse(zone, is_north, utm_point.x(), utm_point.y(), gps.lat, gps.lon); } catch (const GeographicLib::GeographicErr & err) { std::cerr << "Failed to convert from MGRS to WGS"; return gps; @@ -111,13 +111,13 @@ void MGRSProjector::setMGRSCode(const GPSPoint & gps, const int precision) { BasicPoint3d utm_point{0., 0., gps.ele}; int zone{}; - bool northp{}; + bool is_north{}; std::string mgrs_code; try { - GeographicLib::UTMUPS::Forward(gps.lat, gps.lon, zone, northp, utm_point.x(), utm_point.y()); + GeographicLib::UTMUPS::Forward(gps.lat, gps.lon, zone, is_north, utm_point.x(), utm_point.y()); GeographicLib::MGRS::Forward( - zone, northp, utm_point.x(), utm_point.y(), gps.lat, precision, mgrs_code); + zone, is_north, utm_point.x(), utm_point.y(), gps.lat, precision, mgrs_code); } catch (const GeographicLib::GeographicErr & err) { std::cerr << err.what() << std::endl; } From 7be5a9fa55cfd69009d5c26e52cb0869f432f638 Mon Sep 17 00:00:00 2001 From: Kenji Miyake Date: Tue, 2 Aug 2022 21:11:48 +0900 Subject: [PATCH 32/36] use Humble for clang-tidy-differential Signed-off-by: Kenji Miyake --- .github/workflows/build-and-test-differential.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-test-differential.yaml b/.github/workflows/build-and-test-differential.yaml index 746d6bd3..bb407eb5 100644 --- a/.github/workflows/build-and-test-differential.yaml +++ b/.github/workflows/build-and-test-differential.yaml @@ -64,7 +64,7 @@ jobs: clang-tidy-differential: runs-on: ubuntu-latest - container: ros:galactic + container: ros:humble needs: build-and-test-differential steps: - name: Check out repository @@ -83,7 +83,7 @@ jobs: if: ${{ steps.get-modified-packages.outputs.modified-packages != '' }} uses: autowarefoundation/autoware-github-actions/clang-tidy@v1 with: - rosdistro: galactic + rosdistro: humble target-packages: ${{ steps.get-modified-packages.outputs.modified-packages }} clang-tidy-config-url: https://raw.githubusercontent.com/autowarefoundation/autoware/main/.clang-tidy build-depends-repos: build_depends.repos From 8cc2dfb54256ee161b08379dc58d411258954ee1 Mon Sep 17 00:00:00 2001 From: Kenji Miyake Date: Tue, 2 Aug 2022 23:43:44 +0900 Subject: [PATCH 33/36] fix clang-tidy warnings Signed-off-by: Kenji Miyake --- .clang-tidy | 2 + .../projection/mgrs_projector.hpp | 7 +- .../autoware_traffic_light.hpp | 2 +- .../regulatory_elements/detection_area.hpp | 15 ++-- .../regulatory_elements/no_stopping_area.hpp | 8 +-- .../regulatory_elements/road_marking.hpp | 13 ++-- .../virtual_traffic_light.hpp | 7 +- .../visualization/visualization.hpp | 2 +- .../lib/autoware_osm_parser.cpp | 7 +- tmp/lanelet2_extension/lib/detection_area.cpp | 7 +- .../lib/message_conversion.cpp | 6 +- tmp/lanelet2_extension/lib/utilities.cpp | 71 +++++++++++-------- tmp/lanelet2_extension/lib/visualization.cpp | 36 +++++----- .../test/src/test_message_conversion.cpp | 4 +- .../test/src/test_query.cpp | 13 +++- .../test/src/test_regulatory_elements.cpp | 3 +- 16 files changed, 106 insertions(+), 97 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index fcf5a6fc..5d5692a7 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -425,6 +425,8 @@ CheckOptions: value: "" - key: portability-simd-intrinsics.Suggest value: "0" + - key: readability-function-cognitive-complexity.IgnoreMacros + value: "1" - key: readability-else-after-return.WarnOnUnfixable value: "1" - key: readability-identifier-naming.NamespaceCase diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp index 29f589b1..37b6bc07 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/projection/mgrs_projector.hpp @@ -28,9 +28,7 @@ #include #include -namespace lanelet -{ -namespace projection +namespace lanelet::projection { class MGRSProjector : public Projector { @@ -111,8 +109,7 @@ class MGRSProjector : public Projector mutable std::string projected_grid_; }; -} // namespace projection -} // namespace lanelet +} // namespace lanelet::projection // NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp index b7aebed3..ba1da38e 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/autoware_traffic_light.hpp @@ -54,7 +54,7 @@ class AutowareTrafficLight : public lanelet::TrafficLight * There might be multiple traffic light bulbs but they are required to show * the same signal. */ - ConstLineStrings3d lightBulbs() const; + [[nodiscard]] ConstLineStrings3d lightBulbs() const; /** * @brief add a new traffic light bulb diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/detection_area.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/detection_area.hpp index 2100ec8a..4af1ef23 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/detection_area.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/detection_area.hpp @@ -25,9 +25,7 @@ #include #include -namespace lanelet -{ -namespace autoware +namespace lanelet::autoware { class DetectionArea : public lanelet::RegulatoryElement { @@ -48,8 +46,8 @@ class DetectionArea : public lanelet::RegulatoryElement * @brief get the relevant detection_areas * @return detection_areas */ - ConstPolygons3d detectionAreas() const; - Polygons3d detectionAreas(); + [[nodiscard]] ConstPolygons3d detectionAreas() const; + [[nodiscard]] Polygons3d detectionAreas(); /** * @brief add a new detection area @@ -68,8 +66,8 @@ class DetectionArea : public lanelet::RegulatoryElement * @brief get the stop line for the detection area * @return the stop line as LineString */ - ConstLineString3d stopLine() const; - LineString3d stopLine(); + [[nodiscard]] ConstLineString3d stopLine() const; + [[nodiscard]] LineString3d stopLine(); /** * @brief set a new stop line, overwrite the old one @@ -91,8 +89,7 @@ class DetectionArea : public lanelet::RegulatoryElement }; static lanelet::RegisterRegulatoryElement regDetectionArea; -} // namespace autoware -} // namespace lanelet +} // namespace lanelet::autoware // NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/no_stopping_area.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/no_stopping_area.hpp index 3e86026a..156b190c 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/no_stopping_area.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/no_stopping_area.hpp @@ -44,8 +44,8 @@ class NoStoppingArea : public lanelet::RegulatoryElement * @brief get the relevant no stopping area * @return no stopping area */ - ConstPolygons3d noStoppingAreas() const; - Polygons3d noStoppingAreas(); + [[nodiscard]] ConstPolygons3d noStoppingAreas() const; + [[nodiscard]] Polygons3d noStoppingAreas(); /** * @brief add a new no stopping area @@ -64,8 +64,8 @@ class NoStoppingArea : public lanelet::RegulatoryElement * @brief get the stop line for the no stopping area * @return the stop line as LineString */ - Optional stopLine() const; - Optional stopLine(); + [[nodiscard]] Optional stopLine() const; + [[nodiscard]] Optional stopLine(); /** * @brief set a new stop line, overwrite the old one diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/road_marking.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/road_marking.hpp index 6b4cb599..f2241399 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/road_marking.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/road_marking.hpp @@ -23,9 +23,7 @@ #include #include -namespace lanelet -{ -namespace autoware +namespace lanelet::autoware { class RoadMarking : public lanelet::RegulatoryElement { @@ -44,14 +42,14 @@ class RoadMarking : public lanelet::RegulatoryElement * @brief get the relevant road marking * @return road marking */ - ConstLineString3d roadMarking() const; - LineString3d roadMarking(); + [[nodiscard]] ConstLineString3d roadMarking() const; + [[nodiscard]] LineString3d roadMarking(); /** * @brief add a new road marking * @param primitive road marking to add */ - void setRoadMarking(const LineString3d & primitive); + void setRoadMarking(const LineString3d & road_marking); /** * @brief remove a road marking @@ -67,8 +65,7 @@ class RoadMarking : public lanelet::RegulatoryElement }; static lanelet::RegisterRegulatoryElement regRoadMarking; -} // namespace autoware -} // namespace lanelet +} // namespace lanelet::autoware // NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/virtual_traffic_light.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/virtual_traffic_light.hpp index 011c8e78..e0c7b8d9 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/virtual_traffic_light.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/regulatory_elements/virtual_traffic_light.hpp @@ -23,9 +23,7 @@ #include #include -namespace lanelet -{ -namespace autoware +namespace lanelet::autoware { class VirtualTrafficLight : public lanelet::RegulatoryElement { @@ -77,8 +75,7 @@ class VirtualTrafficLight : public lanelet::RegulatoryElement static lanelet::RegisterRegulatoryElement regVirtualTrafficLight; -} // namespace autoware -} // namespace lanelet +} // namespace lanelet::autoware // NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp b/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp index 1eed0cac..822d89f0 100644 --- a/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp +++ b/tmp/lanelet2_extension/include/lanelet2_extension/visualization/visualization.hpp @@ -170,7 +170,7 @@ visualization_msgs::msg::MarkerArray laneletDirectionAsMarkerArray( */ visualization_msgs::msg::MarkerArray lineStringsAsMarkerArray( const std::vector & line_strings, const std::string & name_space, - const std_msgs::msg::ColorRGBA & c, const double lss); + const std_msgs::msg::ColorRGBA & c, const float lss); /** * [autowareTrafficLightsAsMarkerArray creates marker array to visualize traffic diff --git a/tmp/lanelet2_extension/lib/autoware_osm_parser.cpp b/tmp/lanelet2_extension/lib/autoware_osm_parser.cpp index 94fba5ca..2a74f361 100644 --- a/tmp/lanelet2_extension/lib/autoware_osm_parser.cpp +++ b/tmp/lanelet2_extension/lib/autoware_osm_parser.cpp @@ -26,9 +26,7 @@ #include #include -namespace lanelet -{ -namespace io_handlers +namespace lanelet::io_handlers { std::unique_ptr AutowareOsmParser::parse( const std::string & filename, ErrorMessages & errors) const @@ -87,7 +85,6 @@ void AutowareOsmParser::parseVersions( } } -} // namespace io_handlers -} // namespace lanelet +} // namespace lanelet::io_handlers // NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/lib/detection_area.cpp b/tmp/lanelet2_extension/lib/detection_area.cpp index d0818886..54a38996 100644 --- a/tmp/lanelet2_extension/lib/detection_area.cpp +++ b/tmp/lanelet2_extension/lib/detection_area.cpp @@ -27,9 +27,7 @@ #include #include -namespace lanelet -{ -namespace autoware +namespace lanelet::autoware { namespace { @@ -151,7 +149,6 @@ void DetectionArea::setStopLine(const LineString3d & stopLine) void DetectionArea::removeStopLine() { parameters()[RoleName::RefLine] = {}; } -} // namespace autoware -} // namespace lanelet +} // namespace lanelet::autoware // NOLINTEND(readability-identifier-naming) diff --git a/tmp/lanelet2_extension/lib/message_conversion.cpp b/tmp/lanelet2_extension/lib/message_conversion.cpp index 36aec9ff..ae204fb5 100644 --- a/tmp/lanelet2_extension/lib/message_conversion.cpp +++ b/tmp/lanelet2_extension/lib/message_conversion.cpp @@ -134,9 +134,9 @@ void toGeomMsgPt32(const Eigen::Vector3d & src, geometry_msgs::msg::Point32 * ds std::cerr << __FUNCTION__ << "pointer is null!" << std::endl; return; } - dst->x = src.x(); - dst->y = src.y(); - dst->z = src.z(); + dst->x = static_cast(src.x()); + dst->y = static_cast(src.y()); + dst->z = static_cast(src.z()); } geometry_msgs::msg::Point toGeomMsgPt(const geometry_msgs::msg::Point32 & src) diff --git a/tmp/lanelet2_extension/lib/utilities.cpp b/tmp/lanelet2_extension/lib/utilities.cpp index 26b44971..99685449 100644 --- a/tmp/lanelet2_extension/lib/utilities.cpp +++ b/tmp/lanelet2_extension/lib/utilities.cpp @@ -80,7 +80,7 @@ namespace lanelet::BasicPolygon2d poly = ll.polygon2d().basicPolygon(); double distance = lanelet::geometry::distance(poly, search_point); if (distance < std::numeric_limits::epsilon()) { - contacting_lanelet_ids->push_back(ll.id()); + contacting_lanelet_ids->push_back(static_cast(ll.id())); } } } @@ -144,7 +144,7 @@ std::vector resamplePoints( const lanelet::ConstLineString3d & line_string, const int num_segments) { // Calculate length - const auto line_length = lanelet::geometry::length(line_string); + const auto line_length = static_cast(lanelet::geometry::length(line_string)); // Calculate accumulated lengths const auto accumulated_lengths = calculateAccumulatedLengths(line_string); @@ -180,8 +180,8 @@ lanelet::LineString3d getLineStringFromArcLength( double accumulated_length = 0; size_t start_index = linestring.size(); for (size_t i = 0; i < linestring.size() - 1; i++) { - const auto p1 = linestring[i]; - const auto p2 = linestring[i + 1]; + const auto & p1 = linestring[i]; + const auto & p2 = linestring[i + 1]; const double length = boost::geometry::distance(p1.basicPoint(), p2.basicPoint()); if (accumulated_length + length > s1) { start_index = i; @@ -190,8 +190,8 @@ lanelet::LineString3d getLineStringFromArcLength( accumulated_length += length; } if (start_index < linestring.size() - 1) { - const auto p1 = linestring[start_index]; - const auto p2 = linestring[start_index + 1]; + const auto & p1 = linestring[start_index]; + const auto & p2 = linestring[start_index + 1]; const double residue = s1 - accumulated_length; const auto direction_vector = (p2.basicPoint() - p1.basicPoint()).normalized(); const auto start_basic_point = p1.basicPoint() + residue * direction_vector; @@ -202,8 +202,8 @@ lanelet::LineString3d getLineStringFromArcLength( accumulated_length = 0; size_t end_index = linestring.size(); for (size_t i = 0; i < linestring.size() - 1; i++) { - const auto p1 = linestring[i]; - const auto p2 = linestring[i + 1]; + const auto & p1 = linestring[i]; + const auto & p2 = linestring[i + 1]; const double length = boost::geometry::distance(p1.basicPoint(), p2.basicPoint()); if (accumulated_length + length > s2) { end_index = i; @@ -217,15 +217,15 @@ lanelet::LineString3d getLineStringFromArcLength( points.push_back(p); } if (end_index < linestring.size() - 1) { - const auto p1 = linestring[end_index]; - const auto p2 = linestring[end_index + 1]; + const auto & p1 = linestring[end_index]; + const auto & p2 = linestring[end_index + 1]; const double residue = s2 - accumulated_length; const auto direction_vector = (p2.basicPoint() - p1.basicPoint()).normalized(); const auto end_basic_point = p1.basicPoint() + residue * direction_vector; const auto end_point = lanelet::Point3d(lanelet::InvalId, end_basic_point); points.push_back(end_point); } - return lanelet::LineString3d(lanelet::InvalId, points); + return lanelet::LineString3d{lanelet::InvalId, points}; } lanelet::ConstLanelet combineLanelets(const lanelet::ConstLanelets & lanelets) @@ -258,8 +258,10 @@ lanelet::LineString3d generateFineCenterline( const lanelet::ConstLanelet & lanelet_obj, const double resolution) { // Get length of longer border - const double left_length = lanelet::geometry::length(lanelet_obj.leftBound()); - const double right_length = lanelet::geometry::length(lanelet_obj.rightBound()); + const double left_length = + static_cast(lanelet::geometry::length(lanelet_obj.leftBound())); + const double right_length = + static_cast(lanelet::geometry::length(lanelet_obj.rightBound())); const double longer_distance = (left_length > right_length) ? left_length : right_length; const int num_segments = std::max(static_cast(ceil(longer_distance / resolution)), 1); @@ -284,8 +286,10 @@ lanelet::ConstLineString3d getCenterlineWithOffset( const lanelet::ConstLanelet & lanelet_obj, const double offset, const double resolution) { // Get length of longer border - const double left_length = lanelet::geometry::length(lanelet_obj.leftBound()); - const double right_length = lanelet::geometry::length(lanelet_obj.rightBound()); + const double left_length = + static_cast(lanelet::geometry::length(lanelet_obj.leftBound())); + const double right_length = + static_cast(lanelet::geometry::length(lanelet_obj.rightBound())); const double longer_distance = (left_length > right_length) ? left_length : right_length; const int num_segments = std::max(static_cast(ceil(longer_distance / resolution)), 1); @@ -315,8 +319,10 @@ lanelet::ConstLineString3d getRightBoundWithOffset( const lanelet::ConstLanelet & lanelet_obj, const double offset, const double resolution) { // Get length of longer border - const double left_length = lanelet::geometry::length(lanelet_obj.leftBound()); - const double right_length = lanelet::geometry::length(lanelet_obj.rightBound()); + const double left_length = + static_cast(lanelet::geometry::length(lanelet_obj.leftBound())); + const double right_length = + static_cast(lanelet::geometry::length(lanelet_obj.rightBound())); const double longer_distance = (left_length > right_length) ? left_length : right_length; const int num_segments = std::max(static_cast(ceil(longer_distance / resolution)), 1); @@ -344,8 +350,10 @@ lanelet::ConstLineString3d getLeftBoundWithOffset( const lanelet::ConstLanelet & lanelet_obj, const double offset, const double resolution) { // Get length of longer border - const double left_length = lanelet::geometry::length(lanelet_obj.leftBound()); - const double right_length = lanelet::geometry::length(lanelet_obj.rightBound()); + const double left_length = + static_cast(lanelet::geometry::length(lanelet_obj.leftBound())); + const double right_length = + static_cast(lanelet::geometry::length(lanelet_obj.rightBound())); const double longer_distance = (left_length > right_length) ? left_length : right_length; const int num_segments = std::max(static_cast(ceil(longer_distance / resolution)), 1); @@ -563,12 +571,13 @@ bool lineStringToPolygon( double getLaneletLength2d(const lanelet::ConstLanelet & lanelet) { - return boost::geometry::length(lanelet::utils::to2D(lanelet.centerline()).basicLineString()); + return static_cast( + boost::geometry::length(lanelet::utils::to2D(lanelet.centerline()).basicLineString())); } double getLaneletLength3d(const lanelet::ConstLanelet & lanelet) { - return boost::geometry::length(lanelet.centerline().basicLineString()); + return static_cast(boost::geometry::length(lanelet.centerline().basicLineString())); } double getLaneletLength2d(const lanelet::ConstLanelets & lanelet_sequence) @@ -606,7 +615,7 @@ lanelet::ArcCoordinates getArcCoordinates( arc_coordinates.length += length; break; } - length += boost::geometry::length(centerline_2d); + length += static_cast(boost::geometry::length(centerline_2d)); } return arc_coordinates; } @@ -654,14 +663,14 @@ lanelet::CompoundPolygon3d getPolygonFromArcLength( const auto ratio_s1 = s1_saturated / total_length; const auto ratio_s2 = s2_saturated / total_length; - const auto s1_left = - ratio_s1 * boost::geometry::length(combined_lanelet.leftBound().basicLineString()); - const auto s2_left = - ratio_s2 * boost::geometry::length(combined_lanelet.leftBound().basicLineString()); - const auto s1_right = - ratio_s1 * boost::geometry::length(combined_lanelet.rightBound().basicLineString()); - const auto s2_right = - ratio_s2 * boost::geometry::length(combined_lanelet.rightBound().basicLineString()); + const auto s1_left = static_cast( + ratio_s1 * boost::geometry::length(combined_lanelet.leftBound().basicLineString())); + const auto s2_left = static_cast( + ratio_s2 * boost::geometry::length(combined_lanelet.leftBound().basicLineString())); + const auto s1_right = static_cast( + ratio_s1 * boost::geometry::length(combined_lanelet.rightBound().basicLineString())); + const auto s2_right = static_cast( + ratio_s2 * boost::geometry::length(combined_lanelet.rightBound().basicLineString())); const auto left_bound = getLineStringFromArcLength(combined_lanelet.leftBound(), s1_left, s2_left); @@ -707,7 +716,7 @@ geometry_msgs::msg::Pose getClosestCenterPose( closest_pose.position.y = p.y(); closest_pose.position.z = search_point.z; - const float lane_yaw = getLaneletAngle(lanelet, search_point); + const double lane_yaw = getLaneletAngle(lanelet, search_point); tf2::Quaternion q; q.setRPY(0, 0, lane_yaw); closest_pose.orientation = tf2::toMsg(q); diff --git a/tmp/lanelet2_extension/lib/visualization.cpp b/tmp/lanelet2_extension/lib/visualization.cpp index 50664bb4..eb728a5d 100644 --- a/tmp/lanelet2_extension/lib/visualization.cpp +++ b/tmp/lanelet2_extension/lib/visualization.cpp @@ -112,7 +112,7 @@ bool inputLightMarker(visualization_msgs::msg::Marker * marker, const lanelet::C return false; } - marker->id = p.id(); + marker->id = static_cast(p.id()); geometry_msgs::msg::Point point; marker->pose.position.x = p.x(); @@ -254,7 +254,7 @@ void pushLaneletDirectionMarker( bool isClockWise(const geometry_msgs::msg::Polygon & polygon) { - const int N = polygon.points.size(); + const int N = static_cast(polygon.points.size()); const double x_offset = polygon.points[0].x; const double y_offset = polygon.points[0].y; double sum = 0.0; @@ -368,7 +368,7 @@ void visualization::polygon2Triangle( } // ear clipping: find smallest internal angle in polygon - int N = poly.points.size(); + int N = static_cast(poly.points.size()); // array of angles for each vertex std::vector is_acute_angle; @@ -419,7 +419,9 @@ void visualization::polygon2Triangle( } // create triangle - geometry_msgs::msg::Point32 p0, p1, p2; + geometry_msgs::msg::Point32 p0; + geometry_msgs::msg::Point32 p1; + geometry_msgs::msg::Point32 p2; adjacentPoints(clipped_vertex, N, poly, &p0, &p1, &p2); geometry_msgs::msg::Polygon triangle; triangle.points.push_back(p0); @@ -438,7 +440,7 @@ void visualization::polygon2Triangle( is_acute_angle.erase(it_angle); // update angle list - N = poly.points.size(); + N = static_cast(poly.points.size()); if (clipped_vertex == N) { clipped_vertex = 0; } @@ -547,7 +549,7 @@ visualization_msgs::msg::MarkerArray visualization::generateTrafficLightIdMaker( marker.header.frame_id = "map"; marker.header.stamp = rclcpp::Time(); marker.ns = "traffic_light_id"; - marker.id = ls.id(); + marker.id = static_cast(ls.id()); marker.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING; marker.lifetime = duration; marker.action = visualization_msgs::msg::Marker::ADD; @@ -614,7 +616,7 @@ visualization_msgs::msg::MarkerArray visualization::detectionAreasAsMarkerArray( for (const auto & da_reg_elem : da_reg_elems) { marker.points.clear(); marker.colors.clear(); - marker.id = da_reg_elem->id(); + marker.id = static_cast(da_reg_elem->id()); // area visualization const auto detection_areas = da_reg_elem->detectionAreas(); @@ -689,7 +691,7 @@ visualization_msgs::msg::MarkerArray visualization::noStoppingAreasAsMarkerArray for (const auto & no_reg_elem : no_reg_elems) { marker.points.clear(); marker.colors.clear(); - marker.id = no_reg_elem->id(); + marker.id = static_cast(no_reg_elem->id()); // area visualization const auto no_stopping_areas = no_reg_elem->noStoppingAreas(); @@ -801,12 +803,12 @@ visualization_msgs::msg::MarkerArray visualization::generateLaneletIdMarker( marker.header.frame_id = "map"; marker.header.stamp = rclcpp::Clock().now(); marker.ns = "lanelet_id"; - marker.id = ll.id(); + marker.id = static_cast(ll.id()); marker.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING; marker.action = visualization_msgs::msg::Marker::ADD; const auto centerline = ll.centerline(); const size_t target_position_index = centerline.size() / 2; - const auto target_position = centerline[target_position_index]; + const auto & target_position = centerline[target_position_index]; marker.pose.position.x = target_position.x(); marker.pose.position.y = target_position.y(); marker.pose.position.z = target_position.z(); @@ -844,7 +846,7 @@ visualization_msgs::msg::MarkerArray visualization::obstaclePolygonsAsMarkerArra visualization_msgs::msg::MarkerArray visualization::lineStringsAsMarkerArray( const std::vector & line_strings, const std::string & name_space, - const std_msgs::msg::ColorRGBA & c, const double lss) + const std_msgs::msg::ColorRGBA & c, const float lss) { visualization_msgs::msg::MarkerArray ls_marker_array; if (line_strings.empty()) { @@ -869,7 +871,7 @@ visualization_msgs::msg::MarkerArray visualization::laneletsBoundaryAsMarkerArra const bool viz_centerline, const std::string & additional_namespace) { const float lss = 0.1; // line string size - const float lss_center = std::max(lss * 0.1, 0.02); + const float lss_center = static_cast(std::max(lss * 0.1, 0.02)); std::unordered_set added; visualization_msgs::msg::Marker left_line_strip; @@ -1151,10 +1153,11 @@ void visualization::pushLineStringMarker( } for (auto i = ls.begin(); i + 1 != ls.end(); i++) { geometry_msgs::msg::Point p; - const float heading = std::atan2((*(i + 1)).y() - (*i).y(), (*(i + 1)).x() - (*i).x()); + const float heading = + static_cast(std::atan2((*(i + 1)).y() - (*i).y(), (*(i + 1)).x() - (*i).x())); - const float x_offset = lss * 0.5 * std::sin(heading); - const float y_offset = lss * 0.5 * std::cos(heading); + const float x_offset = static_cast(lss * 0.5 * std::sin(heading)); + const float y_offset = static_cast(lss * 0.5 * std::cos(heading)); p.x = (*i).x() + x_offset; p.y = (*i).y() - y_offset; @@ -1233,7 +1236,8 @@ void visualization::pushArrowsMarker( return; } for (auto i = ls.begin(); i + 1 != ls.end(); i++) { - const float heading = std::atan2((*(i + 1)).y() - (*i).y(), (*(i + 1)).x() - (*i).x()); + const float heading = + static_cast(std::atan2((*(i + 1)).y() - (*i).y(), (*(i + 1)).x() - (*i).x())); const float sin_offset = std::sin(heading); const float cos_offset = std::cos(heading); diff --git a/tmp/lanelet2_extension/test/src/test_message_conversion.cpp b/tmp/lanelet2_extension/test/src/test_message_conversion.cpp index 2ec88ab0..f1d930c2 100644 --- a/tmp/lanelet2_extension/test/src/test_message_conversion.cpp +++ b/tmp/lanelet2_extension/test/src/test_message_conversion.cpp @@ -39,7 +39,9 @@ class TestSuite : public ::testing::Test // NOLINT for gtest Point3d p5; Point3d p6; Point3d p7; - LineString3d traffic_light_base, traffic_light_bulbs, stop_line; + LineString3d traffic_light_base; + LineString3d traffic_light_bulbs; + LineString3d stop_line; p1 = Point3d(getId(), 0., 0., 0.); p2 = Point3d(getId(), 0., 1., 0.); diff --git a/tmp/lanelet2_extension/test/src/test_query.cpp b/tmp/lanelet2_extension/test/src/test_query.cpp index 090f7cba..874efb34 100644 --- a/tmp/lanelet2_extension/test/src/test_query.cpp +++ b/tmp/lanelet2_extension/test/src/test_query.cpp @@ -55,8 +55,17 @@ class TestSuite : public ::testing::Test // NOLINT for gtest lanelet::AttributeValueString::Crosswalk; // create sample traffic light - Point3d p5, p6, p7, p8, p9, p10, p11, p12; - LineString3d traffic_light_base, traffic_light_bulbs, stop_line; + Point3d p5; + Point3d p6; + Point3d p7; + Point3d p8; + Point3d p9; + Point3d p10; + Point3d p11; + Point3d p12; + LineString3d traffic_light_base; + LineString3d traffic_light_bulbs; + LineString3d stop_line; p6 = Point3d(getId(), 0., 1., 4.); p7 = Point3d(getId(), 1., 1., 4.); diff --git a/tmp/lanelet2_extension/test/src/test_regulatory_elements.cpp b/tmp/lanelet2_extension/test/src/test_regulatory_elements.cpp index dce43181..7037e951 100644 --- a/tmp/lanelet2_extension/test/src/test_regulatory_elements.cpp +++ b/tmp/lanelet2_extension/test/src/test_regulatory_elements.cpp @@ -56,7 +56,8 @@ TEST(TestSuite, FactoryConstructsTrafficLight) // NOLINT for gtest Point3d p6; Point3d p7; LineStringOrPolygon3d traffic_light_base; - LineString3d traffic_light_bulbs, stop_line; + LineString3d traffic_light_bulbs; + LineString3d stop_line; p1 = Point3d(getId(), 0., 1., 4.); p2 = Point3d(getId(), 1., 1., 4.); From ecfb44b5c563cfc5d17e00f00c46fdcdc291f510 Mon Sep 17 00:00:00 2001 From: Kenji Miyake Date: Wed, 3 Aug 2022 08:41:22 +0900 Subject: [PATCH 34/36] fix clang-tidy warnings Signed-off-by: Kenji Miyake --- tmp/lanelet2_extension/lib/visualization.cpp | 9 +++++---- tmp/lanelet2_extension/src/validation.cpp | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tmp/lanelet2_extension/lib/visualization.cpp b/tmp/lanelet2_extension/lib/visualization.cpp index eb728a5d..9543a9de 100644 --- a/tmp/lanelet2_extension/lib/visualization.cpp +++ b/tmp/lanelet2_extension/lib/visualization.cpp @@ -359,6 +359,7 @@ void visualization::lanelet2Triangle( polygon2Triangle(ll_poly, triangles); } +// NOLINTNEXTLINE(readability-function-cognitive-complexity) void visualization::polygon2Triangle( const geometry_msgs::msg::Polygon & polygon, std::vector * triangles) { @@ -1153,11 +1154,11 @@ void visualization::pushLineStringMarker( } for (auto i = ls.begin(); i + 1 != ls.end(); i++) { geometry_msgs::msg::Point p; - const float heading = + const auto heading = static_cast(std::atan2((*(i + 1)).y() - (*i).y(), (*(i + 1)).x() - (*i).x())); - const float x_offset = static_cast(lss * 0.5 * std::sin(heading)); - const float y_offset = static_cast(lss * 0.5 * std::cos(heading)); + const auto x_offset = static_cast(lss * 0.5 * std::sin(heading)); + const auto y_offset = static_cast(lss * 0.5 * std::cos(heading)); p.x = (*i).x() + x_offset; p.y = (*i).y() - y_offset; @@ -1236,7 +1237,7 @@ void visualization::pushArrowsMarker( return; } for (auto i = ls.begin(); i + 1 != ls.end(); i++) { - const float heading = + const auto heading = static_cast(std::atan2((*(i + 1)).y() - (*i).y(), (*(i + 1)).x() - (*i).x())); const float sin_offset = std::sin(heading); diff --git a/tmp/lanelet2_extension/src/validation.cpp b/tmp/lanelet2_extension/src/validation.cpp index 814824c3..353f7bf3 100644 --- a/tmp/lanelet2_extension/src/validation.cpp +++ b/tmp/lanelet2_extension/src/validation.cpp @@ -71,6 +71,7 @@ void validateElevationTag(const std::string & filename) } } +// NOLINTNEXTLINE(readability-function-cognitive-complexity) void validateTrafficLight(const lanelet::LaneletMapPtr lanelet_map) { if (!lanelet_map) { @@ -79,7 +80,7 @@ void validateTrafficLight(const lanelet::LaneletMapPtr lanelet_map) } for (const auto & lanelet : lanelet_map->laneletLayer) { - auto autoware_traffic_lights = + const auto autoware_traffic_lights = lanelet.regulatoryElementsAs(); if (autoware_traffic_lights.empty()) { continue; From d0dda710c2d43e03eb509c9b7319094b5dd8ce3d Mon Sep 17 00:00:00 2001 From: Kenji Miyake Date: Wed, 3 Aug 2022 09:18:13 +0900 Subject: [PATCH 35/36] fix for cpplint Signed-off-by: Kenji Miyake --- tmp/lanelet2_extension/lib/visualization.cpp | 3 ++- tmp/lanelet2_extension/src/validation.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tmp/lanelet2_extension/lib/visualization.cpp b/tmp/lanelet2_extension/lib/visualization.cpp index 9543a9de..aa867680 100644 --- a/tmp/lanelet2_extension/lib/visualization.cpp +++ b/tmp/lanelet2_extension/lib/visualization.cpp @@ -359,7 +359,7 @@ void visualization::lanelet2Triangle( polygon2Triangle(ll_poly, triangles); } -// NOLINTNEXTLINE(readability-function-cognitive-complexity) +// NOLINTBEGIN(readability-function-cognitive-complexity) void visualization::polygon2Triangle( const geometry_msgs::msg::Polygon & polygon, std::vector * triangles) { @@ -453,6 +453,7 @@ void visualization::polygon2Triangle( is_acute_angle.at(i_prev) = isAcuteAngle(p0, p1, p2); } } +// NOLINTEND(readability-function-cognitive-complexity) void visualization::lanelet2Polygon( const lanelet::ConstLanelet & ll, geometry_msgs::msg::Polygon * polygon) diff --git a/tmp/lanelet2_extension/src/validation.cpp b/tmp/lanelet2_extension/src/validation.cpp index 353f7bf3..4b087141 100644 --- a/tmp/lanelet2_extension/src/validation.cpp +++ b/tmp/lanelet2_extension/src/validation.cpp @@ -71,7 +71,7 @@ void validateElevationTag(const std::string & filename) } } -// NOLINTNEXTLINE(readability-function-cognitive-complexity) +// NOLINTBEGIN(readability-function-cognitive-complexity) void validateTrafficLight(const lanelet::LaneletMapPtr lanelet_map) { if (!lanelet_map) { @@ -114,6 +114,7 @@ void validateTrafficLight(const lanelet::LaneletMapPtr lanelet_map) } } } +// NOLINTEND(readability-function-cognitive-complexity) void validateTurnDirection(const lanelet::LaneletMapPtr lanelet_map) { From f5771f4d9006e35627b65b0cb6087dcd4643233f Mon Sep 17 00:00:00 2001 From: Kenji Miyake Date: Wed, 3 Aug 2022 21:45:20 +0900 Subject: [PATCH 36/36] apply autowarefoundation/autoware.universe#1506 Signed-off-by: Kenji Miyake --- tmp/lanelet2_extension/lib/utilities.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tmp/lanelet2_extension/lib/utilities.cpp b/tmp/lanelet2_extension/lib/utilities.cpp index 99685449..808f8f5a 100644 --- a/tmp/lanelet2_extension/lib/utilities.cpp +++ b/tmp/lanelet2_extension/lib/utilities.cpp @@ -148,6 +148,7 @@ std::vector resamplePoints( // Calculate accumulated lengths const auto accumulated_lengths = calculateAccumulatedLengths(line_string); + if (accumulated_lengths.size() < 2) return {}; // Create each segment std::vector resampled_points;