]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/geometry/doc/example_adapting_a_legacy_geometry_object_model.qbk
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / boost / libs / geometry / doc / example_adapting_a_legacy_geometry_object_model.qbk
CommitLineData
7c673cae
FG
1[section Example: Adapting a legacy geometry object model]
2
3One of the primary benefits of __boost_geometry__, and the reason for its fairly complex template-based implementation, is that it allows for integration with legacy classes/objects.
4
5By defining the relationship between the __boost_geometry__ concepts and an existing, legacy object model, the legacy objects can be used in place of __boost_geometry__'s own geometry classes.
6
7__boost_geometry__ will then happliy read and write directly from and to the legacy object, treating it as a native __boost_geometry__ object.
8
9This means that one can adapt algorithms and methods from __boost_geometry__ to any existing legacy geometry object model at a very small runtime cost, which is simply not possible with most geometry libraries, where one has to make an intermediate object specific to the geometry library one is using.
10
11The following example will demonstrate the adaption process of a legacy geometry object model for use with __boost_geometry__.
12
13[h2 Adapting a shared geometry legacy object model]
14
15[h3 Example code: object hierarcy]
16
17 class QPoint
18 {
19 public:
20 double x;
21 double y;
22 QPoint(double x, double y) : x(x), y(y) {}
23 };
24
25 class QLineString
26 {
27 public:
28 bool cw;
29 std::vector<QPoint*> points;
30 };
31
32 class QRing
33 {
34 public:
35 std::vector<QLineString*> lines;
36 };
37
38 class QPolygon
39 {
40 public:
41 QRing* exterior;
42 std::vector<QRing*> interiors;
43 };
44
45The legacy object hierarcy is based on topology (e.g. two QRings might share one QLineString) instead of points directly (i.e. each object does not point directly to it's QPoints), and it also uses pointers for access.
46
47This is the other common way to approach geometries, to enable e.g. shared boundaries between surfaces. __boost_geometry__'s approach use simple features, and does not have shared geometries.
48
49The mismatch in representation is fixed by creating a custom iterator, that exposes a __boost_range__ of Points for every object. This way, __boost_geometry__'s functions will operate on the QRing as if it was a collection of Points, which is a requirement.
50
51[h2 Adapting QPoint]
52
53The [link adaption_of_qpoint_source_code adaption of the QPoint] is fairly straightforward, one just needs to implement the requirements.
54
55Even though the geometries in our legacy object model use pointers of QPoints, __boost_geometry__ automatically handles the conversion from pointers-to-Points to references-to-Points internally, so we do not have to convert them manually.
56
57Alternatively, we can use the [link geometry.reference.adapted.register.boost_geometry_register_point_2d BOOST_GEOMETRY_REGISTER_POINT_2D(QPoint, double, cs::cartesian, x, y)] helper macro, which does exactly the same as our manual adaption.
58
59The sample code adapts QPoint to the [link geometry.reference.concepts.concept_point Point Concept] using specialization of the traits class.
60
61[h2 Adapting QLineString]
62
63The [link adaption_of_qlinestring_source_code adaption of the QLineString] is very simple on the surface, as it is just "a specialization of traits::tag defining linestring_tag as type". Alternatively, we can use the [link geometry.reference.adapted.register.boost_geometry_register_linestring BOOST_GEOMETRY_REGISTER_LINESTRING(QLineString)] helper macro, which does exactly the same as our manual adaption.
64
65However, the [link geometry.reference.concepts.concept_linestring LineString concept] also requires that the collection of Points "must behave like a __boost_range__ Random Access Range" and "the type defined by the metafunction range_value<...>::type must fulfill the Point Concept".
66
67This means that we have to do two things:
68
69* Make QLineString behave like a __boost_range__, with Random Access requirements
70* Make sure that the __boost_range__ iterates over QPoints, which we already have adapted
71
72This might look like a lot of work, but we are in luck: a std::vector is nearly a __boost_range__, and already iterate over pointers-to-QPoints, that are handled by __boost_geometry__. The [link adaption_of_qlinestring_range_source_code code for making QLineString a __boost_range__] is therefore fairly straightforward.
73
74[h2 Adapting QRing]
75
76The [link adaption_of_qring_source_code adaption of the QRing] is mostly equal to the QLineString in that there is a tag and a collection to iterate through. Alternatively, we can use the [link geometry.reference.adapted.register.boost_geometry_register_ring BOOST_GEOMETRY_REGISTER_RING(QRing)] helper macro, which does exactly the same as our manual adaption.
77
78However, the QRing expose pointers-to-QLineStrings, and not QPoints directly, which is [link geometry.reference.concepts.concept_ring required in the Ring concept], so it is not enough to trivially make the std::vector into a __boost_range__. We need to create a Boost.Iterator that expose QPoints, and because we are dealing with a legacy object model, we are not allowed to change the class definition.
79
80The [link adaption_of_qring_iterator_source_code custom iterator that does this] uses Boost.Iterator Facade, and is not very different from the [@http://www.boost.org/doc/libs/1_53_0/libs/iterator/doc/iterator_facade.html example provided in Boost.Iterator's own documentation](link), except that our __boost_range__ need to be random access.
81
82Now, with the custom iterator made, we can [link adaption_of_qring_range_source_code define the __boost_range__] that traverses through QPoints.
83
84[h2 Adapting QPolygon]
85
86[link adaption_of_qpolygon_source_code Adapting the QPolygon] to the [link geometry.reference.concepts.concept_polygon Polygon Concept] is a little more involved than the other geometry types.
87
88The only requirement that is not straightforward to adapt is the interior_rings' get method.
89
90A __boost_geometry__ Polygon operates on Ring objects, and unfortunately, __boost_geometry__ does not automatically handle the conversion from pointers to references for Rings internally (only Points, as mentioned).
91
92Therefore, we need to expose QRings instead of pointers-to-QRings for the interior Rings, which means a little more work than the pointers-to-QPoints for QLineString and QRing.
93
94First, we [link adaption_of_qpolygon_iterator_source_code create a Boost.Iterator Facade] that returns QRing instead of pointer-to-QRing:
95
96Now we have an iterator that can "convert" our pointer-to-QRing into QRing. However, the get method of the interior Rings must return a __boost_range__ compatible object, which a plain PolygonRingIterator is not.
97
98We need to [link adaption_of_qpolygon_range_source_code define another __boost_range__], that can be constructed with PolygonRingIterators as arguments, and returned from the get method.
99
100[h2 Conclusion]
101
102That's it! The methods of __boost_geometry__ can now be used directly on instances of our legacy object model.
103
104[endsect]
105
106[section Example source code: Adapting a legacy geometry object model]
107
108[h2 Adaption of QPoint]
109[#adaption_of_qpoint_source_code]
110
111 #include <boost/geometry.hpp>
112
113 namespace boost
114 {
115 namespace geometry
116 {
117 namespace traits
118 {
119 // Adapt QPoint to Boost.Geometry
120
121 template<> struct tag<QPoint>
122 { typedef point_tag type; };
123
124 template<> struct coordinate_type<QPoint>
125 { typedef QPoint::double type; };
126
127 template<> struct coordinate_system<QPoint>
128 { typedef cs::cartesian type; };
129
130 template<> struct dimension<QPoint> : boost::mpl::int_<2> {};
131
132 template<>
133 struct access<QPoint, 0>
134 {
135 static QPoint::double get(QPoint const& p)
136 {
137 return p.x;
138 }
139
140 static void set(QPoint& p, QPoint::double const& value)
141 {
142 p.x = value;
143 }
144 };
145
146 template<>
147 struct access<QPoint, 1>
148 {
149 static QPoint::double get(QPoint const& p)
150 {
151 return p.y;
152 }
153
154 static void set(QPoint& p, QPoint::double const& value)
155 {
156 p.y = value;
157 }
158 };
159 }
160 }
161 } // namespace boost::geometry::traits
162
163
164
165
166[h2 Adaption of QLineString]
167[#adaption_of_qlinestring_source_code]
168
169 namespace boost
170 {
171 namespace geometry
172 {
173 namespace traits
174 {
175 template<>
176 struct tag<QLineString>
177 {
178 typedef linestring_tag type;
179 };
180 }
181 }
182 } // namespace boost::geometry::traits
183
184[h3 Boost.Range for QLineString]
185[#adaption_of_qlinestring_range_source_code]
186
187 #include <boost/range.hpp>
188
189 namespace boost
190 {
191 template <>
192 struct range_iterator<QLineString>
193 { typedef std::vector<QPoint*>::iterator type; };
194
195 template<>
196 struct range_const_iterator<QLineString>
197 { typedef std::vector<QPoint*>::const_iterator type; };
198 }
199
200 inline std::vector<QPoint*>::iterator
201 range_begin(QLineString& qls) {return qls.points.begin();}
202
203 inline std::vector<QPoint*>::iterator
204 range_end(QLineString& qls) {return qls.points.end();}
205
206 inline std::vector<QPoint*>::const_iterator
207 range_begin(const QLineString& qls) {return qls.points.begin();}
208
209 inline std::vector<QPoint*>::const_iterator
210 range_end(const QLineString& qls) {return qls.points.end();}
211
212[h2 Adaption of QRing]
213[#adaption_of_qring_source_code]
214
215 namespace boost
216 {
217 namespace geometry
218 {
219 namespace traits
220 {
221 template<>
222 struct tag<QRing>
223 {
224 typedef ring_tag type;
225 };
226 }
227 }
228 } // namespace boost::geometry::traits
229
230[h3 Boost.Iterator for QRing]
231[#adaption_of_qring_iterator_source_code]
232
233 #include <boost/iterator/iterator_facade.hpp>
234
235 /* Custom iterator type that flattens a 2D array into a 1D array */
236 template <class I, // Line iterator type
237 class R // Point reference type
238 >
239 class RingIteratorImpl : public boost::iterator_facade<
240 RingIteratorImpl<I,R>, R, std::random_access_iterator_tag, R> //new traversal tag boost::random_access_traversal_tag
241 {
242 public:
243 RingIteratorImpl() : pointIndex_(0)
244 {
245 }
246
247 explicit RingIteratorImpl(I lineStringIterCurrent)
248 : lineStringIterCurrent_(lineStringIterCurrent), pointIndex_(0)
249 {
250 }
251
252 template<class OtherI, class OtherR>
253 RingIteratorImpl(RingIteratorImpl<OtherI, OtherR> const& other) :
254 lineStringIterCurrent_(other.getLineStrIt()), pointIndex_(other.getPointIdx())
255 {
256 }
257
258 I getLineStrIt() const {return lineStringIterCurrent_;}
259
260 bool isEmpty() const {return isEmpty;}
261 size_t getPointIdx() const {return pointIndex_;}
262
263 typedef typename boost::iterator_facade<RingIteratorImpl<I,R>, R, std::random_access_iterator_tag, R>::difference_type difference_type;
264
265 private:
266 friend class boost::iterator_core_access;
267
268 void increment()
269 {
270 ++pointIndex_;
271 if (pointIndex_ >= (*lineStringIterCurrent_)->points.size())
272 {
273 ++lineStringIterCurrent_;
274 pointIndex_ = 0;
275 }
276 }
277
278 void decrement()
279 {
280 if(pointIndex_>0)
281 {
282 --pointIndex_;
283 }
284 else
285 {
286 --lineStringIterCurrent_;
287 pointIndex_ = (*lineStringIterCurrent_)->points.size();
288 }
289 }
290
291 void advance(difference_type n)
292 {
293 difference_type counter = n;
294
295 difference_type maxPointIndex, remainderPointIndex;
296
297 while(counter>0)
298 {
299 maxPointIndex = (*lineStringIterCurrent_)->points.size(),
300 remainderPointIndex = maxPointIndex - pointIndex_;
301
302 if(counter>remainderPointIndex)
303 {
304 counter -= remainderPointIndex;
305 ++lineStringIterCurrent_;
306 }
307 else // (counter<=remainderPointIndex)
308 {
309 counter = 0;
310 pointIndex_ = remainderPointIndex;
311 }
312 }
313
314 }
315
316 difference_type distance_to(const RingIteratorImpl& other) const
317 {
318 I currentLineStringIter = getLineStrIt();
319 I otherLineStringIter = other.getLineStrIt();
320
321 difference_type count = 0;
322 difference_type distance_to_other = std::distance(currentLineStringIter, otherLineStringIter);
323
324 if(distance_to_other < 0)
325 {
326 count += pointIndex_;
327
328 while(distance_to_other < 0)
329 {
330 QLineString const* ls = *otherLineStringIter;
331 count -= ls->points.size();
332
333 ++otherLineStringIter;
334 ++distance_to_other;
335 }
336
337 assert(otherLineStringIter==currentLineStringIter);
338 }
339 else if(distance_to_other > 0)
340 {
341 count -= pointIndex_;
342
343 while(distance_to_other < 0)
344 {
345 QLineString const* ls = *currentLineStringIter;
346 count += ls->points.size();
347
348 ++currentLineStringIter;
349 --distance_to_other;
350 }
351
352 assert(otherLineStringIter==currentLineStringIter);
353 }
354 else
355 {
356 count = pointIndex_ - other.getPointIdx();
357 }
358
359 return count;
360 }
361
362 bool equal(const RingIteratorImpl& other) const
363 {
364 return (lineStringIterCurrent_ == other.getLineStrIt()) &&
365 (pointIndex_ == other.getPointIdx());
366 }
367
368 R dereference() const {return *(*lineStringIterCurrent_)->points[pointIndex_];}
369
370
371 I lineStringIterCurrent_;
372
373 bool empty;
374 size_t pointIndex_;
375 };
376
377
378[h3 Boost.Range for QRing]
379[#adaption_of_qring_range_source_code]
380
381 typedef RingIteratorImpl<std::vector<QLineString*>::iterator, QPoint> RingIterator;
382 typedef RingIteratorImpl<std::vector<QLineString*>::const_iterator, const QPoint> ConstRingIterator;
383
384 namespace boost
385 {
386 // Specialize metafunctions. We must include the range.hpp header.
387 // We must open the 'boost' namespace.
388
389 template <>
390 struct range_iterator<QRing>
391 { typedef RingIterator type; };
392
393 template<>
394 struct range_const_iterator<QRing>
395 { typedef ConstRingIterator type; };
396
397 } // namespace 'boost'
398
399
400 // The required Range functions. These should be defined in the same namespace
401 // as Ring.
402
403 inline RingIterator range_begin(QRing& r)
404 {return RingIterator(r.lines.begin());}
405
406 inline ConstRingIterator range_begin(const QRing& r)
407 {return ConstRingIterator(r.lines.begin());}
408
409 inline RingIterator range_end(QRing& r)
410 {return RingIterator(r.lines.end());}
411
412 inline ConstRingIterator range_end(const QRing& r)
413 {return ConstRingIterator(r.lines.end());}
414
415[h2 Adaption of QPolygon]
416[#adaption_of_qpolygon_source_code]
417
418 namespace boost {
419 namespace geometry {
420 namespace traits {
421 template<> struct tag<QPolygon> { typedef polygon_tag type; };
422 template<> struct ring_const_type<QPolygon> { typedef const QRing& type; };
423 template<> struct ring_mutable_type<QPolygon> { typedef QRing& type; };
424 template<> struct interior_const_type<QPolygon> { typedef const CustomPolygonRingRange type; };
425 template<> struct interior_mutable_type<QPolygon> { typedef CustomPolygonRingRange type; };
426
427 template<> struct exterior_ring<QPolygon>
428 {
429 static QRing& get(QPolygon& p)
430 {
431 return (*p.exterior);
432 }
433 static QRing const& get(QPolygon const& p)
434 {
435 return (*p.exterior);
436 }
437 };
438
439 template<> struct interior_rings<QPolygon>
440 {
441 static CustomPolygonRingRange get(QPolygon& p)
442 {
443 return CustomPolygonRingRange(PolygonRingIterator(p.interiors.begin()), PolygonRingIterator(p.interiors.end()));
444 }
445 static const CustomPolygonRingRange get(QPolygon const& p)
446 {
447 return CustomPolygonRingRange(ConstPolygonRingIterator(p.interiors.begin()), ConstPolygonRingIterator(p.interiors.end()));
448 }
449 };
450 }
451 }
452 } // namespace boost::geometry::traits
453
454
455[h3 Boost.Iterator for QRings in QPolygon]
456[#adaption_of_qpolygon_iterator_source_code]
457
458 template <class I, // Line iterator type
459 class R // Point reference type
460 >
461 class PolyRingIterator : public boost::iterator_facade<
462 PolyRingIterator<I,R>, R, std::random_access_iterator_tag, R> //new traversal tag
463 {
464 public:
465 PolyRingIterator() {}
466
467 explicit PolyRingIterator(I ringIter) : _ringIter(ringIter) {}
468
469 template<class OtherI, class OtherR>
470 PolyRingIterator(PolyRingIterator<OtherI, OtherR> const& other) :
471 _ringIter(other.getRingIter()) {}
472
473 I getRingIter() const {return _ringIter;}
474
475 typedef typename boost::iterator_facade<PolyRingIterator<I,R>, R, std::random_access_iterator_tag, R>::difference_type difference_type;
476
477 private:
478 friend class boost::iterator_core_access;
479
480 void increment()
481 {
482 ++_ringIter;
483 }
484
485 void decrement()
486 {
487 --_ringIter;
488 }
489
490 void advance(difference_type n)
491 {
492 std::advance(_ringIter,n);
493 }
494
495 difference_type distance_to(const PolyRingIterator& other) const
496 {
497 return std::distance(_ringIter, other.getRingIter());
498 }
499
500 bool equal(const PolyRingIterator& other) const
501 {
502 return _ringIter == other.getRingIter();
503 }
504
505 R dereference() const {return *(*_ringIter);}
506
507 I _ringIter;
508 };
509
510[h3 Boost.Range for PolygonRingIterator]
511[#adaption_of_qpolygon_range_source_code]
512
513 typedef PolyRingIterator<std::vector<QRing*>::iterator, QRing> PolygonRingIterator;
514 typedef PolyRingIterator<std::vector<QRing*>::const_iterator, const QRing> ConstPolygonRingIterator;
515
516 class CustomPolygonRingRange
517 {
518 PolygonRingIterator _begin;
519 PolygonRingIterator _end;
520
521 bool isIterSet;
522
523 ConstPolygonRingIterator _cbegin;
524 ConstPolygonRingIterator _cend;
525
526 bool isCIterSet;
527
528 public:
529
530 CustomPolygonRingRange(PolygonRingIterator begin, PolygonRingIterator end) : _begin(begin), _end(end), isIterSet(true) {}
531 CustomPolygonRingRange(ConstPolygonRingIterator begin, ConstPolygonRingIterator end) : _cbegin(begin), _cend(end), isCIterSet(true) {}
532
533 PolygonRingIterator begin()
534 {
535 assert(isIterSet);
536 return _begin;
537 }
538
539 ConstPolygonRingIterator cbegin() const
540 {
541 assert(isCIterSet);
542 return _cbegin;
543 }
544
545 PolygonRingIterator end()
546 {
547 assert(isIterSet);
548 return _end;
549 }
550
551 ConstPolygonRingIterator cend() const
552 {
553 assert(isCIterSet);
554 return _cend;
555 }
556 };
557
558 namespace boost
559 {
560 // Specialize metafunctions. We must include the range.hpp header.
561 // We must open the 'boost' namespace.
562
563 template <>
564 struct range_iterator<CustomPolygonRingRange> { typedef PolygonRingIterator type; };
565
566 template<>
567 struct range_const_iterator<CustomPolygonRingRange> { typedef ConstPolygonRingIterator type; };
568
569 } // namespace 'boost'
570
571
572 // The required Range functions. These should be defined in the same namespace
573 // as Ring.
574
575 inline PolygonRingIterator range_begin(CustomPolygonRingRange& r)
576 {return r.begin();}
577
578 inline ConstPolygonRingIterator range_begin(const CustomPolygonRingRange& r)
579 {return r.cbegin();}
580
581 inline PolygonRingIterator range_end(CustomPolygonRingRange& r)
582 {return r.end();}
583
584 inline ConstPolygonRingIterator range_end(const CustomPolygonRingRange& r)
585 {return r.cend();}
586
587[endsect]