• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 
3 // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
4 // Copyright (c) 2008-2015 Bruno Lalande, Paris, France.
5 // Copyright (c) 2009-2015 Mateusz Loskot, London, UK.
6 
7 // This file was modified by Oracle on 2018.
8 // Modifications copyright (c) 2018 Oracle and/or its affiliates.
9 
10 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
11 
12 // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
13 // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
14 
15 // Use, modification and distribution is subject to the Boost Software License,
16 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
17 // http://www.boost.org/LICENSE_1_0.txt)
18 
19 #ifndef BOOST_GEOMETRY_ALGORITHMS_SIMPLIFY_HPP
20 #define BOOST_GEOMETRY_ALGORITHMS_SIMPLIFY_HPP
21 
22 #include <cstddef>
23 #include <set>
24 
25 #include <boost/core/ignore_unused.hpp>
26 #include <boost/range.hpp>
27 
28 #include <boost/variant/apply_visitor.hpp>
29 #include <boost/variant/static_visitor.hpp>
30 #include <boost/variant/variant_fwd.hpp>
31 
32 #include <boost/geometry/core/cs.hpp>
33 #include <boost/geometry/core/closure.hpp>
34 #include <boost/geometry/core/exterior_ring.hpp>
35 #include <boost/geometry/core/interior_rings.hpp>
36 #include <boost/geometry/core/mutable_range.hpp>
37 #include <boost/geometry/core/tags.hpp>
38 
39 #include <boost/geometry/geometries/concepts/check.hpp>
40 #include <boost/geometry/strategies/agnostic/simplify_douglas_peucker.hpp>
41 #include <boost/geometry/strategies/concepts/simplify_concept.hpp>
42 #include <boost/geometry/strategies/default_strategy.hpp>
43 #include <boost/geometry/strategies/distance.hpp>
44 
45 #include <boost/geometry/algorithms/area.hpp>
46 #include <boost/geometry/algorithms/clear.hpp>
47 #include <boost/geometry/algorithms/convert.hpp>
48 #include <boost/geometry/algorithms/detail/equals/point_point.hpp>
49 #include <boost/geometry/algorithms/not_implemented.hpp>
50 #include <boost/geometry/algorithms/is_empty.hpp>
51 #include <boost/geometry/algorithms/perimeter.hpp>
52 
53 #include <boost/geometry/algorithms/detail/distance/default_strategies.hpp>
54 
55 namespace boost { namespace geometry
56 {
57 
58 #ifndef DOXYGEN_NO_DETAIL
59 namespace detail { namespace simplify
60 {
61 
62 template <typename Range, typename EqualsStrategy>
is_degenerate(Range const & range,EqualsStrategy const & strategy)63 inline bool is_degenerate(Range const& range, EqualsStrategy const& strategy)
64 {
65     return boost::size(range) == 2
66         && detail::equals::equals_point_point(geometry::range::front(range),
67                                               geometry::range::back(range),
68                                               strategy);
69 }
70 
71 struct simplify_range_insert
72 {
73     template<typename Range, typename Strategy, typename OutputIterator, typename Distance>
applyboost::geometry::detail::simplify::simplify_range_insert74     static inline void apply(Range const& range, OutputIterator out,
75                              Distance const& max_distance, Strategy const& strategy)
76     {
77         typedef typename Strategy::distance_strategy_type::equals_point_point_strategy_type
78             equals_strategy_type;
79 
80         boost::ignore_unused(strategy);
81 
82         if (is_degenerate(range, equals_strategy_type()))
83         {
84             std::copy(boost::begin(range), boost::begin(range) + 1, out);
85         }
86         else if (boost::size(range) <= 2 || max_distance < 0)
87         {
88             std::copy(boost::begin(range), boost::end(range), out);
89         }
90         else
91         {
92             strategy.apply(range, out, max_distance);
93         }
94     }
95 };
96 
97 
98 struct simplify_copy
99 {
100     template <typename RangeIn, typename RangeOut, typename Strategy, typename Distance>
applyboost::geometry::detail::simplify::simplify_copy101     static inline void apply(RangeIn const& range, RangeOut& out,
102                              Distance const& , Strategy const& )
103     {
104         std::copy
105             (
106                 boost::begin(range), boost::end(range),
107                     geometry::range::back_inserter(out)
108             );
109     }
110 };
111 
112 
113 template <std::size_t MinimumToUseStrategy>
114 struct simplify_range
115 {
116     template <typename RangeIn, typename RangeOut, typename Strategy, typename Distance>
applyboost::geometry::detail::simplify::simplify_range117     static inline void apply(RangeIn const& range, RangeOut& out,
118                     Distance const& max_distance, Strategy const& strategy)
119     {
120         typedef typename Strategy::distance_strategy_type::equals_point_point_strategy_type
121             equals_strategy_type;
122 
123         // For a RING:
124         // Note that, especially if max_distance is too large,
125         // the output ring might be self intersecting while the input ring is
126         // not, although chances are low in normal polygons
127 
128         if (boost::size(range) <= MinimumToUseStrategy || max_distance < 0)
129         {
130             simplify_copy::apply(range, out, max_distance, strategy);
131         }
132         else
133         {
134             simplify_range_insert::apply
135                 (
136                     range, geometry::range::back_inserter(out), max_distance, strategy
137                 );
138         }
139 
140         // Verify the two remaining points are equal. If so, remove one of them.
141         // This can cause the output being under the minimum size
142         if (is_degenerate(out, equals_strategy_type()))
143         {
144             range::resize(out, 1);
145         }
146     }
147 };
148 
149 struct simplify_ring
150 {
151 private :
152     template <typename Area>
area_signboost::geometry::detail::simplify::simplify_ring153     static inline int area_sign(Area const& area)
154     {
155         return area > 0 ? 1 : area < 0 ? -1 : 0;
156     }
157 
158     template <typename Strategy, typename Ring>
get_oppositeboost::geometry::detail::simplify::simplify_ring159     static std::size_t get_opposite(std::size_t index, Ring const& ring)
160     {
161         typename Strategy::distance_strategy_type distance_strategy;
162 
163         // Verify if it is NOT the case that all points are less than the
164         // simplifying distance. If so, output is empty.
165         typename Strategy::distance_type max_distance(-1);
166 
167         typename geometry::point_type<Ring>::type point = range::at(ring, index);
168         std::size_t i = 0;
169         for (typename boost::range_iterator<Ring const>::type
170                 it = boost::begin(ring); it != boost::end(ring); ++it, ++i)
171         {
172             // This actually is point-segment distance but will result
173             // in point-point distance
174             typename Strategy::distance_type dist = distance_strategy.apply(*it, point, point);
175             if (dist > max_distance)
176             {
177                 max_distance = dist;
178                 index = i;
179             }
180         }
181         return index;
182     }
183 
184 public :
185     template <typename Ring, typename Strategy, typename Distance>
applyboost::geometry::detail::simplify::simplify_ring186     static inline void apply(Ring const& ring, Ring& out,
187                     Distance const& max_distance, Strategy const& strategy)
188     {
189         std::size_t const size = boost::size(ring);
190         if (size == 0)
191         {
192             return;
193         }
194 
195         int const input_sign = area_sign(geometry::area(ring));
196 
197         std::set<std::size_t> visited_indexes;
198 
199         // Rotate it into a copied vector
200         // (vector, because source type might not support rotation)
201         // (duplicate end point will be simplified away)
202         typedef typename geometry::point_type<Ring>::type point_type;
203 
204         std::vector<point_type> rotated(size);
205 
206         // Closing point (but it will not start here)
207         std::size_t index = 0;
208 
209         // Iterate (usually one iteration is enough)
210         for (std::size_t iteration = 0; iteration < 4u; iteration++)
211         {
212             // Always take the opposite. Opposite guarantees that no point
213             // "halfway" is chosen, creating an artefact (very narrow triangle)
214             // Iteration 0: opposite to closing point (1/2, = on convex hull)
215             //              (this will start simplification with that point
216             //               and its opposite ~0)
217             // Iteration 1: move a quarter on that ring, then opposite to 1/4
218             //              (with its opposite 3/4)
219             // Iteration 2: move an eight on that ring, then opposite (1/8)
220             // Iteration 3: again move a quarter, then opposite (7/8)
221             // So finally 8 "sides" of the ring have been examined (if it were
222             // a semi-circle). Most probably, there are only 0 or 1 iterations.
223             switch (iteration)
224             {
225                 case 1 : index = (index + size / 4) % size; break;
226                 case 2 : index = (index + size / 8) % size; break;
227                 case 3 : index = (index + size / 4) % size; break;
228             }
229             index = get_opposite<Strategy>(index, ring);
230 
231             if (visited_indexes.count(index) > 0)
232             {
233                 // Avoid trying the same starting point more than once
234                 continue;
235             }
236 
237             std::rotate_copy(boost::begin(ring), range::pos(ring, index),
238                              boost::end(ring), rotated.begin());
239 
240             // Close the rotated copy
241             rotated.push_back(range::at(ring, index));
242 
243             simplify_range<0>::apply(rotated, out, max_distance, strategy);
244 
245             // Verify that what was positive, stays positive (or goes to 0)
246             // and what was negative stays negative (or goes to 0)
247             int const output_sign = area_sign(geometry::area(out));
248             if (output_sign == input_sign)
249             {
250                 // Result is considered as satisfactory (usually this is the
251                 // first iteration - only for small rings, having a scale
252                 // similar to simplify_distance, next iterations are tried
253                 return;
254             }
255 
256             // Original is simplified away. Possibly there is a solution
257             // when another starting point is used
258             geometry::clear(out);
259 
260             if (iteration == 0
261                 && geometry::perimeter(ring) < 3 * max_distance)
262             {
263                 // Check if it is useful to iterate. A minimal triangle has a
264                 // perimeter of a bit more than 3 times the simplify distance
265                 return;
266             }
267 
268             // Prepare next try
269             visited_indexes.insert(index);
270             rotated.resize(size);
271         }
272     }
273 };
274 
275 
276 struct simplify_polygon
277 {
278 private:
279 
280     template
281     <
282         typename IteratorIn,
283         typename InteriorRingsOut,
284         typename Distance,
285         typename Strategy
286     >
iterateboost::geometry::detail::simplify::simplify_polygon287     static inline void iterate(IteratorIn begin, IteratorIn end,
288                     InteriorRingsOut& interior_rings_out,
289                     Distance const& max_distance, Strategy const& strategy)
290     {
291         typedef typename boost::range_value<InteriorRingsOut>::type single_type;
292         for (IteratorIn it = begin; it != end; ++it)
293         {
294             single_type out;
295             simplify_ring::apply(*it, out, max_distance, strategy);
296             if (! geometry::is_empty(out))
297             {
298                 range::push_back(interior_rings_out, out);
299             }
300         }
301     }
302 
303     template
304     <
305         typename InteriorRingsIn,
306         typename InteriorRingsOut,
307         typename Distance,
308         typename Strategy
309     >
apply_interior_ringsboost::geometry::detail::simplify::simplify_polygon310     static inline void apply_interior_rings(
311                     InteriorRingsIn const& interior_rings_in,
312                     InteriorRingsOut& interior_rings_out,
313                     Distance const& max_distance, Strategy const& strategy)
314     {
315         range::clear(interior_rings_out);
316 
317         iterate(
318             boost::begin(interior_rings_in), boost::end(interior_rings_in),
319             interior_rings_out,
320             max_distance, strategy);
321     }
322 
323 public:
324     template <typename Polygon, typename Strategy, typename Distance>
applyboost::geometry::detail::simplify::simplify_polygon325     static inline void apply(Polygon const& poly_in, Polygon& poly_out,
326                     Distance const& max_distance, Strategy const& strategy)
327     {
328         // Note that if there are inner rings, and distance is too large,
329         // they might intersect with the outer ring in the output,
330         // while it didn't in the input.
331         simplify_ring::apply(exterior_ring(poly_in), exterior_ring(poly_out),
332             max_distance, strategy);
333 
334         apply_interior_rings(interior_rings(poly_in),
335             interior_rings(poly_out), max_distance, strategy);
336     }
337 };
338 
339 
340 template<typename Policy>
341 struct simplify_multi
342 {
343     template <typename MultiGeometry, typename Strategy, typename Distance>
applyboost::geometry::detail::simplify::simplify_multi344     static inline void apply(MultiGeometry const& multi, MultiGeometry& out,
345                     Distance const& max_distance, Strategy const& strategy)
346     {
347         range::clear(out);
348 
349         typedef typename boost::range_value<MultiGeometry>::type single_type;
350 
351         for (typename boost::range_iterator<MultiGeometry const>::type
352                 it = boost::begin(multi); it != boost::end(multi); ++it)
353         {
354             single_type single_out;
355             Policy::apply(*it, single_out, max_distance, strategy);
356             if (! geometry::is_empty(single_out))
357             {
358                 range::push_back(out, single_out);
359             }
360         }
361     }
362 };
363 
364 
365 }} // namespace detail::simplify
366 #endif // DOXYGEN_NO_DETAIL
367 
368 
369 #ifndef DOXYGEN_NO_DISPATCH
370 namespace dispatch
371 {
372 
373 template
374 <
375     typename Geometry,
376     typename Tag = typename tag<Geometry>::type
377 >
378 struct simplify: not_implemented<Tag>
379 {};
380 
381 template <typename Point>
382 struct simplify<Point, point_tag>
383 {
384     template <typename Distance, typename Strategy>
applyboost::geometry::dispatch::simplify385     static inline void apply(Point const& point, Point& out,
386                     Distance const& , Strategy const& )
387     {
388         geometry::convert(point, out);
389     }
390 };
391 
392 // Linestring, keep 2 points (unless those points are the same)
393 template <typename Linestring>
394 struct simplify<Linestring, linestring_tag>
395     : detail::simplify::simplify_range<2>
396 {};
397 
398 template <typename Ring>
399 struct simplify<Ring, ring_tag>
400     : detail::simplify::simplify_ring
401 {};
402 
403 template <typename Polygon>
404 struct simplify<Polygon, polygon_tag>
405     : detail::simplify::simplify_polygon
406 {};
407 
408 
409 template
410 <
411     typename Geometry,
412     typename Tag = typename tag<Geometry>::type
413 >
414 struct simplify_insert: not_implemented<Tag>
415 {};
416 
417 
418 template <typename Linestring>
419 struct simplify_insert<Linestring, linestring_tag>
420     : detail::simplify::simplify_range_insert
421 {};
422 
423 template <typename Ring>
424 struct simplify_insert<Ring, ring_tag>
425     : detail::simplify::simplify_range_insert
426 {};
427 
428 template <typename MultiPoint>
429 struct simplify<MultiPoint, multi_point_tag>
430     : detail::simplify::simplify_copy
431 {};
432 
433 
434 template <typename MultiLinestring>
435 struct simplify<MultiLinestring, multi_linestring_tag>
436     : detail::simplify::simplify_multi<detail::simplify::simplify_range<2> >
437 {};
438 
439 
440 template <typename MultiPolygon>
441 struct simplify<MultiPolygon, multi_polygon_tag>
442     : detail::simplify::simplify_multi<detail::simplify::simplify_polygon>
443 {};
444 
445 
446 } // namespace dispatch
447 #endif // DOXYGEN_NO_DISPATCH
448 
449 
450 namespace resolve_strategy
451 {
452 
453 struct simplify
454 {
455     template <typename Geometry, typename Distance, typename Strategy>
applyboost::geometry::resolve_strategy::simplify456     static inline void apply(Geometry const& geometry,
457                              Geometry& out,
458                              Distance const& max_distance,
459                              Strategy const& strategy)
460     {
461         dispatch::simplify<Geometry>::apply(geometry, out, max_distance, strategy);
462     }
463 
464     template <typename Geometry, typename Distance>
applyboost::geometry::resolve_strategy::simplify465     static inline void apply(Geometry const& geometry,
466                              Geometry& out,
467                              Distance const& max_distance,
468                              default_strategy)
469     {
470         typedef typename point_type<Geometry>::type point_type;
471 
472         typedef typename strategy::distance::services::default_strategy
473         <
474             point_tag, segment_tag, point_type
475         >::type ds_strategy_type;
476 
477         typedef strategy::simplify::douglas_peucker
478         <
479             point_type, ds_strategy_type
480         > strategy_type;
481 
482         BOOST_CONCEPT_ASSERT(
483             (concepts::SimplifyStrategy<strategy_type, point_type>)
484         );
485 
486         apply(geometry, out, max_distance, strategy_type());
487     }
488 };
489 
490 struct simplify_insert
491 {
492     template
493     <
494         typename Geometry,
495         typename OutputIterator,
496         typename Distance,
497         typename Strategy
498     >
applyboost::geometry::resolve_strategy::simplify_insert499     static inline void apply(Geometry const& geometry,
500                              OutputIterator& out,
501                              Distance const& max_distance,
502                              Strategy const& strategy)
503     {
504         dispatch::simplify_insert<Geometry>::apply(geometry, out, max_distance, strategy);
505     }
506 
507     template <typename Geometry, typename OutputIterator, typename Distance>
applyboost::geometry::resolve_strategy::simplify_insert508     static inline void apply(Geometry const& geometry,
509                              OutputIterator& out,
510                              Distance const& max_distance,
511                              default_strategy)
512     {
513         typedef typename point_type<Geometry>::type point_type;
514 
515         typedef typename strategy::distance::services::default_strategy
516         <
517             point_tag, segment_tag, point_type
518         >::type ds_strategy_type;
519 
520         typedef strategy::simplify::douglas_peucker
521         <
522             point_type, ds_strategy_type
523         > strategy_type;
524 
525         BOOST_CONCEPT_ASSERT(
526             (concepts::SimplifyStrategy<strategy_type, point_type>)
527         );
528 
529         apply(geometry, out, max_distance, strategy_type());
530     }
531 };
532 
533 } // namespace resolve_strategy
534 
535 
536 namespace resolve_variant {
537 
538 template <typename Geometry>
539 struct simplify
540 {
541     template <typename Distance, typename Strategy>
applyboost::geometry::resolve_variant::simplify542     static inline void apply(Geometry const& geometry,
543                              Geometry& out,
544                              Distance const& max_distance,
545                              Strategy const& strategy)
546     {
547         resolve_strategy::simplify::apply(geometry, out, max_distance, strategy);
548     }
549 };
550 
551 template <BOOST_VARIANT_ENUM_PARAMS(typename T)>
552 struct simplify<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> >
553 {
554     template <typename Distance, typename Strategy>
555     struct visitor: boost::static_visitor<void>
556     {
557         Distance const& m_max_distance;
558         Strategy const& m_strategy;
559 
visitorboost::geometry::resolve_variant::simplify::visitor560         visitor(Distance const& max_distance, Strategy const& strategy)
561             : m_max_distance(max_distance)
562             , m_strategy(strategy)
563         {}
564 
565         template <typename Geometry>
operator ()boost::geometry::resolve_variant::simplify::visitor566         void operator()(Geometry const& geometry, Geometry& out) const
567         {
568             simplify<Geometry>::apply(geometry, out, m_max_distance, m_strategy);
569         }
570     };
571 
572     template <typename Distance, typename Strategy>
573     static inline void
applyboost::geometry::resolve_variant::simplify574     apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry,
575           boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>& out,
576           Distance const& max_distance,
577           Strategy const& strategy)
578     {
579         boost::apply_visitor(
580             visitor<Distance, Strategy>(max_distance, strategy),
581             geometry,
582             out
583         );
584     }
585 };
586 
587 } // namespace resolve_variant
588 
589 
590 /*!
591 \brief Simplify a geometry using a specified strategy
592 \ingroup simplify
593 \tparam Geometry \tparam_geometry
594 \tparam Distance A numerical distance measure
595 \tparam Strategy A type fulfilling a SimplifyStrategy concept
596 \param strategy A strategy to calculate simplification
597 \param geometry input geometry, to be simplified
598 \param out output geometry, simplified version of the input geometry
599 \param max_distance distance (in units of input coordinates) of a vertex
600     to other segments to be removed
601 \param strategy simplify strategy to be used for simplification, might
602     include point-distance strategy
603 
604 \image html svg_simplify_country.png "The image below presents the simplified country"
605 \qbk{distinguish,with strategy}
606 */
607 template<typename Geometry, typename Distance, typename Strategy>
simplify(Geometry const & geometry,Geometry & out,Distance const & max_distance,Strategy const & strategy)608 inline void simplify(Geometry const& geometry, Geometry& out,
609                      Distance const& max_distance, Strategy const& strategy)
610 {
611     concepts::check<Geometry>();
612 
613     geometry::clear(out);
614 
615     resolve_variant::simplify<Geometry>::apply(geometry, out, max_distance, strategy);
616 }
617 
618 
619 
620 
621 /*!
622 \brief Simplify a geometry
623 \ingroup simplify
624 \tparam Geometry \tparam_geometry
625 \tparam Distance \tparam_numeric
626 \note This version of simplify simplifies a geometry using the default
627     strategy (Douglas Peucker),
628 \param geometry input geometry, to be simplified
629 \param out output geometry, simplified version of the input geometry
630 \param max_distance distance (in units of input coordinates) of a vertex
631     to other segments to be removed
632 
633 \qbk{[include reference/algorithms/simplify.qbk]}
634  */
635 template<typename Geometry, typename Distance>
simplify(Geometry const & geometry,Geometry & out,Distance const & max_distance)636 inline void simplify(Geometry const& geometry, Geometry& out,
637                      Distance const& max_distance)
638 {
639     concepts::check<Geometry>();
640 
641     geometry::simplify(geometry, out, max_distance, default_strategy());
642 }
643 
644 
645 #ifndef DOXYGEN_NO_DETAIL
646 namespace detail { namespace simplify
647 {
648 
649 
650 /*!
651 \brief Simplify a geometry, using an output iterator
652     and a specified strategy
653 \ingroup simplify
654 \tparam Geometry \tparam_geometry
655 \param geometry input geometry, to be simplified
656 \param out output iterator, outputs all simplified points
657 \param max_distance distance (in units of input coordinates) of a vertex
658     to other segments to be removed
659 \param strategy simplify strategy to be used for simplification,
660     might include point-distance strategy
661 
662 \qbk{distinguish,with strategy}
663 \qbk{[include reference/algorithms/simplify.qbk]}
664 */
665 template<typename Geometry, typename OutputIterator, typename Distance, typename Strategy>
simplify_insert(Geometry const & geometry,OutputIterator out,Distance const & max_distance,Strategy const & strategy)666 inline void simplify_insert(Geometry const& geometry, OutputIterator out,
667                             Distance const& max_distance, Strategy const& strategy)
668 {
669     concepts::check<Geometry const>();
670 
671     resolve_strategy::simplify_insert::apply(geometry, out, max_distance, strategy);
672 }
673 
674 /*!
675 \brief Simplify a geometry, using an output iterator
676 \ingroup simplify
677 \tparam Geometry \tparam_geometry
678 \param geometry input geometry, to be simplified
679 \param out output iterator, outputs all simplified points
680 \param max_distance distance (in units of input coordinates) of a vertex
681     to other segments to be removed
682 
683 \qbk{[include reference/algorithms/simplify_insert.qbk]}
684  */
685 template<typename Geometry, typename OutputIterator, typename Distance>
simplify_insert(Geometry const & geometry,OutputIterator out,Distance const & max_distance)686 inline void simplify_insert(Geometry const& geometry, OutputIterator out,
687                             Distance const& max_distance)
688 {
689     // Concept: output point type = point type of input geometry
690     concepts::check<Geometry const>();
691     concepts::check<typename point_type<Geometry>::type>();
692 
693     simplify_insert(geometry, out, max_distance, default_strategy());
694 }
695 
696 }} // namespace detail::simplify
697 #endif // DOXYGEN_NO_DETAIL
698 
699 
700 
701 }} // namespace boost::geometry
702 
703 #endif // BOOST_GEOMETRY_ALGORITHMS_SIMPLIFY_HPP
704