1 // Boost.Geometry (aka GGL, Generic Geometry Library)
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 // Copyright (c) 2014-2015 Adam Wulkiewicz, Lodz, Poland.
8 // This file was modified by Oracle on 2013, 2014, 2015, 2017, 2018.
9 // Modifications copyright (c) 2013-2018 Oracle and/or its affiliates.
11 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
12 // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
14 // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
15 // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
17 // Use, modification and distribution is subject to the Boost Software License,
18 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
19 // http://www.boost.org/LICENSE_1_0.txt)
21 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_SECTIONALIZE_HPP
22 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_SECTIONALIZE_HPP
27 #include <boost/concept/requires.hpp>
28 #include <boost/core/ignore_unused.hpp>
29 #include <boost/mpl/assert.hpp>
30 #include <boost/mpl/vector_c.hpp>
31 #include <boost/range.hpp>
32 #include <boost/static_assert.hpp>
33 #include <boost/type_traits/is_same.hpp>
34 #include <boost/type_traits/is_fundamental.hpp>
36 #include <boost/geometry/algorithms/assign.hpp>
37 #include <boost/geometry/algorithms/envelope.hpp>
38 #include <boost/geometry/algorithms/expand.hpp>
40 #include <boost/geometry/algorithms/detail/interior_iterator.hpp>
41 #include <boost/geometry/algorithms/detail/recalculate.hpp>
42 #include <boost/geometry/algorithms/detail/ring_identifier.hpp>
43 #include <boost/geometry/algorithms/detail/signed_size_type.hpp>
45 #include <boost/geometry/core/access.hpp>
46 #include <boost/geometry/core/closure.hpp>
47 #include <boost/geometry/core/exterior_ring.hpp>
48 #include <boost/geometry/core/point_order.hpp>
49 #include <boost/geometry/core/tags.hpp>
51 #include <boost/geometry/geometries/concepts/check.hpp>
52 #include <boost/geometry/util/math.hpp>
53 #include <boost/geometry/policies/robustness/no_rescale_policy.hpp>
54 #include <boost/geometry/policies/robustness/robust_point_type.hpp>
55 #include <boost/geometry/views/closeable_view.hpp>
56 #include <boost/geometry/views/reversible_view.hpp>
57 #include <boost/geometry/geometries/segment.hpp>
59 #include <boost/geometry/algorithms/detail/expand_by_epsilon.hpp>
60 #include <boost/geometry/strategies/envelope.hpp>
61 #include <boost/geometry/strategies/expand.hpp>
63 namespace boost { namespace geometry
68 \brief Structure containing section information
69 \details Section information consists of a bounding box, direction
70 information (if it is increasing or decreasing, per dimension),
71 index information (begin-end, ring, multi) and the number of
72 segments in this section
75 \tparam DimensionCount number of dimensions for this section
81 std::size_t DimensionCount
86 static std::size_t const dimension_count = DimensionCount;
88 int directions[DimensionCount];
89 ring_identifier ring_id;
92 // NOTE: size_type could be passed as template parameter
93 // NOTE: these probably also could be of type std::size_t
94 signed_size_type begin_index;
95 signed_size_type end_index;
97 std::size_t range_count;
99 signed_size_type non_duplicate_index;
101 bool is_non_duplicate_first;
102 bool is_non_duplicate_last;
110 , non_duplicate_index(-1)
111 , is_non_duplicate_first(false)
112 , is_non_duplicate_last(false)
114 assign_inverse(bounding_box);
115 for (std::size_t i = 0; i < DimensionCount; i++)
124 \brief Structure containing a collection of sections
125 \note Derived from a vector, proves to be faster than of deque
126 \note vector might be templated in the future
127 \ingroup sectionalize
129 template <typename Box, std::size_t DimensionCount>
130 struct sections : std::vector<section<Box, DimensionCount> >
132 typedef Box box_type;
133 static std::size_t const value = DimensionCount;
137 #ifndef DOXYGEN_NO_DETAIL
138 namespace detail { namespace sectionalize
141 // NOTE: This utility will NOT work for latitudes, dimension 1 in spherical
142 // and geographic coordinate system because in these coordinate systems
143 // e.g. a segment on northern hemisphere may go towards greater latitude
144 // and then towards lesser latitude.
148 typename DimensionVector,
151 typename CastedCSTag = typename tag_cast
153 typename cs_tag<Point>::type,
157 struct get_direction_loop
159 typedef typename boost::mpl::at_c<DimensionVector, Index>::type dimension;
161 template <typename Segment>
162 static inline void apply(Segment const& seg,
163 int directions[Count])
165 typedef typename coordinate_type<Segment>::type coordinate_type;
167 coordinate_type const c0 = geometry::get<0, dimension::value>(seg);
168 coordinate_type const c1 = geometry::get<1, dimension::value>(seg);
170 directions[Index] = c1 > c0 ? 1 : c1 < c0 ? -1 : 0;
179 >::apply(seg, directions);
186 typename DimensionVector,
189 struct get_direction_loop<Point, DimensionVector, 0, Count, spherical_tag>
191 typedef typename boost::mpl::at_c<DimensionVector, 0>::type dimension;
193 template <typename Segment>
194 static inline void apply(Segment const& seg,
195 int directions[Count])
197 typedef typename coordinate_type<Segment>::type coordinate_type;
198 typedef typename coordinate_system<Point>::type::units units_t;
200 coordinate_type const diff = math::longitude_distance_signed
202 units_t, coordinate_type
203 >(geometry::get<0, 0>(seg),
204 geometry::get<1, 0>(seg));
206 coordinate_type zero = coordinate_type();
207 directions[0] = diff > zero ? 1 : diff < zero ? -1 : 0;
216 >::apply(seg, directions);
223 typename DimensionVector,
227 struct get_direction_loop<Point, DimensionVector, Count, Count, CastedCSTag>
229 template <typename Segment>
230 static inline void apply(Segment const&, int [Count])
235 //! Copy one static array to another
236 template <typename T, std::size_t Index, std::size_t Count>
239 static inline void apply(T const source[Count], T target[Count])
241 target[Index] = source[Index];
242 copy_loop<T, Index + 1, Count>::apply(source, target);
246 template <typename T, std::size_t Count>
247 struct copy_loop<T, Count, Count>
249 static inline void apply(T const [Count], T [Count])
253 //! Compare two static arrays
254 template <typename T, std::size_t Index, std::size_t Count>
257 static inline bool apply(T const array1[Count], T const array2[Count])
259 return array1[Index] != array2[Index]
264 >::apply(array1, array2);
268 template <typename T, std::size_t Count>
269 struct compare_loop<T, Count, Count>
271 static inline bool apply(T const [Count], T const [Count])
279 template <std::size_t Dimension, std::size_t DimensionCount>
280 struct check_duplicate_loop
282 template <typename Segment>
283 static inline bool apply(Segment const& seg)
285 if (! geometry::math::equals
287 geometry::get<0, Dimension>(seg),
288 geometry::get<1, Dimension>(seg)
295 return check_duplicate_loop
297 Dimension + 1, DimensionCount
302 template <std::size_t DimensionCount>
303 struct check_duplicate_loop<DimensionCount, DimensionCount>
305 template <typename Segment>
306 static inline bool apply(Segment const&)
312 //! Assign a value to a static array
313 template <typename T, std::size_t Index, std::size_t Count>
316 static inline void apply(T dims[Count], T const value)
319 assign_loop<T, Index + 1, Count>::apply(dims, value);
323 template <typename T, std::size_t Count>
324 struct assign_loop<T, Count, Count>
326 static inline void apply(T [Count], T const)
331 template <typename CSTag>
332 struct box_first_in_section
334 template <typename Box, typename Point, typename EnvelopeStrategy>
335 static inline void apply(Box & box, Point const& prev, Point const& curr,
336 EnvelopeStrategy const& strategy)
338 geometry::model::referring_segment<Point const> seg(prev, curr);
339 geometry::envelope(seg, box, strategy);
344 struct box_first_in_section<cartesian_tag>
346 template <typename Box, typename Point, typename ExpandStrategy>
347 static inline void apply(Box & box, Point const& prev, Point const& curr,
348 ExpandStrategy const& )
350 geometry::envelope(prev, box);
351 geometry::expand(box, curr);
355 template <typename CSTag>
356 struct box_next_in_section
358 template <typename Box, typename Point, typename Strategy>
359 static inline void apply(Box & box, Point const& prev, Point const& curr,
360 Strategy const& strategy)
362 geometry::model::referring_segment<Point const> seg(prev, curr);
363 geometry::expand(box, seg, strategy);
368 struct box_next_in_section<cartesian_tag>
370 template <typename Box, typename Point, typename Strategy>
371 static inline void apply(Box & box, Point const& , Point const& curr,
374 geometry::expand(box, curr);
378 /// @brief Helper class to create sections of a part of a range, on the fly
382 typename DimensionVector
384 struct sectionalize_part
386 static const std::size_t dimension_count
387 = boost::mpl::size<DimensionVector>::value;
392 typename RobustPolicy,
395 static inline void apply(Sections& sections,
396 Iterator begin, Iterator end,
397 RobustPolicy const& robust_policy,
398 ring_identifier ring_id,
399 std::size_t max_count)
401 typedef typename strategy::envelope::services::default_strategy
404 typename cs_tag<typename Sections::box_type>::type
405 >::type envelope_strategy_type;
407 typedef typename strategy::expand::services::default_strategy
410 typename cs_tag<typename Sections::box_type>::type
411 >::type expand_strategy_type;
413 apply(sections, begin, end,
415 envelope_strategy_type(),
416 expand_strategy_type(),
423 typename RobustPolicy,
425 typename EnvelopeStrategy,
426 typename ExpandStrategy
428 static inline void apply(Sections& sections,
429 Iterator begin, Iterator end,
430 RobustPolicy const& robust_policy,
431 EnvelopeStrategy const& envelope_strategy,
432 ExpandStrategy const& expand_strategy,
433 ring_identifier ring_id,
434 std::size_t max_count)
436 boost::ignore_unused(robust_policy);
438 typedef typename boost::range_value<Sections>::type section_type;
441 (static_cast<std::size_t>(section_type::dimension_count)
442 == static_cast<std::size_t>(boost::mpl::size<DimensionVector>::value))
445 typedef typename geometry::robust_point_type
449 >::type robust_point_type;
451 std::size_t const count = std::distance(begin, end);
457 signed_size_type index = 0;
458 signed_size_type ndi = 0; // non duplicate index
459 section_type section;
461 bool mark_first_non_duplicated = true;
462 std::size_t last_non_duplicate_index = sections.size();
465 robust_point_type previous_robust_point;
466 geometry::recalculate(previous_robust_point, *it, robust_policy);
468 for(Iterator previous = it++;
470 ++previous, ++it, index++)
472 robust_point_type current_robust_point;
473 geometry::recalculate(current_robust_point, *it, robust_policy);
474 model::referring_segment<robust_point_type> robust_segment(
475 previous_robust_point, current_robust_point);
477 int direction_classes[dimension_count] = {0};
480 Point, DimensionVector, 0, dimension_count
481 >::apply(robust_segment, direction_classes);
483 // if "dir" == 0 for all point-dimensions, it is duplicate.
484 // Those sections might be omitted, if wished, lateron
485 bool duplicate = false;
487 if (direction_classes[0] == 0)
489 // Recheck because ALL dimensions should be checked,
490 // not only first one.
491 // (dimension_count might be < dimension<P>::value)
492 if (check_duplicate_loop
494 0, geometry::dimension<Point>::type::value
495 >::apply(robust_segment)
500 // Change direction-info to force new section
501 // Note that wo consecutive duplicate segments will generate
502 // only one duplicate-section.
503 // Actual value is not important as long as it is not -1,0,1
506 int, 0, dimension_count
507 >::apply(direction_classes, -99);
511 if (section.count > 0
514 int, 0, dimension_count
515 >::apply(direction_classes, section.directions)
516 || section.count > max_count)
519 if (! section.duplicate)
521 last_non_duplicate_index = sections.size();
524 sections.push_back(section);
525 section = section_type();
528 if (section.count == 0)
530 section.begin_index = index;
531 section.ring_id = ring_id;
532 section.duplicate = duplicate;
533 section.non_duplicate_index = ndi;
534 section.range_count = count;
536 if (mark_first_non_duplicated && ! duplicate)
538 section.is_non_duplicate_first = true;
539 mark_first_non_duplicated = false;
544 int, 0, dimension_count
545 >::apply(direction_classes, section.directions);
547 // In cartesian this is envelope of previous point expanded with current point
548 // in non-cartesian this is envelope of a segment
549 box_first_in_section<typename cs_tag<robust_point_type>::type>
550 ::apply(section.bounding_box, previous_robust_point, current_robust_point, envelope_strategy);
554 // In cartesian this is expand with current point
555 // in non-cartesian this is expand with a segment
556 box_next_in_section<typename cs_tag<robust_point_type>::type>
557 ::apply(section.bounding_box, previous_robust_point, current_robust_point, expand_strategy);
560 section.end_index = index + 1;
566 previous_robust_point = current_robust_point;
569 // Add last section if applicable
570 if (section.count > 0)
572 if (! section.duplicate)
574 last_non_duplicate_index = sections.size();
577 sections.push_back(section);
580 if (last_non_duplicate_index < sections.size()
581 && ! sections[last_non_duplicate_index].duplicate)
583 sections[last_non_duplicate_index].is_non_duplicate_last = true;
591 closure_selector Closure,
594 typename DimensionVector
596 struct sectionalize_range
601 typename RobustPolicy,
603 typename EnvelopeStrategy,
604 typename ExpandStrategy
606 static inline void apply(Range const& range,
607 RobustPolicy const& robust_policy,
609 EnvelopeStrategy const& envelope_strategy,
610 ExpandStrategy const& expand_strategy,
611 ring_identifier ring_id,
612 std::size_t max_count)
614 typedef typename closeable_view<Range const, Closure>::type cview_type;
615 typedef typename reversible_view
618 Reverse ? iterate_reverse : iterate_forward
621 cview_type cview(range);
622 view_type view(cview);
624 std::size_t const n = boost::size(view);
627 // Zero points, no section
633 // Line with one point ==> no sections
637 sectionalize_part<Point, DimensionVector>::apply(sections,
638 boost::begin(view), boost::end(view),
639 robust_policy, envelope_strategy, expand_strategy,
647 typename DimensionVector
649 struct sectionalize_polygon
654 typename RobustPolicy,
656 typename EnvelopeStrategy,
657 typename ExpandStrategy
659 static inline void apply(Polygon const& poly,
660 RobustPolicy const& robust_policy,
662 EnvelopeStrategy const& envelope_strategy,
663 ExpandStrategy const& expand_strategy,
664 ring_identifier ring_id,
665 std::size_t max_count)
667 typedef typename point_type<Polygon>::type point_type;
668 typedef sectionalize_range
670 closure<Polygon>::value, Reverse,
671 point_type, DimensionVector
674 ring_id.ring_index = -1;
675 per_range::apply(exterior_ring(poly), robust_policy, sections,
676 envelope_strategy, expand_strategy, ring_id, max_count);
678 ring_id.ring_index++;
679 typename interior_return_type<Polygon const>::type
680 rings = interior_rings(poly);
681 for (typename detail::interior_iterator<Polygon const>::type
682 it = boost::begin(rings); it != boost::end(rings); ++it, ++ring_id.ring_index)
684 per_range::apply(*it, robust_policy, sections,
685 envelope_strategy, expand_strategy, ring_id, max_count);
690 template <typename DimensionVector>
691 struct sectionalize_box
696 typename RobustPolicy,
698 typename EnvelopeStrategy,
699 typename ExpandStrategy
701 static inline void apply(Box const& box,
702 RobustPolicy const& robust_policy,
704 EnvelopeStrategy const& envelope_strategy,
705 ExpandStrategy const& expand_strategy,
706 ring_identifier const& ring_id, std::size_t max_count)
708 typedef typename point_type<Box>::type point_type;
710 assert_dimension<Box, 2>();
712 // Add all four sides of the 2D-box as separate section.
713 // Easiest is to convert it to a polygon.
714 // However, we don't have the polygon type
715 // (or polygon would be a helper-type).
716 // Therefore we mimic a linestring/std::vector of 5 points
718 // TODO: might be replaced by assign_box_corners_oriented
720 point_type ll, lr, ul, ur;
721 geometry::detail::assign_box_corners(box, ll, lr, ul, ur);
723 std::vector<point_type> points;
724 points.push_back(ll);
725 points.push_back(ul);
726 points.push_back(ur);
727 points.push_back(lr);
728 points.push_back(ll);
730 // NOTE: Use cartesian envelope strategy in all coordinate systems
731 // because edges of a box are not geodesic segments
737 >::apply(points, robust_policy, sections,
738 envelope_strategy, expand_strategy,
743 template <typename DimensionVector, typename Policy>
744 struct sectionalize_multi
748 typename MultiGeometry,
749 typename RobustPolicy,
751 typename EnvelopeStrategy,
752 typename ExpandStrategy
754 static inline void apply(MultiGeometry const& multi,
755 RobustPolicy const& robust_policy,
757 EnvelopeStrategy const& envelope_strategy,
758 ExpandStrategy const& expand_strategy,
759 ring_identifier ring_id,
760 std::size_t max_count)
762 ring_id.multi_index = 0;
763 for (typename boost::range_iterator<MultiGeometry const>::type
764 it = boost::begin(multi);
765 it != boost::end(multi);
766 ++it, ++ring_id.multi_index)
768 Policy::apply(*it, robust_policy, sections,
769 envelope_strategy, expand_strategy,
775 template <typename Sections>
776 inline void enlarge_sections(Sections& sections)
778 // Robustness issue. Increase sections a tiny bit such that all points are really within (and not on border)
779 // Reason: turns might, rarely, be missed otherwise (case: "buffer_mp1")
780 // Drawback: not really, range is now completely inside the section. Section is a tiny bit too large,
781 // which might cause (a small number) of more comparisons
783 // NOTE: above is old comment to the not used code expanding the Boxes by relaxed_epsilon(10)
785 // Enlarge sections by scaled epsilon, this should be consistent with math::equals().
786 // Points and Segments are equal-compared WRT machine epsilon, but Boxes aren't
787 // Enlarging Boxes ensures that they correspond to the bound objects,
788 // Segments in this case, since Sections are collections of Segments.
789 for (typename boost::range_iterator<Sections>::type it = boost::begin(sections);
790 it != boost::end(sections);
793 detail::expand_by_epsilon(it->bounding_box);
798 }} // namespace detail::sectionalize
799 #endif // DOXYGEN_NO_DETAIL
802 #ifndef DOXYGEN_NO_DISPATCH
811 typename DimensionVector
817 false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE
826 typename DimensionVector
828 struct sectionalize<box_tag, Box, Reverse, DimensionVector>
829 : detail::sectionalize::sectionalize_box<DimensionVector>
835 typename DimensionVector
844 : detail::sectionalize::sectionalize_range
847 typename point_type<LineString>::type,
856 typename DimensionVector
858 struct sectionalize<ring_tag, Ring, Reverse, DimensionVector>
859 : detail::sectionalize::sectionalize_range
861 geometry::closure<Ring>::value, Reverse,
862 typename point_type<Ring>::type,
871 typename DimensionVector
873 struct sectionalize<polygon_tag, Polygon, Reverse, DimensionVector>
874 : detail::sectionalize::sectionalize_polygon
876 Reverse, DimensionVector
882 typename MultiPolygon,
884 typename DimensionVector
886 struct sectionalize<multi_polygon_tag, MultiPolygon, Reverse, DimensionVector>
887 : detail::sectionalize::sectionalize_multi
890 detail::sectionalize::sectionalize_polygon
901 typename MultiLinestring,
903 typename DimensionVector
905 struct sectionalize<multi_linestring_tag, MultiLinestring, Reverse, DimensionVector>
906 : detail::sectionalize::sectionalize_multi
909 detail::sectionalize::sectionalize_range
912 typename point_type<MultiLinestring>::type,
919 } // namespace dispatch
924 \brief Split a geometry into monotonic sections
925 \ingroup sectionalize
926 \tparam Geometry type of geometry to check
927 \tparam Sections type of sections to create
928 \param geometry geometry to create sections from
929 \param robust_policy policy to handle robustness issues
930 \param sections structure with sections
931 \param envelope_strategy strategy for envelope calculation
932 \param expand_strategy strategy for partitions
933 \param source_index index to assign to the ring_identifiers
934 \param max_count maximal number of points per section
935 (defaults to 10, this seems to give the fastest results)
941 typename DimensionVector,
944 typename RobustPolicy,
945 typename EnvelopeStrategy,
946 typename ExpandStrategy
948 inline void sectionalize(Geometry const& geometry,
949 RobustPolicy const& robust_policy,
951 EnvelopeStrategy const& envelope_strategy,
952 ExpandStrategy const& expand_strategy,
953 int source_index = 0,
954 std::size_t max_count = 10)
956 BOOST_STATIC_ASSERT((! boost::is_fundamental<EnvelopeStrategy>::value));
958 concepts::check<Geometry const>();
960 typedef typename boost::range_value<Sections>::type section_type;
962 // Compiletime check for point type of section boxes
963 // and point type related to robust policy
964 typedef typename geometry::coordinate_type
966 typename section_type::box_type
968 typedef typename geometry::coordinate_type
970 typename geometry::robust_point_type
972 typename geometry::point_type<Geometry>::type,
977 BOOST_MPL_ASSERT((boost::is_same<ctype1, ctype2>));
982 ring_identifier ring_id;
983 ring_id.source_index = source_index;
985 dispatch::sectionalize
987 typename tag<Geometry>::type,
991 >::apply(geometry, robust_policy, sections,
992 envelope_strategy, expand_strategy,
995 detail::sectionalize::enlarge_sections(sections);
1002 typename DimensionVector,
1005 typename RobustPolicy
1007 inline void sectionalize(Geometry const& geometry,
1008 RobustPolicy const& robust_policy,
1010 int source_index = 0,
1011 std::size_t max_count = 10)
1013 typedef typename strategy::envelope::services::default_strategy
1015 typename tag<Geometry>::type,
1016 typename cs_tag<Geometry>::type
1017 >::type envelope_strategy_type;
1019 typedef typename strategy::expand::services::default_strategy
1021 typename boost::mpl::if_c
1023 boost::is_same<typename tag<Geometry>::type, box_tag>::value,
1027 typename cs_tag<Geometry>::type
1028 >::type expand_strategy_type;
1030 boost::geometry::sectionalize
1032 Reverse, DimensionVector
1033 >(geometry, robust_policy, sections,
1034 envelope_strategy_type(),
1035 expand_strategy_type(),
1036 source_index, max_count);
1039 }} // namespace boost::geometry
1042 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_SECTIONALIZE_HPP