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