Imported Upstream version 1.64.0
[platform/upstream/boost.git] / libs / geometry / doc / example_adapting_a_legacy_geometry_object_model.qbk
1 [section Example: Adapting a legacy geometry object model]
2
3 One 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
5 By 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
9 This 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
11 The 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
45 The 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
47 This 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
49 The 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
53 The [link adaption_of_qpoint_source_code adaption of the QPoint] is fairly straightforward, one just needs to implement the requirements. 
54
55 Even 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
57 Alternatively, 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
59 The 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
63 The [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
65 However, 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
67 This 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
72 This 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
76 The [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
78 However, 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
80 The [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
82 Now, 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
88 The only requirement that is not straightforward to adapt is the interior_rings' get method.
89
90 A __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
92 Therefore, 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
94 First, we [link adaption_of_qpolygon_iterator_source_code create a Boost.Iterator Facade] that returns QRing instead of pointer-to-QRing:
95
96 Now 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
98 We 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
102 That'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 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]