summaryrefslogtreecommitdiff
path: root/boost/geometry/algorithms/detail/equals/collect_vectors.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/geometry/algorithms/detail/equals/collect_vectors.hpp')
-rw-r--r--boost/geometry/algorithms/detail/equals/collect_vectors.hpp285
1 files changed, 247 insertions, 38 deletions
diff --git a/boost/geometry/algorithms/detail/equals/collect_vectors.hpp b/boost/geometry/algorithms/detail/equals/collect_vectors.hpp
index dc1cd74900..eab73ea680 100644
--- a/boost/geometry/algorithms/detail/equals/collect_vectors.hpp
+++ b/boost/geometry/algorithms/detail/equals/collect_vectors.hpp
@@ -19,11 +19,14 @@
#include <boost/numeric/conversion/cast.hpp>
#include <boost/geometry/algorithms/detail/interior_iterator.hpp>
+#include <boost/geometry/algorithms/detail/normalize.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/core/interior_rings.hpp>
#include <boost/geometry/core/tags.hpp>
+#include <boost/geometry/formulas/spherical.hpp>
+
#include <boost/geometry/geometries/concepts/check.hpp>
#include <boost/geometry/util/math.hpp>
@@ -33,31 +36,61 @@
namespace boost { namespace geometry
{
-// TODO: if Boost.LA of Emil Dotchevski is accepted, adapt this
-template <typename T>
+// Since these vectors (though ray would be a better name) are used in the
+// implementation of equals() for Areal geometries the internal representation
+// should be consistent with the default side strategy for CS because currently
+// it's used in other relops.
+
+template <
+ typename T,
+ typename Geometry,
+ typename CSTag = typename cs_tag<Geometry>::type
+>
struct collected_vector
{
typedef T type;
-
+
inline collected_vector()
{}
inline collected_vector(T const& px, T const& py,
- T const& pdx, T const& pdy)
+ T const& pdx, T const& pdy)
: x(px)
, y(py)
, dx(pdx)
, dy(pdy)
- , dx_0(T())
- , dy_0(T())
+ //, dx_0(dx)
+ //, dy_0(dy)
{}
- T x, y;
- T dx, dy;
- T dx_0, dy_0;
+ template <typename Point>
+ inline collected_vector(Point const& p1, Point const& p2)
+ : x(get<0>(p1))
+ , y(get<1>(p1))
+ , dx(get<0>(p2) - x)
+ , dy(get<1>(p2) - y)
+ //, dx_0(dx)
+ //, dy_0(dy)
+ {}
+
+ bool normalize()
+ {
+ T magnitude = math::sqrt(
+ boost::numeric_cast<T>(dx * dx + dy * dy));
+
+ // NOTE: shouldn't here math::equals() be called?
+ if (magnitude > 0)
+ {
+ dx /= magnitude;
+ dy /= magnitude;
+ return true;
+ }
+
+ return false;
+ }
// For sorting
- inline bool operator<(collected_vector<T> const& other) const
+ inline bool operator<(collected_vector const& other) const
{
if (math::equals(x, other.x))
{
@@ -74,7 +107,21 @@ struct collected_vector
return x < other.x;
}
- inline bool same_direction(collected_vector<T> const& other) const
+ inline bool next_is_collinear(collected_vector const& other) const
+ {
+ return same_direction(other);
+ }
+
+ // For std::equals
+ inline bool operator==(collected_vector const& other) const
+ {
+ return math::equals(x, other.x)
+ && math::equals(y, other.y)
+ && same_direction(other);
+ }
+
+private:
+ inline bool same_direction(collected_vector const& other) const
{
// For high precision arithmetic, we have to be
// more relaxed then using ==
@@ -84,13 +131,156 @@ struct collected_vector
&& math::equals_with_epsilon(dy, other.dy);
}
+ T x, y;
+ T dx, dy;
+ //T dx_0, dy_0;
+};
+
+template <typename T, typename Geometry>
+struct collected_vector<T, Geometry, spherical_equatorial_tag>
+{
+ typedef T type;
+
+ typedef typename coordinate_system<Geometry>::type cs_type;
+ typedef model::point<T, 2, cs_type> point_type;
+ typedef model::point<T, 3, cs::cartesian> vector_type;
+
+ collected_vector()
+ {}
+
+ template <typename Point>
+ collected_vector(Point const& p1, Point const& p2)
+ : origin(get<0>(p1), get<1>(p1))
+ {
+ origin = detail::return_normalized<point_type>(origin);
+
+ using namespace geometry::formula;
+ prev = sph_to_cart3d<vector_type>(p1);
+ next = sph_to_cart3d<vector_type>(p2);
+ direction = cross_product(prev, next);
+ }
+
+ bool normalize()
+ {
+ T magnitude_sqr = dot_product(direction, direction);
+
+ // NOTE: shouldn't here math::equals() be called?
+ if (magnitude_sqr > 0)
+ {
+ divide_value(direction, math::sqrt(magnitude_sqr));
+ return true;
+ }
+
+ return false;
+ }
+
+ bool operator<(collected_vector const& other) const
+ {
+ if (math::equals(get<0>(origin), get<0>(other.origin)))
+ {
+ if (math::equals(get<1>(origin), get<1>(other.origin)))
+ {
+ if (math::equals(get<0>(direction), get<0>(other.direction)))
+ {
+ if (math::equals(get<1>(direction), get<1>(other.direction)))
+ {
+ return get<2>(direction) < get<2>(other.direction);
+ }
+ return get<1>(direction) < get<1>(other.direction);
+ }
+ return get<0>(direction) < get<0>(other.direction);
+ }
+ return get<1>(origin) < get<1>(other.origin);
+ }
+ return get<0>(origin) < get<0>(other.origin);
+ }
+
+ // For consistency with side and intersection strategies used by relops
+ // IMPORTANT: this method should be called for previous vector
+ // and next vector should be passed as parameter
+ bool next_is_collinear(collected_vector const& other) const
+ {
+ return formula::sph_side_value(direction, other.next) == 0;
+ }
+
// For std::equals
- inline bool operator==(collected_vector<T> const& other) const
+ bool operator==(collected_vector const& other) const
{
- return math::equals(x, other.x)
- && math::equals(y, other.y)
- && same_direction(other);
+ return math::equals(get<0>(origin), get<0>(other.origin))
+ && math::equals(get<1>(origin), get<1>(other.origin))
+ && is_collinear(other);
+ }
+
+private:
+ // For consistency with side and intersection strategies used by relops
+ bool is_collinear(collected_vector const& other) const
+ {
+ return formula::sph_side_value(direction, other.prev) == 0
+ && formula::sph_side_value(direction, other.next) == 0;
}
+
+ /*bool same_direction(collected_vector const& other) const
+ {
+ return math::equals_with_epsilon(get<0>(direction), get<0>(other.direction))
+ && math::equals_with_epsilon(get<1>(direction), get<1>(other.direction))
+ && math::equals_with_epsilon(get<2>(direction), get<2>(other.direction));
+ }*/
+
+ point_type origin; // used for sorting and equality check
+ vector_type direction; // used for sorting, only in operator<
+ vector_type prev; // used for collinearity check, only in operator==
+ vector_type next; // used for collinearity check
+};
+
+template <typename T, typename Geometry>
+struct collected_vector<T, Geometry, spherical_polar_tag>
+ : public collected_vector<T, Geometry, spherical_equatorial_tag>
+{
+ typedef collected_vector<T, Geometry, spherical_equatorial_tag> base_type;
+
+ collected_vector() {}
+
+ template <typename Point>
+ collected_vector(Point const& p1, Point const& p2)
+ : base_type(to_equatorial(p1), to_equatorial(p2))
+ {}
+
+private:
+ template <typename Point>
+ Point polar_to_equatorial(Point const& p)
+ {
+ typedef typename coordinate_type<Point>::type coord_type;
+
+ typedef math::detail::constants_on_spheroid
+ <
+ coord_type,
+ typename coordinate_system<Point>::type::units
+ > constants;
+
+ coord_type const pi_2 = constants::half_period() / 2;
+
+ Point res = p;
+ set<1>(res, pi_2 - get<1>(p));
+ return res;
+ }
+};
+
+// This is consistent with the currently used default geographic side
+// and intersection strategies. Spherical strategies are used by default.
+// When default strategies are changed in the future this specialization
+// should be changed too.
+template <typename T, typename Geometry>
+struct collected_vector<T, Geometry, geographic_tag>
+ : public collected_vector<T, Geometry, spherical_equatorial_tag>
+{
+ typedef collected_vector<T, Geometry, spherical_equatorial_tag> base_type;
+
+ collected_vector() {}
+
+ template <typename Point>
+ collected_vector(Point const& p1, Point const& p2)
+ : base_type(p1, p2)
+ {}
};
@@ -117,39 +307,26 @@ struct range_collect_vectors
typedef typename boost::range_iterator<Range const>::type iterator;
- bool first = true;
+ bool is_first = true;
iterator it = boost::begin(range);
for (iterator prev = it++;
it != boost::end(range);
prev = it++)
{
- typename boost::range_value<Collection>::type v;
-
- v.x = get<0>(*prev);
- v.y = get<1>(*prev);
- v.dx = get<0>(*it) - v.x;
- v.dy = get<1>(*it) - v.y;
- v.dx_0 = v.dx;
- v.dy_0 = v.dy;
+ typename boost::range_value<Collection>::type v(*prev, *it);
// Normalize the vector -> this results in points+direction
// and is comparible between geometries
- calculation_type magnitude = math::sqrt(
- boost::numeric_cast<calculation_type>(v.dx * v.dx + v.dy * v.dy));
-
// Avoid non-duplicate points (AND division by zero)
- if (magnitude > 0)
+ if (v.normalize())
{
- v.dx /= magnitude;
- v.dy /= magnitude;
-
// Avoid non-direction changing points
- if (first || ! v.same_direction(collection.back()))
+ if (is_first || ! collection.back().next_is_collinear(v))
{
collection.push_back(v);
}
- first = false;
+ is_first = false;
}
}
@@ -160,7 +337,7 @@ struct range_collect_vectors
typedef typename boost::range_iterator<Collection>::type c_iterator;
c_iterator first = range::pos(collection, c_old_size);
- if ( first->same_direction(collection.back()) )
+ if (collection.back().next_is_collinear(*first) )
{
//collection.erase(first);
// O(1) instead of O(N)
@@ -172,7 +349,8 @@ struct range_collect_vectors
};
-template <typename Box, typename Collection>
+// Default version (cartesian)
+template <typename Box, typename Collection, typename CSTag = typename cs_tag<Box>::type>
struct box_collect_vectors
{
// Calculate on coordinate type, but if it is integer,
@@ -183,9 +361,9 @@ struct box_collect_vectors
static inline void apply(Collection& collection, Box const& box)
{
typename point_type<Box>::type lower_left, lower_right,
- upper_left, upper_right;
+ upper_left, upper_right;
geometry::detail::assign_box_corners(box, lower_left, lower_right,
- upper_left, upper_right);
+ upper_left, upper_right);
typedef typename boost::range_value<Collection>::type item;
@@ -196,6 +374,37 @@ struct box_collect_vectors
}
};
+// NOTE: This is not fully correct because Box in spherical and geographic
+// cordinate systems cannot be seen as Polygon
+template <typename Box, typename Collection>
+struct box_collect_vectors<Box, Collection, spherical_equatorial_tag>
+{
+ static inline void apply(Collection& collection, Box const& box)
+ {
+ typename point_type<Box>::type lower_left, lower_right,
+ upper_left, upper_right;
+ geometry::detail::assign_box_corners(box, lower_left, lower_right,
+ upper_left, upper_right);
+
+ typedef typename boost::range_value<Collection>::type item;
+
+ collection.push_back(item(lower_left, upper_left));
+ collection.push_back(item(upper_left, upper_right));
+ collection.push_back(item(upper_right, lower_right));
+ collection.push_back(item(lower_right, lower_left));
+ }
+};
+
+template <typename Box, typename Collection>
+struct box_collect_vectors<Box, Collection, spherical_polar_tag>
+ : box_collect_vectors<Box, Collection, spherical_equatorial_tag>
+{};
+
+template <typename Box, typename Collection>
+struct box_collect_vectors<Box, Collection, geographic_tag>
+ : box_collect_vectors<Box, Collection, spherical_equatorial_tag>
+{};
+
template <typename Polygon, typename Collection>
struct polygon_collect_vectors
@@ -312,7 +521,7 @@ struct collect_vectors<multi_polygon_tag, Collection, MultiPolygon>
template <typename Collection, typename Geometry>
inline void collect_vectors(Collection& collection, Geometry const& geometry)
{
- concept::check<Geometry const>();
+ concepts::check<Geometry const>();
dispatch::collect_vectors
<