01_dealing_with_lanelet_primitives/main.cpp
Go to the documentation of this file.
2 #include <lanelet2_core/geometry/Area.h>
3 #include <lanelet2_core/geometry/Lanelet.h>
4 #include <lanelet2_core/primitives/Area.h>
5 #include <lanelet2_core/primitives/Lanelet.h>
6 #include <lanelet2_core/primitives/LineString.h>
7 #include <lanelet2_core/primitives/Point.h>
8 #include <lanelet2_core/primitives/Polygon.h>
10 
12 
13 // we want assert statements to work in release mode
14 #undef NDEBUG
15 
16 #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
17 #pragma GCC diagnostic ignored "-Wunused-variable"
18 
19 // we compare floats a couple of times and know this is save in this context
20 #pragma GCC diagnostic ignored "-Wfloat-equal"
21 
22 void part0Primitives();
23 void part1Points();
24 void part2LineStrings();
25 void part3Polygons();
26 void part4Lanelets();
27 void part5Areas();
28 void part6Geometry();
29 
30 int main() {
31  // this tutorial is divided into 6 parts that teach you about all the primitives, one by one and finally how to use
32  // them for geometry calculations.
34  part1Points();
36  part3Polygons();
37  part4Lanelets();
38  part5Areas();
39  part6Geometry();
40  return 0;
41 }
42 
44  using namespace lanelet;
45  using namespace lanelet::units::literals; // this enables us to use _kmh for velocities
46  // there are many concepts that are similar across all primitives of lanelet2. Once you have understood one of
47  // them, you can also understand the rest.
48 
49  // Here is how you create a point in 3d with an Id and the x, y and z coordinates. We will go into details on points
50  // later. The getId() function gives us a unique id so that we can not accidentally have the same id twice. This
51  // would have strange effects once we add these points to a LaneletMap. Every lanelet object has such a unique Id.
52  Point3d p(utils::getId(), 0, 0, 0);
53 
54  // all primitives can easily be copied. Since Lanelet2 data is unique, these copies simply share the same data.
55  Point3d pCopy = p;
56  assert(p == pCopy);
57 
58  // p is mutable, meaning we can change everything we passed at construction time.
59  p.z() = 2;
60  p.setId(utils::getId());
61 
62  // since pCopy shares the data, we modified it as well. This is one of Lanelet2s basic principles to keep data
63  // consistent across the map.
64  assert(p.id() == pCopy.id());
65  assert(p.z() == pCopy.z());
66 
67  // all primitives have attributes in form of key-value pairs. These are of course also shared between copies.
68  // attributes can be strings, doubles, ids or even velocities (with units). In the background lanelet2 tries to
69  // convert them to the respective type and returns it if that was succssful.
70  p.attributes()["type"] = "point";
71  p.attributes()["pi"] = 3.14;
72  p.attributes()["velocity"] = "5 kmh";
73  assert(p.attributes() == pCopy.attributes());
74  assert(p.attribute("type") == "point");
75  assert(!!p.attribute("pi").asDouble()); // returns an optional value that is only valid if conversion was sucessful
76  assert(p.attribute("velocity").asVelocity() == 5_kmh);
77 
78  // when you are not sure an attribute exists, you can use "attributeOr" for convenience. When the attribute does not
79  // exist or can not be converted to the type you passed as default value, the default value is returned:
80  assert(p.attributeOr("nonexistent", -1) == -1);
81  assert(p.attributeOr("type", 0) == 0); // "point" can not be converted to a number
82  assert(p.attributeOr("velocity", 0_kmh) == 5_kmh);
83 
84  // all primitives have a const version, where the data can only be read but not modified. Similar to the copies,
85  // they share a view on the actual data. The operation to create them is very cheap, because this actually only
86  // copies a shared pointer. The const version for our Point3d is ConstPoint3d.
87  ConstPoint3d pConst = p;
88 
89  // the const point has the same functions for accessing data, but can not modify it
90  assert(pConst.z() == 2);
91  // pConst.z() = 3; // impossible
92 
93  // still the const version can observe changes:
94  p.z() = 3;
95  assert(pConst.z() == 3);
96  // actually, and this is more advanced, we do not even have to make a copy, because internally Point3d inherits
97  // from ConstPoint3d
98  ConstPoint3d& pConstRef = p;
99  // pConstRef is now a reference to the const part of the point:
100  assert(pConstRef.z() == 3);
101 
102  // we can convert things to const, but we can not get back. Once it is const, there is no way to get the const away.
103  // This is important, because any function that takes a const object is guaranteed to be unable to modify it,
104  // no matter what.
105 
106  // Point3d pNonConst = pConst; // not possible
107 
108  // we can also access the underlying data (this is the thing that the point copies share). However this is not
109  // really meant for daily usage:
110  assert(p.constData() == pConst.constData());
111 }
112 
113 void part1Points() {
114  using namespace lanelet;
115  // this is a normal point with x=1, y=2, z=3.
116  Point3d p3d(utils::getId(), 1, 2, 3);
117 
118  // we can access the indices as usual for a point:
119  assert(p3d.x() == 1);
120  assert(p3d.y() == 2);
121  assert(p3d.z() == 3);
122 
123  // we can also get access to a BasicPoint which is actually an Eigen point and can be used in time critical
124  // computations:
125  BasicPoint3d& p3dBasic = p3d.basicPoint();
126 
127  // modifying this reference to the basic point also modifies the actual point data:
128  p3dBasic.z() = 4;
129  assert(p3d.z() == 4);
130  // with this eigen point we can do the usual computations:
131  BasicPoint3d pTwice = p3d.basicPoint() * 2;
132  assert(pTwice.z() == 8);
133 
134  // now the interesting part, where we convert to 2d. There is a utility function that generically gives us the
135  // matching 2D type for any lanelet primitive:
136  Point2d p2d = utils::to2D(p3d);
137 
138  // a 2d point works in the same way like a 3d point, but no longer has a z-coordinate. But the important thing is:
139  // it still shares the same data as p3d. The conversion is as efficient as any copy in lanelet2, because we just
140  // copy the data pointer. If we now modify something in 2d we also modify it in p3d:
141  p2d.x() = 3;
142  assert(p3d.x() == 3);
143  assert(p3d.constData() == p2d.constData()); // this is because they share the same underlying data.
144 
145  // the conversion works also from 2d to 3d without losing anything. The z coordinate is always there in the
146  // underlying data:
147  Point3d p3dNew = utils::to3D(p2d);
148  assert(p3dNew.z() == p3d.z());
149  assert(p3dNew.constData() == p3d.constData()); // still the same underlying data
150 }
151 
153  using namespace lanelet;
154  // LineStrings are created from a set of points with linear interpolation between them.
155  Point3d p1{utils::getId(), 0, 0, 0};
156  Point3d p2{utils::getId(), 1, 0, 0};
157  Point3d p3{utils::getId(), 2, 0, 0};
158  LineString3d ls(utils::getId(), {p1, p2, p3});
159 
160  // conceptually, linestrings are similar to an std::vector. You can access individal points or loop over them.
161  assert(ls[1] == p2);
162  assert(ls.size() == 3);
163  for (Point3d& p : ls) {
164  assert(p.y() == 0);
165  }
166  ls.push_back(Point3d(utils::getId(), 3, 0, 0));
167  assert(ls.size() == 4);
168  ls.pop_back();
169  assert(ls.size() == 3);
170 
171  // of course, linestrings also have attributes. This will become interesting if we infer traffic rules. The most
172  // common tags and values are predefined so that you don't have to fear typos:
175 
176  // instead of looping over individual points, you can also loop over individual segments. A segment consists of two
177  // neighbouring points in the LineString:
178  assert(ls.numSegments() >= 2);
179  Segment3d segment = ls.segment(1);
180  assert(segment.first == p2);
181  assert(segment.second == p3);
182 
183  // you can also invert a linestring. Inverted linestrings still share the same data but points are returned in
184  // reversed order:
185  LineString3d lsInv = ls.invert();
186  assert(lsInv.front() == p3);
187  assert(lsInv.inverted()); // inverted is the only way to find out we are actually dealing with an inverted ls
188  assert(lsInv.constData() == ls.constData()); // still the same data
189 
190  // inverting an inverted linestring gives you the original one:
191  assert(lsInv != ls);
192  LineString3d lsInvInv = lsInv.invert();
193  assert(lsInvInv == ls);
194  assert(lsInvInv.front() == p1);
195 
196  // there is also a const version
197  ConstLineString3d lsConst = ls;
198  // lsConst.pop_back() // not possible
199  ConstPoint3d p1Const = ls[0]; // const linestrings return const points
200  assert(lsConst.constData() == ls.constData());
201 
202  // and a 2d version. It behaves exactly as the 3d version, but returns 2d points:
203  LineString2d ls2d = utils::to2D(ls);
204  Point2d front2d = ls2d.front();
205  assert(front2d == utils::to2D(p1));
206 
207  // there is also a hybrid version. This is a bit special, because it returns BasicPoints (aka Eigen points). This
208  // makes it the perfect fit for geometry calculations because they work well with boost.geometry (we will look into
209  // that later)
210  ConstHybridLineString3d lsHybrid = utils::toHybrid(ls);
211  BasicPoint3d p1Basic = lsHybrid[0];
212  assert(p1Basic.x() == p1.x());
213 
214  // you can also get a BasicLineString, which is just a vector of Eigen points. These are also usable within
215  // boost.geometry. This is the only conversion operation where actual copying is done in the background:
216  BasicLineString3d lsBasic = ls.basicLineString();
217  assert(lsBasic.front() == lsHybrid.front());
218 }
219 
221  using namespace lanelet;
222  // polygons are very, very similar to linestrings. the only actual difference is that there is a last, implicit
223  // segment between the last and the first point that closes the polygon. The interface is basically the same:
224  Point3d p1{utils::getId(), 0, 0, 0};
225  Point3d p2{utils::getId(), 1, 0, 0};
226  Point3d p3{utils::getId(), 2, -1, 0};
227  Polygon3d poly(utils::getId(), {p1, p2, p3});
228 
229  assert(poly.size() == 3);
230  assert(poly.numSegments() == 3); // not 2, see?
231  Segment3d lastSegment = poly.segment(2);
232  assert(lastSegment.first == p3);
233  assert(lastSegment.second == p1);
234 
235  // one more difference: you cannot invert polygons. That would not make sense. Polygons are always in clockwise
236  // orientation.
237 
238  // poly.invert(); // no.
239 
240  // there are all the types that you already know from linestrings, const, 2d, 3d, hybrid:
241  ConstPolygon3d polyConst = poly;
242  ConstHybridPolygon3d polyHybrid = utils::toHybrid(poly);
243  ConstPolygon2d poly2dConst = utils::to2D(polyConst);
244  assert(polyHybrid.constData() == poly2dConst.constData());
245 }
246 
248  using namespace lanelet;
249  // Technically, lanelets are not very special, they have a left and a right bound. You already know how to create
250  // them, so lets skip that.
253 
255  assert(lanelet.leftBound() == left);
256  assert(lanelet.rightBound() == right);
257  lanelet.setLeftBound(left); // we can also change that linestring
258 
259  // lanelets also have a centerline. By default, the lanelet computes it for you. It is const because it should not
260  // be modified.
261  ConstLineString3d centerline = lanelet.centerline();
262 
263  // the centerline is cached, because computation is not so cheap. When we set a new boundary, the cache is cleared
264  ConstLineString3d centerline2 = lanelet.centerline(); // from the cache
265  assert(centerline2 == centerline);
266  lanelet.setLeftBound(examples::getLineStringAtY(2));
267  ConstLineString3d centerline3 = lanelet.centerline();
268  assert(centerline3 != centerline); // new centerline
269 
270  // however there is one limitation. If you modify one of the bounds, the lanelet does not notice it. You have to
271  // reset the cache yourself.
272  right.push_back(Point3d(utils::getId(), 4, 0, 0));
273  assert(centerline3 == lanelet.centerline()); // centerline is still the same, which is wrong.
274  lanelet.resetCache();
275  assert(centerline3 != lanelet.centerline()); // new centerline is computed
276  right.pop_back();
277  lanelet.resetCache();
278 
279  // lanelets can also be inverted. This is used to deal with lanelets that are drivable in both ways. Inverted
280  // lanelets have left and right bounds flipped and inverted. Shared data is still the same:
281  Lanelet laneletInv = lanelet.invert();
282  assert(laneletInv.leftBound().front() == lanelet.rightBound().back());
283  assert(laneletInv.constData() == lanelet.constData());
284 
285  // when you deal with lanelets geometrically, you need their outline (a polygon). You haven't met the
286  // CompoundPolygon type yet, but it behaves like a single polygon while it is actually composed of multiple
287  // linestrings that together form the bound (in clockwise order again).
288  CompoundPolygon3d polygon = lanelet.polygon3d();
289  assert(polygon.size() == 6); // both boundaries have 3 points
290  assert(polygon[0] == lanelet.leftBound().front()); // the polygon starts at the first point of the left bound
291  assert(polygon.back() == lanelet.rightBound().front()); // and ends at the start of the right bound
292 
293  // there is also a const version, but no 3d or 2d version (lanelets are "dimensionless").
294  ConstLanelet laneletConst = lanelet;
295  assert(laneletConst.constData() == lanelet.constData());
296  ConstLineString3d leftConst = lanelet.leftBound(); // the bounds are now const as well
297  // laneletConst.setLeftBound(left); // no
298 
299  // lanelets hold regulatory elements but we will get to that in the next example
300  assert(lanelet.regulatoryElements().empty());
301 }
302 
303 void part5Areas() {
304  using namespace lanelet;
305  // areas are similar to lanelets in their design. But instead of left and right bound they have set of lineStrings
306  // that are connected and together form the outer bound. They must be in clockwise order
311  Area area(utils::getId(), {top, right, bottom, left});
312 
313  // you can get the outer bounds
314  LineStrings3d outer = area.outerBound();
315  // or set them
316  area.setOuterBound(outer);
317 
318  // you can also get a polygon for the outer bounds, similar to a lanelet:
319  CompoundPolygon3d outerPolygon = area.outerBoundPolygon();
320 
321  // areas can also have holes (they work similar to the outer bounds but are in counter-clockwise order). Hovewer
322  // they are rarely used.
323  assert(area.innerBounds().empty());
324 
325  // areas can be const
326  ConstArea areaConst = area;
327  ConstLineStrings3d outerConst = areaConst.outerBound(); // now the outer bound linestrings are also const
328 
329  // areas also hold regulatory elements
330  assert(area.regulatoryElements().empty());
331 }
332 
334  using namespace lanelet;
335  // lanelet2 allows lots of geometry calculations with all kinds of primitives. This tutorial only shows some of them
336  // to show you the basic idea. There are many more algorithms that you can find in the geometry folder in
337  // lanelet2_core.
338 
339  // the primitives we are going to work with
340  Point3d point(utils::getId(), 1, 4, 1);
341  LineString3d ls = examples::getLineStringAtY(2); // linestring that goes from (0,2,0) to (2,2,0)
342  Polygon3d poly = examples::getAPolygon(); // polygon with points (0,0,0), (2,0,0), (2, -2, 0)
343  Lanelet lanelet = examples::getALanelet(); // lanelet that goes from x=0 to x=3 and extends from y=2 to y=0
344  Area area = examples::getAnArea(); // quadratic area with the edge points (0,0,0) and (2,2,0)
345 
346  // most computations with linestrings and polygons are better done with ther hybrid types (see the doc for more info
347  // on this).
348  ConstHybridLineString3d lsHybrid = utils::toHybrid(ls);
349  ConstHybridPolygon3d polyHybrid = utils::toHybrid(poly);
350 
351  // the points, linestrings and polygons directly interface with boost.geometry:
352  auto dP2Line3d = geometry::distance(point, lsHybrid);
353  // we used 3d objects so the result will also be in 3d.
354  assert(dP2Line3d > 2);
355  // to calculate in 2d, transform the objects to 2d
356  auto dP2Line2d = geometry::distance(utils::to2D(point), utils::to2D(lsHybrid));
357  assert(dP2Line2d == 2);
358 
359  // other algorithms would be the length or the area
360  auto l3d = geometry::length(lsHybrid);
361  assert(l3d == 2);
362  auto ar = geometry::area(utils::to2D(polyHybrid)); // not defined in 3d
363  assert(ar == 2);
364 
365  // lanelet2 provides more algorithms where boost.geometry lacks inplementations. For those implementations you don't
366  // have to use the hybrid type:
367  BasicPoint3d pProj = geometry::project(ls, point); // gets the projecion of point on ls
368  assert(pProj.y() == 2);
369 
370  ArcCoordinates arcCoordinates =
371  geometry::toArcCoordinates(utils::to2D(ls), utils::to2D(point)); // transforms the point into arc coordinates
372  assert(arcCoordinates.distance == 2); // signed distance to linestring
373  assert(arcCoordinates.length == 1); // length along linestring
374 
375  // bounding boxes can be calculated for all types:
376  BoundingBox3d pointBox = geometry::boundingBox3d(point); // trivial box for points
379  BoundingBox2d areaBox = geometry::boundingBox2d(area);
380 
381  // we can now use these boxes for efficient intersection estimation. Intersects returns true also if the boxes only
382  // touch but the shared area is still 0.
383  assert(geometry::intersects(laneletBox, areaBox));
384  assert(!geometry::intersects(pointBox, lsBox));
385 
386  // if you want more than an estimation you can use the primitives directly. Overlaps only returns true if the shared
387  // area of the two primitives is >0. In 3d, the distance in z must also be smaller than a margin.
388  assert(geometry::overlaps3d(area, lanelet, 3));
389 }
ConstLineStrings3d
std::vector< ConstLineString3d > ConstLineStrings3d
lanelet::ArcCoordinates::length
double length
LineStrings3d
std::vector< LineString3d > LineStrings3d
lanelet::geometry::boundingBox3d
IfAr< AreaT, BoundingBox3d > boundingBox3d(const AreaT &area)
main
int main()
Definition: 01_dealing_with_lanelet_primitives/main.cpp:30
lanelet::Point3d::x
double & x() noexcept
lanelet::Point3d::y
double & y() noexcept
lanelet::Point3d::z
double & z() noexcept
lanelet::examples::getAPolygon
Polygon3d getAPolygon()
Definition: ExampleHelpers.h:17
lanelet::CompoundPolygon3d
LaneletMap.h
lanelet
lanelet::geometry::project
BasicPoint2d project(const BasicSegment2d &segment, const BasicPoint2d &pointToProject)
BasicPoint3d
Eigen::Vector3d BasicPoint3d
lanelet::ConstArea
lanelet::ConstHybridPolygon3d
p2
BasicPoint p2
part2LineStrings
void part2LineStrings()
Definition: 01_dealing_with_lanelet_primitives/main.cpp:152
part0Primitives
void part0Primitives()
Definition: 01_dealing_with_lanelet_primitives/main.cpp:43
p
BasicPoint p
lanelet::utils::getId
Id getId()
lanelet::Point2d
part3Polygons
void part3Polygons()
Definition: 01_dealing_with_lanelet_primitives/main.cpp:220
lanelet::AttributeValueString::LineThin
static constexpr const char LineThin[]
lanelet::ConstPolygon3d
lanelet::ConstPoint3d::z
double z() const noexcept
lanelet::examples::getALanelet
Lanelet getALanelet()
Definition: ExampleHelpers.h:32
Primitive< ConstPoint3d >::attributes
AttributeMap & attributes() noexcept
ConstLineStringImpl< Point3d >::inverted
bool inverted() const noexcept
lanelet::Area::outerBound
const LineStrings3d & outerBound()
lanelet::ArcCoordinates::distance
double distance
lanelet::ArcCoordinates
right
BasicPolygon3d right
Segment3d
Segment< Point3d > Segment3d
lanelet::BoundingBox2d
Units.h
lanelet::Area
lanelet::LineString2d
ConstPrimitive< PointData >::constData
const std::shared_ptr< const PointData > & constData() const
lanelet::ConstPoint3d
part5Areas
void part5Areas()
Definition: 01_dealing_with_lanelet_primitives/main.cpp:303
lanelet::Lanelet
lanelet::AttributeName::Type
@ Type
BoundingBox3d
Eigen::AlignedBox3d BoundingBox3d
part1Points
void part1Points()
Definition: 01_dealing_with_lanelet_primitives/main.cpp:113
lanelet::Point2d::x
double & x() noexcept
lanelet::LineString3d::invert
LineString3d invert() const noexcept
LineStringImpl< ConstLineString3d >::front
PointType & front()
p1
BasicPoint p1
lanelet::examples::getLineStringAtY
LineString3d getLineStringAtY(double y)
Definition: ExampleHelpers.h:12
ExampleHelpers.h
part6Geometry
void part6Geometry()
Definition: 01_dealing_with_lanelet_primitives/main.cpp:333
lanelet::geometry::overlaps3d
IfAr< AreaT, bool > overlaps3d(const AreaT &area, const AreaT &otherArea, double heightTolerance)
lanelet::Point3d
left
BasicPolygon3d left
lanelet::ConstPolygon2d
part4Lanelets
void part4Lanelets()
Definition: 01_dealing_with_lanelet_primitives/main.cpp:247
lanelet::ConstHybridLineString3d::front
const BasicPointType & front() const noexcept
lanelet::geometry::boundingBox2d
IfAr< AreaT, BoundingBox2d > boundingBox2d(const AreaT &area)
lanelet::geometry::toArcCoordinates
ArcCoordinates toArcCoordinates(const LineString2dT &lineString, const BasicPoint2d &point)
lanelet::ConstLineString3d
CompoundLineStringImpl< ConstPoint3d >::size
size_t size() const noexcept
lanelet::Polygon3d
lanelet::units::literals
lanelet::ConstArea::outerBound
ConstLineStrings3d outerBound() const
lanelet::AttributeValueString::Dashed
static constexpr const char Dashed[]
lanelet::ConstLanelet
CompoundLineStringImpl< ConstPoint3d >::back
const PointType & back() const noexcept
lanelet::ConstHybridLineString3d
lanelet::Lanelet::leftBound
LineString3d leftBound()
lanelet::examples::getAnArea
Area getAnArea()
Definition: ExampleHelpers.h:24
lanelet::examples::getLineStringAtX
LineString3d getLineStringAtX(double x)
Definition: ExampleHelpers.h:7
lanelet::Point3d::basicPoint
const BasicPoint & basicPoint() const noexcept
lanelet::LineString3d
lanelet::AttributeName::Subtype
@ Subtype
ConstPrimitive< PointData >::id
Id id() const noexcept
BasicLineString3d
BasicPoints3d BasicLineString3d


lanelet2_examples
Author(s): Fabian Poggenhans
autogenerated on Thu Mar 6 2025 03:26:15