1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 
3 // Copyright (c) 2015, Oracle and/or its affiliates.
4 
5 // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
6 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
7 
8 // Licensed under the Boost Software License version 1.0.
9 // http://www.boost.org/users/license.html
10 
11 #ifndef BOOST_GEOMETRY_POLICIES_IS_VALID_FAILING_REASON_POLICY_HPP
12 #define BOOST_GEOMETRY_POLICIES_IS_VALID_FAILING_REASON_POLICY_HPP
13 
14 #include <sstream>
15 
16 #include <boost/geometry/io/dsv/write.hpp>
17 #include <boost/geometry/util/condition.hpp>
18 #include <boost/geometry/util/range.hpp>
19 #include <boost/geometry/algorithms/validity_failure_type.hpp>
20 #include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
21 
22 
23 namespace boost { namespace geometry
24 {
25 
26 
validity_failure_type_message(validity_failure_type failure)27 inline char const* validity_failure_type_message(validity_failure_type failure)
28 {
29     switch (failure)
30     {
31     case no_failure:
32         return "Geometry is valid";
33     case failure_few_points:
34         return "Geometry has too few points";
35     case failure_wrong_topological_dimension:
36         return "Geometry has wrong topological dimension";
37     case failure_not_closed:
38         return "Geometry is defined as closed but is open";
39     case failure_spikes:
40         return "Geometry has spikes";
41     case failure_self_intersections:
42         return "Geometry has invalid self-intersections";
43     case failure_wrong_orientation:
44         return "Geometry has wrong orientation";
45     case failure_interior_rings_outside:
46         return "Geometry has interior rings defined outside the outer boundary";
47     case failure_nested_interior_rings:
48         return "Geometry has nested interior rings";
49     case failure_disconnected_interior:
50         return "Geometry has disconnected interior";
51     case failure_intersecting_interiors:
52         return "Multi-polygon has intersecting interiors";
53     case failure_duplicate_points:
54         return "Geometry has duplicate (consecutive) points";
55     case failure_wrong_corner_order:
56         return "Box has corners in wrong order";
57     default: // to avoid -Wreturn-type warning
58         return "";
59     }
60 }
61 
62 
63 template <bool AllowDuplicates = true, bool AllowSpikes = true>
64 class failing_reason_policy
65 {
66 private:
67     static inline
transform_failure_type(validity_failure_type failure)68     validity_failure_type transform_failure_type(validity_failure_type failure)
69     {
70         if (BOOST_GEOMETRY_CONDITION(
71                 AllowDuplicates && failure == failure_duplicate_points))
72         {
73             return no_failure;
74         }
75         return failure;
76     }
77 
78     static inline
transform_failure_type(validity_failure_type failure,bool is_linear)79     validity_failure_type transform_failure_type(validity_failure_type failure,
80                                                  bool is_linear)
81     {
82         if (BOOST_GEOMETRY_CONDITION(
83                 is_linear && AllowSpikes && failure == failure_spikes))
84         {
85             return no_failure;
86         }
87         return transform_failure_type(failure);
88     }
89 
set_failure_message(validity_failure_type failure)90     inline void set_failure_message(validity_failure_type failure)
91     {
92         m_oss.str("");
93         m_oss.clear();
94         m_oss << validity_failure_type_message(failure);
95     }
96 
97     template
98     <
99         validity_failure_type Failure,
100         typename Data1,
101         typename Data2 = Data1,
102         typename Dummy = void
103     >
104     struct process_data
105     {
applyboost::geometry::failing_reason_policy::process_data106         static inline void apply(std::ostringstream&, Data1 const&)
107         {
108         }
109 
applyboost::geometry::failing_reason_policy::process_data110         static inline void apply(std::ostringstream&,
111                                  Data1 const&,
112                                  Data2 const&)
113         {
114         }
115     };
116 
117     template <typename SpikePoint>
118     struct process_data<failure_spikes, bool, SpikePoint>
119     {
applyboost::geometry::failing_reason_policy::process_data120         static inline void apply(std::ostringstream& oss,
121                                  bool is_linear,
122                                  SpikePoint const& spike_point)
123         {
124             if (BOOST_GEOMETRY_CONDITION(is_linear && AllowSpikes))
125             {
126                 return;
127             }
128 
129             oss << ". A spike point was found with apex at "
130                 << geometry::dsv(spike_point);
131         }
132     };
133 
134     template <typename Turns>
135     struct process_data<failure_self_intersections, Turns>
136     {
137         static inline
apply_to_segment_identifierboost::geometry::failing_reason_policy::process_data138         void apply_to_segment_identifier(std::ostringstream& oss,
139                                          segment_identifier seg_id)
140         {
141             oss << "{" << seg_id.source_index
142                 << ", " << seg_id.multi_index
143                 << ", " << seg_id.ring_index
144                 << ", " << seg_id.segment_index
145                 << "}";
146         }
147 
applyboost::geometry::failing_reason_policy::process_data148         static inline void apply(std::ostringstream& oss,
149                                  Turns const& turns)
150         {
151             typedef typename boost::range_value<Turns>::type turn_type;
152             turn_type const& turn = range::front(turns);
153             oss << ". A self-intersection point was found at "
154                 << geometry::dsv(turn.point);
155 
156             oss << "; method: " << method_char(turn.method)
157                 << "; operations: "
158                 << operation_char(turn.operations[0].operation)
159                 << "/"
160                 << operation_char(turn.operations[1].operation)
161                 << "; segment IDs {source, multi, ring, segment}: ";
162             apply_to_segment_identifier(oss, turn.operations[0].seg_id);
163             oss << "/";
164             apply_to_segment_identifier(oss, turn.operations[1].seg_id);
165         }
166     };
167 
168     template <typename Point>
169     struct process_data<failure_duplicate_points, Point>
170     {
applyboost::geometry::failing_reason_policy::process_data171         static inline void apply(std::ostringstream& oss,
172                                  Point const& point)
173         {
174             if (BOOST_GEOMETRY_CONDITION(AllowDuplicates))
175             {
176                 return;
177             }
178             oss << ". Duplicate points were found near point "
179                 << geometry::dsv(point);
180         }
181     };
182 
183 public:
failing_reason_policy(std::ostringstream & oss)184     failing_reason_policy(std::ostringstream& oss)
185         : m_oss(oss)
186     {}
187 
188     template <validity_failure_type Failure>
apply()189     inline bool apply()
190     {
191         validity_failure_type const failure = transform_failure_type(Failure);
192         set_failure_message(failure);
193         return failure == no_failure;
194     }
195 
196     template <validity_failure_type Failure, typename Data>
apply(Data const & data)197     inline bool apply(Data const& data)
198     {
199         validity_failure_type const failure = transform_failure_type(Failure);
200         set_failure_message(failure);
201         process_data<Failure, Data>::apply(m_oss, data);
202         return failure == no_failure;
203     }
204 
205     template <validity_failure_type Failure, typename Data1, typename Data2>
apply(Data1 const & data1,Data2 const & data2)206     inline bool apply(Data1 const& data1, Data2 const& data2)
207     {
208         validity_failure_type const failure
209             = transform_failure_type(Failure, data1);
210         set_failure_message(failure);
211         process_data<Failure, Data1, Data2>::apply(m_oss, data1, data2);
212         return failure == no_failure;
213     }
214 
215 private:
216     std::ostringstream& m_oss;
217 };
218 
219 
220 }} // namespace boost::geometry
221 
222 #endif // BOOST_GEOMETRY_POLICIES_IS_VALID_FAILING_REASON_POLICY_HPP
223