1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands.
4 // Copyright (c) 2008-2014 Bruno Lalande, Paris, France.
5 // Copyright (c) 2009-2014 Mateusz Loskot, London, UK.
6 // Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland.
8 // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
9 // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
11 // Use, modification and distribution is subject to the Boost Software License,
12 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
13 // http://www.boost.org/LICENSE_1_0.txt)
15 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_COLLECT_VECTORS_HPP
16 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_COLLECT_VECTORS_HPP
19 #include <boost/numeric/conversion/cast.hpp>
21 #include <boost/geometry/algorithms/detail/interior_iterator.hpp>
22 #include <boost/geometry/algorithms/detail/normalize.hpp>
24 #include <boost/geometry/core/cs.hpp>
25 #include <boost/geometry/core/interior_rings.hpp>
26 #include <boost/geometry/core/tags.hpp>
28 #include <boost/geometry/formulas/spherical.hpp>
30 #include <boost/geometry/geometries/concepts/check.hpp>
32 #include <boost/geometry/util/math.hpp>
33 #include <boost/geometry/util/range.hpp>
36 namespace boost { namespace geometry
39 // Since these vectors (though ray would be a better name) are used in the
40 // implementation of equals() for Areal geometries the internal representation
41 // should be consistent with the default side strategy for CS because currently
42 // it's used in other relops.
47 typename CSTag = typename cs_tag<Geometry>::type
49 struct collected_vector
53 inline collected_vector()
56 inline collected_vector(T const& px, T const& py,
57 T const& pdx, T const& pdy)
66 template <typename Point>
67 inline collected_vector(Point const& p1, Point const& p2)
78 T magnitude = math::sqrt(
79 boost::numeric_cast<T>(dx * dx + dy * dy));
81 // NOTE: shouldn't here math::equals() be called?
93 inline bool operator<(collected_vector const& other) const
95 if (math::equals(x, other.x))
97 if (math::equals(y, other.y))
99 if (math::equals(dx, other.dx))
101 return dy < other.dy;
103 return dx < other.dx;
110 inline bool next_is_collinear(collected_vector const& other) const
112 return same_direction(other);
116 inline bool operator==(collected_vector const& other) const
118 return math::equals(x, other.x)
119 && math::equals(y, other.y)
120 && same_direction(other);
124 inline bool same_direction(collected_vector const& other) const
126 // For high precision arithmetic, we have to be
127 // more relaxed then using ==
128 // Because 2/sqrt( (0,0)<->(2,2) ) == 1/sqrt( (0,0)<->(1,1) )
129 // is not always true (at least, it is not for ttmath)
130 return math::equals_with_epsilon(dx, other.dx)
131 && math::equals_with_epsilon(dy, other.dy);
139 template <typename T, typename Geometry>
140 struct collected_vector<T, Geometry, spherical_equatorial_tag>
144 typedef typename coordinate_system<Geometry>::type cs_type;
145 typedef model::point<T, 2, cs_type> point_type;
146 typedef model::point<T, 3, cs::cartesian> vector_type;
151 template <typename Point>
152 collected_vector(Point const& p1, Point const& p2)
153 : origin(get<0>(p1), get<1>(p1))
155 origin = detail::return_normalized<point_type>(origin);
157 using namespace geometry::formula;
158 prev = sph_to_cart3d<vector_type>(p1);
159 next = sph_to_cart3d<vector_type>(p2);
160 direction = cross_product(prev, next);
165 T magnitude_sqr = dot_product(direction, direction);
167 // NOTE: shouldn't here math::equals() be called?
168 if (magnitude_sqr > 0)
170 divide_value(direction, math::sqrt(magnitude_sqr));
177 bool operator<(collected_vector const& other) const
179 if (math::equals(get<0>(origin), get<0>(other.origin)))
181 if (math::equals(get<1>(origin), get<1>(other.origin)))
183 if (math::equals(get<0>(direction), get<0>(other.direction)))
185 if (math::equals(get<1>(direction), get<1>(other.direction)))
187 return get<2>(direction) < get<2>(other.direction);
189 return get<1>(direction) < get<1>(other.direction);
191 return get<0>(direction) < get<0>(other.direction);
193 return get<1>(origin) < get<1>(other.origin);
195 return get<0>(origin) < get<0>(other.origin);
198 // For consistency with side and intersection strategies used by relops
199 // IMPORTANT: this method should be called for previous vector
200 // and next vector should be passed as parameter
201 bool next_is_collinear(collected_vector const& other) const
203 return formula::sph_side_value(direction, other.next) == 0;
207 bool operator==(collected_vector const& other) const
209 return math::equals(get<0>(origin), get<0>(other.origin))
210 && math::equals(get<1>(origin), get<1>(other.origin))
211 && is_collinear(other);
215 // For consistency with side and intersection strategies used by relops
216 bool is_collinear(collected_vector const& other) const
218 return formula::sph_side_value(direction, other.prev) == 0
219 && formula::sph_side_value(direction, other.next) == 0;
222 /*bool same_direction(collected_vector const& other) const
224 return math::equals_with_epsilon(get<0>(direction), get<0>(other.direction))
225 && math::equals_with_epsilon(get<1>(direction), get<1>(other.direction))
226 && math::equals_with_epsilon(get<2>(direction), get<2>(other.direction));
229 point_type origin; // used for sorting and equality check
230 vector_type direction; // used for sorting, only in operator<
231 vector_type prev; // used for collinearity check, only in operator==
232 vector_type next; // used for collinearity check
235 template <typename T, typename Geometry>
236 struct collected_vector<T, Geometry, spherical_polar_tag>
237 : public collected_vector<T, Geometry, spherical_equatorial_tag>
239 typedef collected_vector<T, Geometry, spherical_equatorial_tag> base_type;
241 collected_vector() {}
243 template <typename Point>
244 collected_vector(Point const& p1, Point const& p2)
245 : base_type(to_equatorial(p1), to_equatorial(p2))
249 template <typename Point>
250 Point polar_to_equatorial(Point const& p)
252 typedef typename coordinate_type<Point>::type coord_type;
254 typedef math::detail::constants_on_spheroid
257 typename coordinate_system<Point>::type::units
260 coord_type const pi_2 = constants::half_period() / 2;
263 set<1>(res, pi_2 - get<1>(p));
268 // This is consistent with the currently used default geographic side
269 // and intersection strategies. Spherical strategies are used by default.
270 // When default strategies are changed in the future this specialization
271 // should be changed too.
272 template <typename T, typename Geometry>
273 struct collected_vector<T, Geometry, geographic_tag>
274 : public collected_vector<T, Geometry, spherical_equatorial_tag>
276 typedef collected_vector<T, Geometry, spherical_equatorial_tag> base_type;
278 collected_vector() {}
280 template <typename Point>
281 collected_vector(Point const& p1, Point const& p2)
287 #ifndef DOXYGEN_NO_DETAIL
288 namespace detail { namespace collect_vectors
292 template <typename Range, typename Collection>
293 struct range_collect_vectors
295 typedef typename boost::range_value<Collection>::type item_type;
296 typedef typename item_type::type calculation_type;
298 static inline void apply(Collection& collection, Range const& range)
300 if (boost::size(range) < 2)
305 typedef typename boost::range_size<Collection>::type collection_size_t;
306 collection_size_t c_old_size = boost::size(collection);
308 typedef typename boost::range_iterator<Range const>::type iterator;
310 bool is_first = true;
311 iterator it = boost::begin(range);
313 for (iterator prev = it++;
314 it != boost::end(range);
317 typename boost::range_value<Collection>::type v(*prev, *it);
319 // Normalize the vector -> this results in points+direction
320 // and is comparible between geometries
321 // Avoid non-duplicate points (AND division by zero)
324 // Avoid non-direction changing points
325 if (is_first || ! collection.back().next_is_collinear(v))
327 collection.push_back(v);
333 // If first one has same direction as last one, remove first one
334 collection_size_t collected_count = boost::size(collection) - c_old_size;
335 if ( collected_count > 1 )
337 typedef typename boost::range_iterator<Collection>::type c_iterator;
338 c_iterator first = range::pos(collection, c_old_size);
340 if (collection.back().next_is_collinear(*first) )
342 //collection.erase(first);
343 // O(1) instead of O(N)
344 *first = collection.back();
345 collection.pop_back();
352 // Default version (cartesian)
353 template <typename Box, typename Collection, typename CSTag = typename cs_tag<Box>::type>
354 struct box_collect_vectors
356 // Calculate on coordinate type, but if it is integer,
358 typedef typename boost::range_value<Collection>::type item_type;
359 typedef typename item_type::type calculation_type;
361 static inline void apply(Collection& collection, Box const& box)
363 typename point_type<Box>::type lower_left, lower_right,
364 upper_left, upper_right;
365 geometry::detail::assign_box_corners(box, lower_left, lower_right,
366 upper_left, upper_right);
368 typedef typename boost::range_value<Collection>::type item;
370 collection.push_back(item(get<0>(lower_left), get<1>(lower_left), 0, 1));
371 collection.push_back(item(get<0>(upper_left), get<1>(upper_left), 1, 0));
372 collection.push_back(item(get<0>(upper_right), get<1>(upper_right), 0, -1));
373 collection.push_back(item(get<0>(lower_right), get<1>(lower_right), -1, 0));
377 // NOTE: This is not fully correct because Box in spherical and geographic
378 // cordinate systems cannot be seen as Polygon
379 template <typename Box, typename Collection>
380 struct box_collect_vectors<Box, Collection, spherical_equatorial_tag>
382 static inline void apply(Collection& collection, Box const& box)
384 typename point_type<Box>::type lower_left, lower_right,
385 upper_left, upper_right;
386 geometry::detail::assign_box_corners(box, lower_left, lower_right,
387 upper_left, upper_right);
389 typedef typename boost::range_value<Collection>::type item;
391 collection.push_back(item(lower_left, upper_left));
392 collection.push_back(item(upper_left, upper_right));
393 collection.push_back(item(upper_right, lower_right));
394 collection.push_back(item(lower_right, lower_left));
398 template <typename Box, typename Collection>
399 struct box_collect_vectors<Box, Collection, spherical_polar_tag>
400 : box_collect_vectors<Box, Collection, spherical_equatorial_tag>
403 template <typename Box, typename Collection>
404 struct box_collect_vectors<Box, Collection, geographic_tag>
405 : box_collect_vectors<Box, Collection, spherical_equatorial_tag>
409 template <typename Polygon, typename Collection>
410 struct polygon_collect_vectors
412 static inline void apply(Collection& collection, Polygon const& polygon)
414 typedef typename geometry::ring_type<Polygon>::type ring_type;
416 typedef range_collect_vectors<ring_type, Collection> per_range;
417 per_range::apply(collection, exterior_ring(polygon));
419 typename interior_return_type<Polygon const>::type
420 rings = interior_rings(polygon);
421 for (typename detail::interior_iterator<Polygon const>::type
422 it = boost::begin(rings); it != boost::end(rings); ++it)
424 per_range::apply(collection, *it);
430 template <typename MultiGeometry, typename Collection, typename SinglePolicy>
431 struct multi_collect_vectors
433 static inline void apply(Collection& collection, MultiGeometry const& multi)
435 for (typename boost::range_iterator<MultiGeometry const>::type
436 it = boost::begin(multi);
437 it != boost::end(multi);
440 SinglePolicy::apply(collection, *it);
446 }} // namespace detail::collect_vectors
447 #endif // DOXYGEN_NO_DETAIL
451 #ifndef DOXYGEN_NO_DISPATCH
462 struct collect_vectors
464 static inline void apply(Collection&, Geometry const&)
469 template <typename Collection, typename Box>
470 struct collect_vectors<box_tag, Collection, Box>
471 : detail::collect_vectors::box_collect_vectors<Box, Collection>
476 template <typename Collection, typename Ring>
477 struct collect_vectors<ring_tag, Collection, Ring>
478 : detail::collect_vectors::range_collect_vectors<Ring, Collection>
482 template <typename Collection, typename LineString>
483 struct collect_vectors<linestring_tag, Collection, LineString>
484 : detail::collect_vectors::range_collect_vectors<LineString, Collection>
488 template <typename Collection, typename Polygon>
489 struct collect_vectors<polygon_tag, Collection, Polygon>
490 : detail::collect_vectors::polygon_collect_vectors<Polygon, Collection>
494 template <typename Collection, typename MultiPolygon>
495 struct collect_vectors<multi_polygon_tag, Collection, MultiPolygon>
496 : detail::collect_vectors::multi_collect_vectors
500 detail::collect_vectors::polygon_collect_vectors
502 typename boost::range_value<MultiPolygon>::type,
510 } // namespace dispatch
515 \ingroup collect_vectors
516 \tparam Collection Collection type, should be e.g. std::vector<>
517 \tparam Geometry geometry type
518 \param collection the collection of vectors
519 \param geometry the geometry to make collect_vectors
521 template <typename Collection, typename Geometry>
522 inline void collect_vectors(Collection& collection, Geometry const& geometry)
524 concepts::check<Geometry const>();
526 dispatch::collect_vectors
528 typename tag<Geometry>::type,
531 >::apply(collection, geometry);
535 }} // namespace boost::geometry
538 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_COLLECT_VECTORS_HPP