From 5868367628456f8f5584f4ade3b552db30573cf4 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 6 Aug 2016 13:45:59 +0200 Subject: [PATCH 01/59] [formulas] Move flattening() from algorithms/detail to formulas. --- .../geometry/formulas/andoyer_inverse.hpp | 5 ++--- .../detail => formulas}/flattening.hpp | 19 ++++++++++--------- .../geometry/formulas/gnomonic_spheroid.hpp | 3 +-- .../formulas/sjoberg_intersection.hpp | 4 ++-- .../boost/geometry/formulas/thomas_direct.hpp | 5 ++--- .../geometry/formulas/thomas_inverse.hpp | 5 ++--- .../geometry/formulas/vincenty_direct.hpp | 5 ++--- .../geometry/formulas/vincenty_inverse.hpp | 5 ++--- .../geographic/distance_andoyer.hpp | 3 +-- 9 files changed, 24 insertions(+), 30 deletions(-) rename include/boost/geometry/{algorithms/detail => formulas}/flattening.hpp (78%) diff --git a/include/boost/geometry/formulas/andoyer_inverse.hpp b/include/boost/geometry/formulas/andoyer_inverse.hpp index 1e1c661e7..9c1a419a2 100644 --- a/include/boost/geometry/formulas/andoyer_inverse.hpp +++ b/include/boost/geometry/formulas/andoyer_inverse.hpp @@ -20,9 +20,8 @@ #include #include -#include - #include +#include #include @@ -75,7 +74,7 @@ public: CT const c0 = CT(0); CT const c1 = CT(1); CT const pi = math::pi(); - CT const f = detail::flattening(spheroid); + CT const f = formula::flattening(spheroid); CT const dlon = lon2 - lon1; CT const sin_dlon = sin(dlon); diff --git a/include/boost/geometry/algorithms/detail/flattening.hpp b/include/boost/geometry/formulas/flattening.hpp similarity index 78% rename from include/boost/geometry/algorithms/detail/flattening.hpp rename to include/boost/geometry/formulas/flattening.hpp index 8ed5fd9a8..f94ead65b 100644 --- a/include/boost/geometry/algorithms/detail/flattening.hpp +++ b/include/boost/geometry/formulas/flattening.hpp @@ -1,6 +1,6 @@ // Boost.Geometry -// Copyright (c) 2014 Oracle and/or its affiliates. +// Copyright (c) 2014-2016 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -8,8 +8,8 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_FLATTENING_HPP -#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_FLATTENING_HPP +#ifndef BOOST_GEOMETRY_FORMULAS_FLATTENING_HPP +#define BOOST_GEOMETRY_FORMULAS_FLATTENING_HPP #include #include @@ -21,7 +21,7 @@ namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DISPATCH -namespace detail_dispatch +namespace formula_dispatch { template ::type> @@ -43,27 +43,28 @@ struct flattening { static inline ResultType apply(Geometry const& geometry) { + // (a - b) / a return ResultType(get_radius<0>(geometry) - get_radius<2>(geometry)) / ResultType(get_radius<0>(geometry)); } }; -} // namespace detail_dispatch +} // namespace formula_dispatch #endif // DOXYGEN_NO_DISPATCH #ifndef DOXYGEN_NO_DETAIL -namespace detail +namespace formula { template ResultType flattening(Geometry const& geometry) { - return detail_dispatch::flattening::apply(geometry); + return formula_dispatch::flattening::apply(geometry); } -} // namespace detail +} // namespace formula #endif // DOXYGEN_NO_DETAIL }} // namespace boost::geometry -#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_FLATTENING_HPP +#endif // BOOST_GEOMETRY_FORMULAS_FLATTENING_HPP diff --git a/include/boost/geometry/formulas/gnomonic_spheroid.hpp b/include/boost/geometry/formulas/gnomonic_spheroid.hpp index 78645883c..acab229d5 100644 --- a/include/boost/geometry/formulas/gnomonic_spheroid.hpp +++ b/include/boost/geometry/formulas/gnomonic_spheroid.hpp @@ -14,12 +14,11 @@ #include -#include - #include #include #include +#include #include #include #include diff --git a/include/boost/geometry/formulas/sjoberg_intersection.hpp b/include/boost/geometry/formulas/sjoberg_intersection.hpp index 45d5e07d3..7db08b35e 100644 --- a/include/boost/geometry/formulas/sjoberg_intersection.hpp +++ b/include/boost/geometry/formulas/sjoberg_intersection.hpp @@ -20,7 +20,7 @@ #include #include -#include +#include namespace boost { namespace geometry { namespace formula @@ -85,7 +85,7 @@ public: CT const pi = math::pi(); CT const pi_half = pi / c2; - CT const f = detail::flattening(spheroid); + CT const f = formula::flattening(spheroid); CT const one_minus_f = c1 - f; CT const e_sqr = f * (c2 - f); diff --git a/include/boost/geometry/formulas/thomas_direct.hpp b/include/boost/geometry/formulas/thomas_direct.hpp index 2819b392d..eebe3acd9 100644 --- a/include/boost/geometry/formulas/thomas_direct.hpp +++ b/include/boost/geometry/formulas/thomas_direct.hpp @@ -20,9 +20,8 @@ #include #include -#include - #include +#include #include @@ -82,7 +81,7 @@ public: CT const a = CT(get_radius<0>(spheroid)); CT const b = CT(get_radius<2>(spheroid)); - CT const f = detail::flattening(spheroid); + CT const f = formula::flattening(spheroid); CT const one_minus_f = c1 - f; CT const pi = math::pi(); diff --git a/include/boost/geometry/formulas/thomas_inverse.hpp b/include/boost/geometry/formulas/thomas_inverse.hpp index 1f6e828da..d0fcb4f0b 100644 --- a/include/boost/geometry/formulas/thomas_inverse.hpp +++ b/include/boost/geometry/formulas/thomas_inverse.hpp @@ -20,9 +20,8 @@ #include #include -#include - #include +#include #include @@ -78,7 +77,7 @@ public: CT const c4 = 4; CT const pi_half = math::pi() / c2; - CT const f = detail::flattening(spheroid); + CT const f = formula::flattening(spheroid); CT const one_minus_f = c1 - f; // CT const tan_theta1 = one_minus_f * tan(lat1); diff --git a/include/boost/geometry/formulas/vincenty_direct.hpp b/include/boost/geometry/formulas/vincenty_direct.hpp index f3647ff4e..1697e5fb6 100644 --- a/include/boost/geometry/formulas/vincenty_direct.hpp +++ b/include/boost/geometry/formulas/vincenty_direct.hpp @@ -23,9 +23,8 @@ #include #include -#include - #include +#include #include @@ -85,7 +84,7 @@ public: CT const radius_a = CT(get_radius<0>(spheroid)); CT const radius_b = CT(get_radius<2>(spheroid)); - CT const flattening = geometry::detail::flattening(spheroid); + CT const flattening = formula::flattening(spheroid); CT const sin_azimuth12 = sin(azimuth12); CT const cos_azimuth12 = cos(azimuth12); diff --git a/include/boost/geometry/formulas/vincenty_inverse.hpp b/include/boost/geometry/formulas/vincenty_inverse.hpp index bbda00036..e13ddf30e 100644 --- a/include/boost/geometry/formulas/vincenty_inverse.hpp +++ b/include/boost/geometry/formulas/vincenty_inverse.hpp @@ -23,9 +23,8 @@ #include #include -#include - #include +#include #include @@ -99,7 +98,7 @@ public: CT const radius_a = CT(get_radius<0>(spheroid)); CT const radius_b = CT(get_radius<2>(spheroid)); - CT const flattening = geometry::detail::flattening(spheroid); + CT const flattening = formula::flattening(spheroid); // U: reduced latitude, defined by tan U = (1-f) tan phi CT const one_min_f = c1 - flattening; diff --git a/include/boost/geometry/strategies/geographic/distance_andoyer.hpp b/include/boost/geometry/strategies/geographic/distance_andoyer.hpp index 1946cd109..d04a90956 100644 --- a/include/boost/geometry/strategies/geographic/distance_andoyer.hpp +++ b/include/boost/geometry/strategies/geographic/distance_andoyer.hpp @@ -20,9 +20,8 @@ #include #include -#include - #include +#include #include From 9a63204ac88abe706df408da3a446ed54b01a94f Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 6 Aug 2016 13:48:15 +0200 Subject: [PATCH 02/59] [formulas] Add eccentricity_sqr() and geo<->cart3d conversions. --- .../geometry/formulas/eccentricity_sqr.hpp | 70 ++++++++++++ .../boost/geometry/formulas/geographic.hpp | 107 ++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 include/boost/geometry/formulas/eccentricity_sqr.hpp create mode 100644 include/boost/geometry/formulas/geographic.hpp diff --git a/include/boost/geometry/formulas/eccentricity_sqr.hpp b/include/boost/geometry/formulas/eccentricity_sqr.hpp new file mode 100644 index 000000000..01a9beacb --- /dev/null +++ b/include/boost/geometry/formulas/eccentricity_sqr.hpp @@ -0,0 +1,70 @@ +// Boost.Geometry + +// Copyright (c) 2016 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_FORMULAS_ECCENCRICITY_SQR_HPP +#define BOOST_GEOMETRY_FORMULAS_ECCENCRICITY_SQR_HPP + +#include +#include +#include + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace formula_dispatch +{ + +template ::type> +struct eccentricity_sqr + : not_implemented +{}; + +template +struct eccentricity_sqr +{ + static inline ResultType apply(Geometry const& /*geometry*/) + { + return ResultType(0); + } +}; + +template +struct eccentricity_sqr +{ + static inline ResultType apply(Geometry const& geometry) + { + // 1 - (b / a)^2 + return ResultType(1) - math::sqr(ResultType(get_radius<2>(geometry)) + / ResultType(get_radius<0>(geometry))); + } +}; + +} // namespace formula_dispatch +#endif // DOXYGEN_NO_DISPATCH + +#ifndef DOXYGEN_NO_DETAIL +namespace formula +{ + +template +ResultType eccentricity_sqr(Geometry const& geometry) +{ + return formula_dispatch::eccentricity_sqr::apply(geometry); +} + +} // namespace formula +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_FORMULAS_ECCENCRICITY_SQR_HPP diff --git a/include/boost/geometry/formulas/geographic.hpp b/include/boost/geometry/formulas/geographic.hpp new file mode 100644 index 000000000..8485b2346 --- /dev/null +++ b/include/boost/geometry/formulas/geographic.hpp @@ -0,0 +1,107 @@ +// Boost.Geometry + +// Copyright (c) 2016, Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_FORMULAS_GEOGRAPHIC_HPP +#define BOOST_GEOMETRY_FORMULAS_GEOGRAPHIC_HPP + +#include +#include +#include +#include + +//#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace boost { namespace geometry { + +namespace formula { + +template +static inline Point3d geo_to_cart3d(PointGeo const& point_geo, Spheroid const& spheroid) +{ + typedef typename coordinate_type::type calc_t; + + calc_t const c1 = 1; + calc_t const e_sqr = eccentricity_sqr(spheroid); + + calc_t const lon = get_as_radian<0>(point_geo); + calc_t const lat = get_as_radian<1>(point_geo); + + Point3d res; + + calc_t const sin_lat = sin(lat); + // "unit" spheroid, a = 1 + calc_t const N = c1 / math::sqrt(c1 - e_sqr * math::sqr(sin_lat)); + calc_t const N_cos_lat = N * cos(lat); + + set<0>(res, N_cos_lat * cos(lon)); + set<1>(res, N_cos_lat * sin(lon)); + set<2>(res, N * (c1 - e_sqr) * sin_lat); + + return res; +} + +template +static inline PointGeo cart3d_to_geo(Point3d const& point_3d, Spheroid const& spheroid) +{ + typedef typename coordinate_type::type coord_t; + typedef typename coordinate_type::type calc_t; + + calc_t const c1 = 1; + //calc_t const c2 = 2; + calc_t const e_sqr = eccentricity_sqr(spheroid); + + calc_t const x = get<0>(point_3d); + calc_t const y = get<1>(point_3d); + calc_t const z = get<2>(point_3d); + calc_t const xy_l = math::sqrt(math::sqr(x) + math::sqr(y)); + + calc_t const lonr = atan2(y, x); + // http://www.iag-aig.org/attach/989c8e501d9c5b5e2736955baf2632f5/V60N2_5FT.pdf + //calc_t const lonr = c2 * atan2(y, x + xy_l); + + calc_t const latr = atan2(z, (c1 - e_sqr) * xy_l); + + // TODO: adjust the result iteratively? + // http://www.navipedia.net/index.php/Ellipsoidal_and_Cartesian_Coordinates_Conversion + // It looks good as it is, at least for "unit" spheroid + + PointGeo res; + + set_from_radian<0>(res, lonr); + set_from_radian<1>(res, latr); + + coord_t lon = get<0>(res); + coord_t lat = get<1>(res); + + math::normalize_spheroidal_coordinates + < + typename coordinate_system::type::units, + coord_t + >(lon, lat); + + set<0>(res, lon); + set<1>(res, lat); + + return res; +} + +} // namespace formula + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_FORMULAS_GEOGRAPHIC_HPP From 65bb0dcb41b328f6695945932cec252b0531301c Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 9 Aug 2016 21:02:54 +0200 Subject: [PATCH 03/59] [formulas] Add geo_to_cart3d() calculating north and east vectors together with converted point. --- .../boost/geometry/formulas/geographic.hpp | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/include/boost/geometry/formulas/geographic.hpp b/include/boost/geometry/formulas/geographic.hpp index 8485b2346..873c525d5 100644 --- a/include/boost/geometry/formulas/geographic.hpp +++ b/include/boost/geometry/formulas/geographic.hpp @@ -44,6 +44,7 @@ static inline Point3d geo_to_cart3d(PointGeo const& point_geo, Spheroid const& s Point3d res; calc_t const sin_lat = sin(lat); + // "unit" spheroid, a = 1 calc_t const N = c1 / math::sqrt(c1 - e_sqr * math::sqr(sin_lat)); calc_t const N_cos_lat = N * cos(lat); @@ -55,6 +56,39 @@ static inline Point3d geo_to_cart3d(PointGeo const& point_geo, Spheroid const& s return res; } +template +static inline void geo_to_cart3d(PointGeo const& point_geo, Point3d & result, Point3d & north, Point3d & east, Spheroid const& spheroid) +{ + typedef typename coordinate_type::type calc_t; + + calc_t const c1 = 1; + calc_t const e_sqr = eccentricity_sqr(spheroid); + + calc_t const lon = get_as_radian<0>(point_geo); + calc_t const lat = get_as_radian<1>(point_geo); + + calc_t const sin_lon = sin(lon); + calc_t const cos_lon = cos(lon); + calc_t const sin_lat = sin(lat); + calc_t const cos_lat = cos(lat); + + // "unit" spheroid, a = 1 + calc_t const N = c1 / math::sqrt(c1 - e_sqr * math::sqr(sin_lat)); + calc_t const N_cos_lat = N * cos_lat; + + set<0>(result, N_cos_lat * cos_lon); + set<1>(result, N_cos_lat * sin_lon); + set<2>(result, N * (c1 - e_sqr) * sin_lat); + + set<0>(east, -sin_lon); + set<1>(east, cos_lon); + set<2>(east, 0); + + set<0>(north, -sin_lat * cos_lon); + set<1>(north, -sin_lat * sin_lon); + set<2>(north, cos_lat); +} + template static inline PointGeo cart3d_to_geo(Point3d const& point_3d, Spheroid const& spheroid) { @@ -71,14 +105,16 @@ static inline PointGeo cart3d_to_geo(Point3d const& point_3d, Spheroid const& sp calc_t const xy_l = math::sqrt(math::sqr(x) + math::sqr(y)); calc_t const lonr = atan2(y, x); + + // NOTE: Alternative version // http://www.iag-aig.org/attach/989c8e501d9c5b5e2736955baf2632f5/V60N2_5FT.pdf - //calc_t const lonr = c2 * atan2(y, x + xy_l); + // calc_t const lonr = c2 * atan2(y, x + xy_l); calc_t const latr = atan2(z, (c1 - e_sqr) * xy_l); - // TODO: adjust the result iteratively? + // NOTE: If h is equal to 0 then there is no need to improve value of latitude + // because then N_i / (N_i + h_i) = 1 // http://www.navipedia.net/index.php/Ellipsoidal_and_Cartesian_Coordinates_Conversion - // It looks good as it is, at least for "unit" spheroid PointGeo res; From 84e3ae417a8cf84b6c08f0aef68491a24ec70f3f Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 10 Aug 2016 13:55:06 +0200 Subject: [PATCH 04/59] [formulas] Add projected_to_xy() function for 3d point on ellipsoid. A function calculates the coordinates of the intersection of a line perpendicular to the surface of ellipsoid and intersecting XY plane at geodesic latitude angle. --- .../boost/geometry/formulas/geographic.hpp | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/include/boost/geometry/formulas/geographic.hpp b/include/boost/geometry/formulas/geographic.hpp index 873c525d5..6d267aa51 100644 --- a/include/boost/geometry/formulas/geographic.hpp +++ b/include/boost/geometry/formulas/geographic.hpp @@ -31,7 +31,7 @@ namespace boost { namespace geometry { namespace formula { template -static inline Point3d geo_to_cart3d(PointGeo const& point_geo, Spheroid const& spheroid) +inline Point3d geo_to_cart3d(PointGeo const& point_geo, Spheroid const& spheroid) { typedef typename coordinate_type::type calc_t; @@ -57,7 +57,7 @@ static inline Point3d geo_to_cart3d(PointGeo const& point_geo, Spheroid const& s } template -static inline void geo_to_cart3d(PointGeo const& point_geo, Point3d & result, Point3d & north, Point3d & east, Spheroid const& spheroid) +inline void geo_to_cart3d(PointGeo const& point_geo, Point3d & result, Point3d & north, Point3d & east, Spheroid const& spheroid) { typedef typename coordinate_type::type calc_t; @@ -90,7 +90,7 @@ static inline void geo_to_cart3d(PointGeo const& point_geo, Point3d & result, Po } template -static inline PointGeo cart3d_to_geo(Point3d const& point_3d, Spheroid const& spheroid) +inline PointGeo cart3d_to_geo(Point3d const& point_3d, Spheroid const& spheroid) { typedef typename coordinate_type::type coord_t; typedef typename coordinate_type::type calc_t; @@ -136,6 +136,32 @@ static inline PointGeo cart3d_to_geo(Point3d const& point_3d, Spheroid const& sp return res; } +template +inline Point3d projected_to_xy(Point3d const& point_3d, Spheroid const& spheroid) +{ + typedef typename coordinate_type::type coord_t; + + // len_xy = sqrt(x^2 + y^2) + // r = len_xy - |z / tan(lat)| + // assuming h = 0 + // lat = atan2(z, (1 - e^2) * len_xy); + // |z / tan(lat)| = (1 - e^2) * len_xy + // r = e^2 * len_xy + // x_res = r * cos(lon) = e^2 * len_xy * x / len_xy = e^2 * x + // y_res = r * sin(lon) = e^2 * len_xy * y / len_xy = e^2 * y + + coord_t const c0 = 0; + coord_t const e_sqr = bg::formula::eccentricity_sqr(spheroid); + + Point3d res; + + bg::set<0>(res, e_sqr * bg::get<0>(point_3d)); + bg::set<1>(res, e_sqr * bg::get<1>(point_3d)); + bg::set<2>(res, c0); + + return res; +} + } // namespace formula }} // namespace boost::geometry From b30806971a499ef2ea0f86dda217a588ca8f6049 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 10 Aug 2016 23:15:52 +0200 Subject: [PATCH 05/59] [formulas] Add projected_to_surface() function calculating the intersection of 3d ray and surface of unit spheroid --- .../boost/geometry/formulas/geographic.hpp | 92 ++++++++++++++++++- 1 file changed, 88 insertions(+), 4 deletions(-) diff --git a/include/boost/geometry/formulas/geographic.hpp b/include/boost/geometry/formulas/geographic.hpp index 6d267aa51..34f9f83ca 100644 --- a/include/boost/geometry/formulas/geographic.hpp +++ b/include/boost/geometry/formulas/geographic.hpp @@ -151,17 +151,101 @@ inline Point3d projected_to_xy(Point3d const& point_3d, Spheroid const& spheroid // y_res = r * sin(lon) = e^2 * len_xy * y / len_xy = e^2 * y coord_t const c0 = 0; - coord_t const e_sqr = bg::formula::eccentricity_sqr(spheroid); + coord_t const e_sqr = formula::eccentricity_sqr(spheroid); Point3d res; - bg::set<0>(res, e_sqr * bg::get<0>(point_3d)); - bg::set<1>(res, e_sqr * bg::get<1>(point_3d)); - bg::set<2>(res, c0); + set<0>(res, e_sqr * get<0>(point_3d)); + set<1>(res, e_sqr * get<1>(point_3d)); + set<2>(res, c0); return res; } +template +inline Point3d projected_to_surface(Point3d const& direction, Spheroid const& spheroid) +{ + typedef typename coordinate_type::type coord_t; + + coord_t const c0 = 0; + coord_t const c2 = 2; + coord_t const c4 = 4; + + // calculate the point of intersection of a ray and spheroid's surface + // the origin is the origin of the coordinate system + //(x*x+y*y)/(a*a) + z*z/(b*b) = 1 + // x = d.x * t + // y = d.y * t + // z = d.z * t + coord_t const dx = get<0>(direction); + coord_t const dy = get<1>(direction); + coord_t const dz = get<2>(direction); + + //coord_t const a_sqr = math::sqr(get_radius<0>(spheroid)); + //coord_t const b_sqr = math::sqr(get_radius<2>(spheroid)); + // "unit" spheroid, a = 1 + coord_t const a_sqr = 1; + coord_t const b_sqr = math::sqr(get_radius<2>(spheroid) / get_radius<0>(spheroid)); + + coord_t const param_a = (dx*dx + dy*dy) / a_sqr + dz*dz / b_sqr; + + coord_t const delta = c4 * param_a; + coord_t const t = delta >= c0 ? + math::sqrt(delta) / (c2 * param_a) : + c0; + + // result = direction * t + point_3d result = direction; + multiply_value(result, t); + + return result; +} + +template +inline Point3d projected_to_surface(Point3d const& origin, Point3d const& direction, Spheroid const& spheroid) +{ + typedef typename coordinate_type::type coord_t; + + coord_t const c0 = 0; + coord_t const c1 = 1; + coord_t const c2 = 2; + coord_t const c4 = 4; + + // calculate the point of intersection of a ray and spheroid's surface + //(x*x+y*y)/(a*a) + z*z/(b*b) = 1 + // x = o.x + d.x * t + // y = o.y + d.y * t + // z = o.z + d.z * t + coord_t const ox = get<0>(origin); + coord_t const oy = get<1>(origin); + coord_t const oz = get<2>(origin); + coord_t const dx = get<0>(direction); + coord_t const dy = get<1>(direction); + coord_t const dz = get<2>(direction); + + //coord_t const a_sqr = math::sqr(get_radius<0>(spheroid)); + //coord_t const b_sqr = math::sqr(get_radius<2>(spheroid)); + // "unit" spheroid, a = 1 + coord_t const a_sqr = 1; + coord_t const b_sqr = math::sqr(get_radius<2>(spheroid) / get_radius<0>(spheroid)); + + coord_t const param_a = (dx*dx + dy*dy) / a_sqr + dz*dz / b_sqr; + coord_t const param_b = c2 * ((ox*dx + oy*dy) / a_sqr + oz*dz / b_sqr); + coord_t const param_c = (ox*ox + oy*oy) / a_sqr + oz*oz / b_sqr - c1; + + coord_t const delta = param_b*param_b - c4 * param_a*param_c; + coord_t const t = delta >= c0 ? + (-param_b + math::sqrt(delta)) / (c2 * param_a) : + c0; + + // result = origin + direction * t + point_3d result = direction; + multiply_value(result, t); + add_point(result, origin); + + return result; +} + } // namespace formula }} // namespace boost::geometry From 613fcedd7a4708aba632a10b6e0767de3bf25a72 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Mon, 15 Aug 2016 04:32:37 +0200 Subject: [PATCH 06/59] [formulas] Add elliptic intersection and improve projected_to_surface(). --- .../boost/geometry/formulas/geographic.hpp | 187 ++++++++++++++++-- 1 file changed, 173 insertions(+), 14 deletions(-) diff --git a/include/boost/geometry/formulas/geographic.hpp b/include/boost/geometry/formulas/geographic.hpp index 34f9f83ca..7a1809f79 100644 --- a/include/boost/geometry/formulas/geographic.hpp +++ b/include/boost/geometry/formulas/geographic.hpp @@ -188,21 +188,19 @@ inline Point3d projected_to_surface(Point3d const& direction, Spheroid const& sp coord_t const b_sqr = math::sqr(get_radius<2>(spheroid) / get_radius<0>(spheroid)); coord_t const param_a = (dx*dx + dy*dy) / a_sqr + dz*dz / b_sqr; - coord_t const delta = c4 * param_a; - coord_t const t = delta >= c0 ? - math::sqrt(delta) / (c2 * param_a) : - c0; + // delta >= 0 + coord_t const t = math::sqrt(delta) / (c2 * param_a); // result = direction * t - point_3d result = direction; + Point3d result = direction; multiply_value(result, t); return result; } template -inline Point3d projected_to_surface(Point3d const& origin, Point3d const& direction, Spheroid const& spheroid) +inline bool projected_to_surface(Point3d const& origin, Point3d const& direction, Point3d & result1, Point3d & result2, Spheroid const& spheroid) { typedef typename coordinate_type::type coord_t; @@ -233,17 +231,178 @@ inline Point3d projected_to_surface(Point3d const& origin, Point3d const& direct coord_t const param_b = c2 * ((ox*dx + oy*dy) / a_sqr + oz*dz / b_sqr); coord_t const param_c = (ox*ox + oy*oy) / a_sqr + oz*oz / b_sqr - c1; - coord_t const delta = param_b*param_b - c4 * param_a*param_c; - coord_t const t = delta >= c0 ? - (-param_b + math::sqrt(delta)) / (c2 * param_a) : - c0; + coord_t const delta = math::sqr(param_b) - c4 * param_a*param_c; + + if (delta < c0) + { + return false; + } // result = origin + direction * t - point_3d result = direction; - multiply_value(result, t); - add_point(result, origin); - return result; + coord_t const sqrt_delta = math::sqrt(delta); + coord_t const two_a = c2 * param_a; + + coord_t const t1 = (-param_b + sqrt_delta) / two_a; + result1 = direction; + multiply_value(result1, t1); + add_point(result1, origin); + + coord_t const t2 = (-param_b - sqrt_delta) / two_a; + result2 = direction; + multiply_value(result2, t2); + add_point(result2, origin); + + return true; +} + +template +inline bool great_elliptic_intersection(Point3d const& a1, Point3d const& a2, + Point3d const& b1, Point3d const& b2, + Point3d & result, + Spheroid const& spheroid) +{ + typedef typename coordinate_type::type coord_t; + + coord_t c0 = 0; + + Point3d n1 = cross_product(a1, a2); + Point3d n2 = cross_product(b1, b2); + + // intersection direction + Point3d id = cross_product(n1, n2); + coord_t id_len_sqr = dot_product(id, id); + + if (math::equals(id_len_sqr, c0)) + { + return false; + } + + result = projected_to_surface(id, spheroid); + + return true; +} + +template +inline bool elliptic_intersection(Point3d const& a1, Point3d const& a2, + Point3d const& b1, Point3d const& b2, + Point3d & result, + Spheroid const& spheroid) +{ + typedef typename coordinate_type::type coord_t; + + coord_t c0 = 0; + coord_t c1 = 1; + + Point3d axy1 = projected_to_xy(a1, spheroid); + Point3d axy2 = projected_to_xy(a2, spheroid); + Point3d bxy1 = projected_to_xy(b1, spheroid); + Point3d bxy2 = projected_to_xy(b2, spheroid); + + // axy12 = axy2 - axy1; + Point3d axy12 = axy2; + subtract_point(axy12, axy1); + // bxy12 = bxy2 - bxy1; + Point3d bxy12 = bxy2; + subtract_point(bxy12, bxy1); + + // aorig = axy1 + axy12 * 0.5; + Point3d aorig = axy12; + multiply_value(aorig, coord_t(0.5)); + add_point(aorig, axy1); + // borig = bxy1 + bxy12 * 0.5; + Point3d borig = bxy12; + multiply_value(borig, coord_t(0.5)); + add_point(borig, bxy1); + + // av1 = a1 - aorig + Point3d a1v = a1; + subtract_point(a1v, aorig); + // av2 = a2 - aorig + Point3d a2v = a2; + subtract_point(a2v, aorig); + // bv1 = b1 - borig + Point3d b1v = b1; + subtract_point(b1v, borig); + // bv2 = b2 - borig + Point3d b2v = b2; + subtract_point(b2v, borig); + + Point3d n1 = cross_product(a1v, a2v); + Point3d n2 = cross_product(b1v, b2v); + + coord_t n1_len = math::sqrt(dot_product(n1, n1)); + coord_t n2_len = math::sqrt(dot_product(n2, n2)); + + if (math::equals(n1_len, c0) || math::equals(n2_len, c0)) + { + return false; + } + + // normalize + divide_value(n1, n1_len); + divide_value(n2, n2_len); + + // Below + // n . (p - o) = 0 + // n . p - n . o = 0 + // n . p + d = 0 + // n . p = h + + coord_t dot_n1_n2 = dot_product(n1, n2); + coord_t denom = c1 - math::sqr(dot_n1_n2); + + // equivalent to |n1 x n2| = 0 + if (math::equals(denom, c0)) + { + return false; + } + + // intersection direction + Point3d id = cross_product(n1, n2); + + coord_t h1 = dot_product(n1, aorig); + coord_t h2 = dot_product(n2, borig); + + coord_t C1 = (h1 - h2 * dot_n1_n2) / denom; + coord_t C2 = (h2 - h1 * dot_n1_n2) / denom; + + // C1 * n1 + C2 * n2 + Point3d C1_n1 = n1; + multiply_value(C1_n1, C1); + Point3d C2_n2 = n2; + multiply_value(C2_n2, C2); + Point3d io = C1_n1; + add_point(io, C2_n2); + + Point3d ip1_s, ip2_s; + if (! projected_to_surface(io, id, ip1_s, ip2_s, spheroid)) + { + return false; + } + + coord_t a1v_len = math::sqrt(dot_product(a1v, a1v)); + divide_value(a1v, a1v_len); + coord_t a2v_len = math::sqrt(dot_product(a2v, a2v)); + divide_value(a2v, a2v_len); + coord_t b1v_len = math::sqrt(dot_product(b1v, b1v)); + divide_value(b1v, a1v_len); + coord_t b2v_len = math::sqrt(dot_product(b2v, b2v)); + divide_value(b2v, a2v_len); + + // NOTE: could be used to calculate comparable distances/angles + coord_t cos_a1i = dot_product(a1v, id); + coord_t cos_a2i = dot_product(a2v, id); + coord_t gri = bg::math::detail::greatest(cos_a1i, cos_a2i); + Point3d neg_id = id; + multiply_value(neg_id, -c1); + coord_t cos_a1ni = dot_product(a1v, neg_id); + coord_t cos_a2ni = dot_product(a2v, neg_id); + coord_t grni = bg::math::detail::greatest(cos_a1ni, cos_a2ni); + + result = gri >= grni ? ip1_s : ip2_s; + + return true; } } // namespace formula From 0e9603c4fd8a6056aba8f6c476e29baf75c3d56f Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 16 Aug 2016 03:05:06 +0200 Subject: [PATCH 07/59] [formulas] Handle special cases (degenerated segments, parallel planes, rays missing spheroid) --- .../boost/geometry/formulas/geographic.hpp | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/include/boost/geometry/formulas/geographic.hpp b/include/boost/geometry/formulas/geographic.hpp index 7a1809f79..136f3e4d7 100644 --- a/include/boost/geometry/formulas/geographic.hpp +++ b/include/boost/geometry/formulas/geographic.hpp @@ -15,7 +15,7 @@ #include #include -//#include +#include #include #include @@ -233,7 +233,8 @@ inline bool projected_to_surface(Point3d const& origin, Point3d const& direction coord_t const delta = math::sqr(param_b) - c4 * param_a*param_c; - if (delta < c0) + // equals() ? + if (delta < c0 || param_a == 0) { return false; } @@ -265,6 +266,7 @@ inline bool great_elliptic_intersection(Point3d const& a1, Point3d const& a2, typedef typename coordinate_type::type coord_t; coord_t c0 = 0; + coord_t c1 = 1; Point3d n1 = cross_product(a1, a2); Point3d n2 = cross_product(b1, b2); @@ -278,7 +280,25 @@ inline bool great_elliptic_intersection(Point3d const& a1, Point3d const& a2, return false; } - result = projected_to_surface(id, spheroid); + // no need to normalize a1 and a2 because the intersection point on + // the opposite side of the globe is at the same distance from the origin + coord_t cos_a1i = dot_product(a1, id); + coord_t cos_a2i = dot_product(a2, id); + coord_t gri = math::detail::greatest(cos_a1i, cos_a2i); + Point3d neg_id = id; + multiply_value(neg_id, -c1); + coord_t cos_a1ni = dot_product(a1, neg_id); + coord_t cos_a2ni = dot_product(a2, neg_id); + coord_t grni = math::detail::greatest(cos_a1ni, cos_a2ni); + + if (gri >= grni) + { + result = projected_to_surface(id, spheroid); + } + else + { + result = projected_to_surface(neg_id, spheroid); + } return true; } @@ -349,21 +369,21 @@ inline bool elliptic_intersection(Point3d const& a1, Point3d const& a2, // n . p + d = 0 // n . p = h - coord_t dot_n1_n2 = dot_product(n1, n2); - coord_t denom = c1 - math::sqr(dot_n1_n2); + // intersection direction + Point3d id = cross_product(n1, n2); - // equivalent to |n1 x n2| = 0 - if (math::equals(denom, c0)) + if (math::equals(dot_product(id, id), c0)) { return false; } - // intersection direction - Point3d id = cross_product(n1, n2); - + coord_t dot_n1_n2 = dot_product(n1, n2); + coord_t dot_n1_n2_sqr = math::sqr(dot_n1_n2); + coord_t h1 = dot_product(n1, aorig); coord_t h2 = dot_product(n2, borig); + coord_t denom = c1 - dot_n1_n2_sqr; coord_t C1 = (h1 - h2 * dot_n1_n2) / denom; coord_t C2 = (h2 - h1 * dot_n1_n2) / denom; @@ -393,12 +413,12 @@ inline bool elliptic_intersection(Point3d const& a1, Point3d const& a2, // NOTE: could be used to calculate comparable distances/angles coord_t cos_a1i = dot_product(a1v, id); coord_t cos_a2i = dot_product(a2v, id); - coord_t gri = bg::math::detail::greatest(cos_a1i, cos_a2i); + coord_t gri = math::detail::greatest(cos_a1i, cos_a2i); Point3d neg_id = id; multiply_value(neg_id, -c1); coord_t cos_a1ni = dot_product(a1v, neg_id); coord_t cos_a2ni = dot_product(a2v, neg_id); - coord_t grni = bg::math::detail::greatest(cos_a1ni, cos_a2ni); + coord_t grni = math::detail::greatest(cos_a1ni, cos_a2ni); result = gri >= grni ? ip1_s : ip2_s; From 73667d44e8c06ab7a40171f7aa26befad7acbfc7 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 16 Aug 2016 03:59:22 +0200 Subject: [PATCH 08/59] [test][formulas] Add tests for intersection of elliptic arcs. --- test/formulas/intersection.cpp | 26 +++++++++++++++++++++ test/formulas/intersection_cases.hpp | 34 +++++++++++++++++++++------- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/test/formulas/intersection.cpp b/test/formulas/intersection.cpp index f0b1b49d7..15c41137a 100644 --- a/test/formulas/intersection.cpp +++ b/test/formulas/intersection.cpp @@ -13,6 +13,7 @@ #include "intersection_cases.hpp" #include +#include #include #include #include @@ -74,6 +75,31 @@ void test_all(expected_results const& results) result.lon *= r2d; result.lat *= r2d; check_inverse(result, results.sjoberg_andoyer, results.sjoberg_karney, 0.0001); + + typedef bg::model::point > point_geo; + typedef bg::model::point point_3d; + point_geo a1(results.p1.lon, results.p1.lat); + point_geo a2(results.p2.lon, results.p2.lat); + point_geo b1(results.q1.lon, results.q1.lat); + point_geo b2(results.q2.lon, results.q2.lat); + point_3d a1v = bg::formula::geo_to_cart3d(a1, spheroid); + point_3d a2v = bg::formula::geo_to_cart3d(a2, spheroid); + point_3d b1v = bg::formula::geo_to_cart3d(b1, spheroid); + point_3d b2v = bg::formula::geo_to_cart3d(b2, spheroid); + point_3d resv(0, 0); + point_geo res(0, 0); + + bg::formula::elliptic_intersection(a1v, a2v, b1v, b2v, resv, spheroid); + res = bg::formula::cart3d_to_geo(resv, spheroid); + result.lon = bg::get<0>(res); + result.lat = bg::get<1>(res); + check_inverse(result, results.elliptic, results.gnomonic_karney, 0.0001); + + bg::formula::great_elliptic_intersection(a1v, a2v, b1v, b2v, resv, spheroid); + res = bg::formula::cart3d_to_geo(resv, spheroid); + result.lon = bg::get<0>(res); + result.lat = bg::get<1>(res); + check_inverse(result, results.great_elliptic, results.gnomonic_karney, 0.0001); } int test_main(int, char*[]) diff --git a/test/formulas/intersection_cases.hpp b/test/formulas/intersection_cases.hpp index d3d148db3..277e2b698 100644 --- a/test/formulas/intersection_cases.hpp +++ b/test/formulas/intersection_cases.hpp @@ -37,6 +37,8 @@ struct expected_results expected_result sjoberg_vincenty; expected_result sjoberg_thomas; expected_result sjoberg_andoyer; + expected_result elliptic; + expected_result great_elliptic; }; expected_results expected[] = @@ -50,7 +52,9 @@ expected_results expected[] = { 0.0000000000633173, 0.0000000000000003 }, { 0.0000000000626537, -0.0000000000000000 }, { -0.0000000187861002, -0.0000000000000000 }, - { -0.0000055778585615, -0.0000000000000000 } + { -0.0000055778585615, -0.0000000000000000 }, + { 0.0000000000000000, 0.0000000000000000 }, + { -0.0000000000000000, -0.0000000000000000 } },{ { 1, 1 },{ -1, -1 }, { -1, 1 },{ 1, -1 }, @@ -60,7 +64,9 @@ expected_results expected[] = { 0.0000000000000000, 0.0000000000633274 }, { 0.0000000000000000, 0.0000000000626632 }, { -0.0000000000000006, -0.0000000187889745 }, - { -0.0000000000000001, -0.0000055787431353 } + { -0.0000000000000001, -0.0000055787431353 }, + { 0.0000000000000000, 0.0000000000000000 }, + { -0.0000000000000000, 0.0000000000000000 } },{ { -1, -1 },{ 1, 1 }, { 1, -1 },{ -1, 1 }, @@ -70,7 +76,9 @@ expected_results expected[] = { -0.0000000000000000, -0.0000000000633268 }, { -0.0000000000000000, -0.0000000000626632 }, { -0.0000000000000004, 0.0000000187889746 }, - { 0.0000000000000001, 0.0000055787431353 } + { 0.0000000000000001, 0.0000055787431353 }, + { 0.0000000000000000, 0.0000000000000000 }, + { 0.0000000000000000, 0.0000000000000000 } },{ { 1, 1 },{ -1, -1 }, { 1, -1 },{ -1, 1 }, @@ -80,7 +88,9 @@ expected_results expected[] = { -0.0000000000633173, 0.0000000000000003 }, { -0.0000000000626537, -0.0000000000000000 }, { 0.0000000187860994, 0.0000000000000001 }, - { 0.0000055778585615, -0.0000000000000000 } + { 0.0000055778585615, -0.0000000000000000 }, + { 0.0000000000000000, 0.0000000000000000 }, + { -0.0000000000000000, 0.0000000000000000 } },{ { 0, 0 },{ 1, 1 }, { 0, 1 },{ 1, 0 }, @@ -90,7 +100,9 @@ expected_results expected[] = { 0.5000000000316606, 0.5000573755188390 }, { 0.5000000000313266, 0.5000573755188389 }, { 0.4999999906069524, 0.5000573755152582 }, - { 0.4999972102164753, 0.5000573755151276 } + { 0.4999972102164753, 0.5000573755151276 }, + { 0.5000000000000001, 0.5000573754962017 }, + { 0.4999999999999999, 0.5000571197534014 } },{ { 1, 1 },{ 0, 0 }, { 0, 1 },{ 1, 0 }, @@ -100,7 +112,9 @@ expected_results expected[] = { 0.5000000000000000, 0.5000573755505008 }, { 0.5000000000000000, 0.5000573755501669 }, { 0.4999999999999996, 0.5000573661218464 }, - { 0.4999999999999999, 0.5000545856093679 } + { 0.4999999999999999, 0.5000545856093679 }, + { 0.5000000000000001, 0.5000573754962017 }, + { 0.4999999999999999, 0.5000571197534014 } },{ { 0, 0 },{ 1, 1 }, { 1, 0 },{ 0, 1 }, @@ -110,7 +124,9 @@ expected_results expected[] = { 0.5000000000000001, 0.5000573754871773 }, { 0.4999999999999999, 0.5000573754875109 }, { 0.4999999999999999, 0.5000573849086647 }, - { 0.5000000000000000, 0.5000601654208935 } + { 0.5000000000000000, 0.5000601654208935 }, + { 0.5000000000000001, 0.5000573754962017 }, + { 0.4999999999999999, 0.5000571197534014 } },{ { 1, 1 },{ 0, 0 }, { 1, 0 },{ 0, 1 }, @@ -120,7 +136,9 @@ expected_results expected[] = { 0.4999999999683394, 0.5000573755188390 }, { 0.4999999999686731, 0.5000573755188389 }, { 0.5000000093930521, 0.5000573755152582 }, - { 0.5000027897835244, 0.5000573755151276 } + { 0.5000027897835244, 0.5000573755151276 }, + { 0.5000000000000001, 0.5000573754962017 }, + { 0.4999999999999999, 0.5000571197534014 } } }; From 883fb13511afa232e5513eefbdd11995d64c51b5 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 24 Aug 2016 16:51:39 +0200 Subject: [PATCH 09/59] [formulas] Divide geographic (elliptic arcs) formulas into smaller, more reusable ones. --- .../boost/geometry/arithmetic/normalize.hpp | 71 +++++++ .../boost/geometry/formulas/geographic.hpp | 177 ++++++++++-------- 2 files changed, 172 insertions(+), 76 deletions(-) create mode 100644 include/boost/geometry/arithmetic/normalize.hpp diff --git a/include/boost/geometry/arithmetic/normalize.hpp b/include/boost/geometry/arithmetic/normalize.hpp new file mode 100644 index 000000000..7dfdbd2b0 --- /dev/null +++ b/include/boost/geometry/arithmetic/normalize.hpp @@ -0,0 +1,71 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2016, Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ARITHMETIC_NORMALIZE_HPP +#define BOOST_GEOMETRY_ARITHMETIC_NORMALIZE_HPP + + +#include + +#include +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +template +inline typename coordinate_type::type vec_length_sqr(Point const& pt) +{ + return dot_product(pt, pt); +} + +template +inline typename coordinate_type::type vec_length(Point const& pt) +{ + // NOTE: hypot() could be used instead of sqrt() + return math::sqrt(dot_product(pt, pt)); +} + +template +inline bool vec_normalize(Point & pt, typename coordinate_type::type & len) +{ + typedef typename coordinate_type::type coord_t; + + coord_t const c0 = 0; + len = vec_length(pt); + + if (math::equals(len, c0)) + { + return false; + } + + divide_value(pt, len); + return true; +} + +template +inline bool vec_normalize(Point & pt) +{ + typedef typename coordinate_type::type coord_t; + coord_t len; + return vec_normalize(pt, len); +} + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ARITHMETIC_NORMALIZE_HPP diff --git a/include/boost/geometry/formulas/geographic.hpp b/include/boost/geometry/formulas/geographic.hpp index 136f3e4d7..e98ac3b58 100644 --- a/include/boost/geometry/formulas/geographic.hpp +++ b/include/boost/geometry/formulas/geographic.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -303,66 +304,36 @@ inline bool great_elliptic_intersection(Point3d const& a1, Point3d const& a2, return true; } +template +static inline int elliptic_side_value(Point3d1 const& origin, Point3d1 const& norm, Point3d2 const& pt) +{ + typedef typename coordinate_type::type calc_t; + calc_t c0 = 0; + + // vector oposite to pt - origin + // only for the purpose of assigning origin + Point3d1 vec = origin; + subtract_point(vec, pt); + + calc_t d = dot_product(norm, vec); + + // since the vector is opposite the signs are opposite + return math::equals(d, c0) ? 0 + : d < c0 ? 1 + : -1; // d > 0 +} + template -inline bool elliptic_intersection(Point3d const& a1, Point3d const& a2, - Point3d const& b1, Point3d const& b2, - Point3d & result, - Spheroid const& spheroid) +inline bool planes_spheroid_intersection(Point3d const& o1, Point3d const& n1, + Point3d const& o2, Point3d const& n2, + Point3d & ip1, Point3d & ip2, + Spheroid const& spheroid) { typedef typename coordinate_type::type coord_t; coord_t c0 = 0; coord_t c1 = 1; - Point3d axy1 = projected_to_xy(a1, spheroid); - Point3d axy2 = projected_to_xy(a2, spheroid); - Point3d bxy1 = projected_to_xy(b1, spheroid); - Point3d bxy2 = projected_to_xy(b2, spheroid); - - // axy12 = axy2 - axy1; - Point3d axy12 = axy2; - subtract_point(axy12, axy1); - // bxy12 = bxy2 - bxy1; - Point3d bxy12 = bxy2; - subtract_point(bxy12, bxy1); - - // aorig = axy1 + axy12 * 0.5; - Point3d aorig = axy12; - multiply_value(aorig, coord_t(0.5)); - add_point(aorig, axy1); - // borig = bxy1 + bxy12 * 0.5; - Point3d borig = bxy12; - multiply_value(borig, coord_t(0.5)); - add_point(borig, bxy1); - - // av1 = a1 - aorig - Point3d a1v = a1; - subtract_point(a1v, aorig); - // av2 = a2 - aorig - Point3d a2v = a2; - subtract_point(a2v, aorig); - // bv1 = b1 - borig - Point3d b1v = b1; - subtract_point(b1v, borig); - // bv2 = b2 - borig - Point3d b2v = b2; - subtract_point(b2v, borig); - - Point3d n1 = cross_product(a1v, a2v); - Point3d n2 = cross_product(b1v, b2v); - - coord_t n1_len = math::sqrt(dot_product(n1, n1)); - coord_t n2_len = math::sqrt(dot_product(n2, n2)); - - if (math::equals(n1_len, c0) || math::equals(n2_len, c0)) - { - return false; - } - - // normalize - divide_value(n1, n1_len); - divide_value(n2, n2_len); - // Below // n . (p - o) = 0 // n . p - n . o = 0 @@ -380,13 +351,13 @@ inline bool elliptic_intersection(Point3d const& a1, Point3d const& a2, coord_t dot_n1_n2 = dot_product(n1, n2); coord_t dot_n1_n2_sqr = math::sqr(dot_n1_n2); - coord_t h1 = dot_product(n1, aorig); - coord_t h2 = dot_product(n2, borig); + coord_t h1 = dot_product(n1, o1); + coord_t h2 = dot_product(n2, o2); coord_t denom = c1 - dot_n1_n2_sqr; coord_t C1 = (h1 - h2 * dot_n1_n2) / denom; coord_t C2 = (h2 - h1 * dot_n1_n2) / denom; - + // C1 * n1 + C2 * n2 Point3d C1_n1 = n1; multiply_value(C1_n1, C1); @@ -395,32 +366,86 @@ inline bool elliptic_intersection(Point3d const& a1, Point3d const& a2, Point3d io = C1_n1; add_point(io, C2_n2); - Point3d ip1_s, ip2_s; - if (! projected_to_surface(io, id, ip1_s, ip2_s, spheroid)) + if (! projected_to_surface(io, id, ip1, ip2, spheroid)) { return false; } - coord_t a1v_len = math::sqrt(dot_product(a1v, a1v)); - divide_value(a1v, a1v_len); - coord_t a2v_len = math::sqrt(dot_product(a2v, a2v)); - divide_value(a2v, a2v_len); - coord_t b1v_len = math::sqrt(dot_product(b1v, b1v)); - divide_value(b1v, a1v_len); - coord_t b2v_len = math::sqrt(dot_product(b2v, b2v)); - divide_value(b2v, a2v_len); + return true; +} - // NOTE: could be used to calculate comparable distances/angles - coord_t cos_a1i = dot_product(a1v, id); - coord_t cos_a2i = dot_product(a2v, id); - coord_t gri = math::detail::greatest(cos_a1i, cos_a2i); - Point3d neg_id = id; - multiply_value(neg_id, -c1); - coord_t cos_a1ni = dot_product(a1v, neg_id); - coord_t cos_a2ni = dot_product(a2v, neg_id); - coord_t grni = math::detail::greatest(cos_a1ni, cos_a2ni); - result = gri >= grni ? ip1_s : ip2_s; +template +inline void experimental_elliptic_plane(Point3d const& p1, Point3d const& p2, + Point3d & v1, Point3d & v2, + Point3d & origin, Point3d & normal, + Spheroid const& spheroid) +{ + typedef typename coordinate_type::type coord_t; + + Point3d xy1 = projected_to_xy(p1, spheroid); + Point3d xy2 = projected_to_xy(p2, spheroid); + + // origin = (xy1 + xy2) / 2 + origin = xy1; + add_point(origin, xy2); + multiply_value(origin, coord_t(0.5)); + + // v1 = p1 - origin + v1 = p1; + subtract_point(v1, origin); + // v2 = p2 - origin + v2 = p2; + subtract_point(v2, origin); + + normal = cross_product(v1, v2); +} + +template +inline void experimental_elliptic_plane(Point3d const& p1, Point3d const& p2, + Point3d & origin, Point3d & normal, + Spheroid const& spheroid) +{ + Point3d v1, v2; + experimental_elliptic_plane(p1, p2, v1, v2, origin, normal, spheroid); +} + +template +inline bool experimental_elliptic_intersection(Point3d const& a1, Point3d const& a2, + Point3d const& b1, Point3d const& b2, + Point3d & result, + Spheroid const& spheroid) +{ + typedef typename coordinate_type::type coord_t; + + coord_t c0 = 0; + coord_t c1 = 1; + + Point3d a1v, a2v, o1, n1; + experimental_elliptic_plane(a1, a2, a1v, a2v, o1, n1, spheroid); + Point3d b1v, b2v, o2, n2; + experimental_elliptic_plane(b1, b2, b1v, b2v, o2, n2, spheroid); + + if (! detail::vec_normalize(n1) || ! detail::vec_normalize(n2)) + { + return false; + } + + Point3d ip1_s, ip2_s; + if (! planes_spheroid_intersection(o1, n1, o2, n2, ip1_s, ip2_s, spheroid)) + { + return false; + } + + // NOTE: simplified test, may not work in all cases + coord_t dot_a1i1 = dot_product(a1, ip1_s); + coord_t dot_a2i1 = dot_product(a2, ip1_s); + coord_t gri1 = math::detail::greatest(dot_a1i1, dot_a2i1); + coord_t dot_a1i2 = dot_product(a1, ip2_s); + coord_t dot_a2i2 = dot_product(a2, ip2_s); + coord_t gri2 = math::detail::greatest(dot_a1i2, dot_a2i2); + + result = gri1 >= gri2 ? ip1_s : ip2_s; return true; } From a97d85a31e7d515627192c18cf7082c60e82f0c9 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 24 Aug 2016 17:03:54 +0200 Subject: [PATCH 10/59] [strategies] Make agnostic ECEF intersection strategy from spherical intersection strategy. Implement CalcPolicies used to define the behavior of this strategy: - great circles intersection - great elliptic arcs intersection - experimental elliptic arcs intersection --- .../strategies/spherical/intersection.hpp | 523 ++++++++++++++---- 1 file changed, 424 insertions(+), 99 deletions(-) diff --git a/include/boost/geometry/strategies/spherical/intersection.hpp b/include/boost/geometry/strategies/spherical/intersection.hpp index 2e113384b..6ab085ab8 100644 --- a/include/boost/geometry/strategies/spherical/intersection.hpp +++ b/include/boost/geometry/strategies/spherical/intersection.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -25,7 +26,9 @@ #include #include #include +#include #include +#include #include #include @@ -68,13 +71,14 @@ namespace strategy { namespace intersection // For now, intersection points near the endpoints are checked explicitly if needed (if the IP is near the endpoint) // to generate precise result for them. Only the crossing (i) case may suffer from lower precision. -template -struct relate_spherical_segments +template +struct relate_ecef_segments { typedef typename Policy::return_type return_type; enum intersection_point_flag { ipi_inters = 0, ipi_at_a1, ipi_at_a2, ipi_at_b1, ipi_at_b2 }; + // segment_intersection_info cannot outlive relate_ecef_segments template struct segment_intersection_info { @@ -83,6 +87,10 @@ struct relate_spherical_segments CoordinateType, double >::type promoted_type; + segment_intersection_info(CalcPolicy const& calc) + : calc_policy(calc) + {} + promoted_type comparable_length_a() const { return robust_ra.denominator(); @@ -110,7 +118,7 @@ struct relate_spherical_segments if (ip_flag == ipi_inters) { // TODO: assign the rest of coordinates - point = formula::cart3d_to_sph(intersection_point); + point = calc_policy.template from_cart3d(intersection_point); } else if (ip_flag == ipi_at_a1) { @@ -134,6 +142,8 @@ struct relate_spherical_segments SegmentRatio robust_ra; SegmentRatio robust_rb; intersection_point_flag ip_flag; + + CalcPolicy const& calc_policy; }; // Relate segments a and b @@ -161,6 +171,11 @@ struct relate_spherical_segments RobustPolicy const&, Point1 const& a1, Point1 const& a2, Point2 const& b1, Point2 const& b2) { + // For now create it using default constructor. In the future it could + // be stored in strategy. However then apply() wouldn't be static and + // all relops and setops would have to take the strategy or model. + CalcPolicy const calc_policy; + BOOST_CONCEPT_ASSERT( (concept::ConstSegment) ); BOOST_CONCEPT_ASSERT( (concept::ConstSegment) ); @@ -185,25 +200,29 @@ struct relate_spherical_segments typedef model::point vec3d_t; - using namespace formula; - vec3d_t const a1v = sph_to_cart3d(a1); - vec3d_t const a2v = sph_to_cart3d(a2); - vec3d_t const b1v = sph_to_cart3d(b1); - vec3d_t const b2v = sph_to_cart3d(b2); + vec3d_t const a1v = calc_policy.template to_cart3d(a1); + vec3d_t const a2v = calc_policy.template to_cart3d(a2); + vec3d_t const b1v = calc_policy.template to_cart3d(b1); + vec3d_t const b2v = calc_policy.template to_cart3d(b2); - vec3d_t norm1 = cross_product(a1v, a2v); - vec3d_t norm2 = cross_product(b1v, b2v); - side_info sides; - // not normalized normals, the same as in SSF - sides.set<0>(sph_side_value(norm2, a1v), sph_side_value(norm2, a2v)); + + typename CalcPolicy::template plane + plane2 = calc_policy.get_plane(b1v, b2v); + + // not normalized normals, the same as in side strategy + sides.set<0>(plane2.side_value(a1v), plane2.side_value(a2v)); if (sides.same<0>()) { // Both points are at same side of other segment, we can leave return Policy::disjoint(); } - // not normalized normals, the same as in SSF - sides.set<1>(sph_side_value(norm1, b1v), sph_side_value(norm1, b2v)); + + typename CalcPolicy::template plane + plane1 = calc_policy.get_plane(a1v, a2v); + + // not normalized normals, the same as in side strategy + sides.set<1>(plane1.side_value(b1v), plane1.side_value(b2v)); if (sides.same<1>()) { // Both points are at same side of other segment, we can leave @@ -212,13 +231,10 @@ struct relate_spherical_segments // NOTE: at this point the segments may still be disjoint - bool collinear = sides.collinear(); + calc_t len1, len2; - calc_t const len1 = math::sqrt(dot_product(norm1, norm1)); - calc_t const len2 = math::sqrt(dot_product(norm2, norm2)); - - // point or opposite sides of a sphere, assume point - if (math::equals(len1, c0)) + // point or opposite sides of a sphere/spheroid, assume point + if (! detail::vec_normalize(plane1.normal, len1)) { a_is_point = true; if (sides.get<0, 0>() == 0 || sides.get<0, 1>() == 0) @@ -226,13 +242,8 @@ struct relate_spherical_segments sides.set<0>(0, 0); } } - else - { - // normalize - divide_value(norm1, len1); - } - if (math::equals(len2, c0)) + if (! detail::vec_normalize(plane2.normal, len2)) { b_is_point = true; if (sides.get<1, 0>() == 0 || sides.get<1, 1>() == 0) @@ -240,11 +251,6 @@ struct relate_spherical_segments sides.set<1>(0, 0); } } - else - { - // normalize - divide_value(norm2, len2); - } // check both degenerated once more if (a_is_point && b_is_point) @@ -255,16 +261,42 @@ struct relate_spherical_segments ; } + // NOTE: at this point the segments may still be disjoint // NOTE: at this point one of the segments may be degenerated - // and the segments may still be disjoint - calc_t dot_n1n2 = dot_product(norm1, norm2); + bool collinear = sides.collinear(); + + if (! collinear) + { + // NOTE: for some approximations it's possible that both points may lie + // on the same geodesic but still some of the sides may be != 0. + // This is e.g. true for long segments represented as elliptic arcs + // with origin different than the center of the coordinate system. + // So make the sides consistent + + // WARNING: the side strategy doesn't have the info about the other + // segment so it may return results inconsistent with this intersection + // strategy, as it checks both segments for consistency + + if (sides.get<0, 0>() == 0 && sides.get<0, 1>() == 0) + { + collinear = true; + sides.set<1>(0, 0); + } + else if (sides.get<1, 0>() == 0 && sides.get<1, 1>() == 0) + { + collinear = true; + sides.set<0>(0, 0); + } + } + + calc_t dot_n1n2 = dot_product(plane1.normal, plane2.normal); // NOTE: this is technically not needed since theoretically above sides // are calculated, but just in case check the normals. // Have in mind that SSF side strategy doesn't check this. // collinear if normals are equal or opposite: cos(a) in {-1, 1} - if (!collinear && math::equals(math::abs(dot_n1n2), c1)) + if (! collinear && math::equals(math::abs(dot_n1n2), c1)) { collinear = true; sides.set<0>(0, 0); @@ -275,12 +307,12 @@ struct relate_spherical_segments { if (a_is_point) { - return collinear_one_degenerted(a, true, b1, b2, a1, a2, b1v, b2v, norm2, a1v); + return collinear_one_degenerted(a, true, b1, b2, a1, a2, b1v, b2v, plane2, a1v); } else if (b_is_point) { // b2 used to be consistent with (degenerated) checks above (is it needed?) - return collinear_one_degenerted(b, false, a1, a2, b1, b2, a1v, a2v, norm1, b1v); + return collinear_one_degenerted(b, false, a1, a2, b1, b2, a1v, a2v, plane1, b1v); } else { @@ -289,16 +321,16 @@ struct relate_spherical_segments // use shorter segment if (len1 <= len2) { - calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, norm1, b1v, dist_a1_a2, dist_a1_b1); - calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, norm1, b2v, dist_a1_a2, dist_a1_b2); + calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, plane1, b1v, dist_a1_a2, dist_a1_b1); + calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, plane1, b2v, dist_a1_a2, dist_a1_b2); dist_b1_b2 = dist_a1_b2 - dist_a1_b1; dist_b1_a1 = -dist_a1_b1; dist_b1_a2 = dist_a1_a2 - dist_a1_b1; } else { - calculate_collinear_data(b1, b2, a1, a2, b1v, b2v, norm2, a1v, dist_b1_b2, dist_b1_a1); - calculate_collinear_data(b1, b2, a1, a2, b1v, b2v, norm2, a2v, dist_b1_b2, dist_b1_a2); + calculate_collinear_data(b1, b2, a1, a2, b1v, b2v, plane2, a1v, dist_b1_b2, dist_b1_a1); + calculate_collinear_data(b1, b2, a1, a2, b1v, b2v, plane2, a2v, dist_b1_b2, dist_b1_a2); dist_a1_a2 = dist_b1_a2 - dist_b1_a1; dist_a1_b1 = -dist_b1_a1; dist_a1_b2 = dist_b1_b2 - dist_b1_a1; @@ -359,7 +391,8 @@ struct relate_spherical_segments vec3d_t i1; intersection_point_flag ip_flag; calc_t dist_a1_a2, dist_a1_i1, dist_b1_b2, dist_b1_i1; - if (calculate_ip_data(a1, a2, b1, b2, a1v, a2v, b1v, b2v, norm1, norm2, sides, + if (calculate_ip_data(a1, a2, b1, b2, a1v, a2v, b1v, b2v, + plane1, plane2, calc_policy, sides, i1, dist_a1_a2, dist_a1_i1, dist_b1_b2, dist_b1_i1, ip_flag)) { // intersects @@ -368,7 +401,7 @@ struct relate_spherical_segments calc_t, segment_ratio, vec3d_t - > sinfo; + > sinfo(calc_policy); sinfo.robust_ra.assign(dist_a1_i1, dist_a1_a2); sinfo.robust_rb.assign(dist_b1_i1, dist_b1_b2); @@ -385,30 +418,31 @@ struct relate_spherical_segments } private: - template + template static inline return_type collinear_one_degenerted(Segment const& segment, bool degenerated_a, Point1 const& a1, Point1 const& a2, Point2 const& b1, Point2 const& b2, - Vec3d const& v1, Vec3d const& v2, Vec3d const& norm, + Vec3d const& v1, Vec3d const& v2, + Plane const& plane, Vec3d const& vother) { CalcT dist_1_2, dist_1_o; - return ! calculate_collinear_data(a1, a2, b1, b2, v1, v2, norm, vother, dist_1_2, dist_1_o) + return ! calculate_collinear_data(a1, a2, b1, b2, v1, v2, plane, vother, dist_1_2, dist_1_o) ? Policy::disjoint() : Policy::one_degenerate(segment, segment_ratio(dist_1_o, dist_1_2), degenerated_a); } - template - static inline bool calculate_collinear_data(Point1 const& a1, Point1 const& a2, - Point2 const& b1, Point2 const& b2, - Vec3d const& a1v, // in - Vec3d const& a2v, // in - Vec3d const& norm1, // in - Vec3d const& b1v_or_b2v, // in + template + static inline bool calculate_collinear_data(Point1 const& a1, Point1 const& a2, // in + Point2 const& b1, Point2 const& b2, // in + Vec3d const& a1v, // in + Vec3d const& a2v, // in + Plane const& plane1, // in + Vec3d const& b1v_or_b2v, // in CalcT& dist_a1_a2, CalcT& dist_a1_i1) // out { // calculate dist_a1_a2 and dist_a1_i1 - calculate_dists(a1v, a2v, norm1, b1v_or_b2v, dist_a1_a2, dist_a1_i1); + calculate_dists(a1v, a2v, plane1, b1v_or_b2v, dist_a1_a2, dist_a1_i1); // if i1 is close to a1 and b1 or b2 is equal to a1 if (is_endpoint_equal(dist_a1_i1, a1, b1, b2)) @@ -427,54 +461,53 @@ private: return segment_ratio(dist_a1_i1, dist_a1_a2).on_segment(); } - template + template static inline bool calculate_ip_data(Point1 const& a1, Point1 const& a2, // in Point2 const& b1, Point2 const& b2, // in Vec3d const& a1v, Vec3d const& a2v, // in Vec3d const& b1v, Vec3d const& b2v, // in - Vec3d const& norm1, Vec3d const& norm2, // in - side_info const& sides, // in - Vec3d & i1, // in-out - CalcT& dist_a1_a2, CalcT& dist_a1_i1, // out - CalcT& dist_b1_b2, CalcT& dist_b1_i1, // out + Plane const& plane1, // in + Plane const& plane2, // in + CalcPolicy const& calc_policy, // in + side_info const& sides, // in + Vec3d & ip, // out + CalcT& dist_a1_a2, CalcT& dist_a1_ip, // out + CalcT& dist_b1_b2, CalcT& dist_b1_ip, // out intersection_point_flag& ip_flag) // out { - // great circles intersections - i1 = cross_product(norm1, norm2); - // NOTE: the length should be greater than 0 at this point - // if the normals were not normalized and their dot product - // not checked before this function is called the length - // should be checked here (math::equals(len, c0)) - CalcT const len = math::sqrt(dot_product(i1, i1)); - divide_value(i1, len); // normalize i1 - - calculate_dists(a1v, a2v, norm1, i1, dist_a1_a2, dist_a1_i1); + Vec3d ip1, ip2; + calc_policy.intersection_points(plane1, plane2, ip1, ip2); + calculate_dists(a1v, a2v, plane1, ip1, dist_a1_a2, dist_a1_ip); + ip = ip1; + // choose the opposite side of the globe if the distance is shorter { - CalcT const d = abs_distance(dist_a1_a2, dist_a1_i1); + CalcT const d = abs_distance(dist_a1_a2, dist_a1_ip); if (d > CalcT(0)) { - CalcT const dist_a1_i2 = dist_of_i2(dist_a1_i1); + // TODO: this should be ok not only for sphere + // but requires more investigation + CalcT const dist_a1_i2 = dist_of_i2(dist_a1_ip); CalcT const d2 = abs_distance(dist_a1_a2, dist_a1_i2); if (d2 < d) { - dist_a1_i1 = dist_a1_i2; - multiply_value(i1, CalcT(-1)); // the opposite intersection + dist_a1_ip = dist_a1_i2; + ip = ip2; } } } bool is_on_a = false, is_near_a1 = false, is_near_a2 = false; - if (! is_potentially_crossing(dist_a1_a2, dist_a1_i1, is_on_a, is_near_a1, is_near_a2)) + if (! is_potentially_crossing(dist_a1_a2, dist_a1_ip, is_on_a, is_near_a1, is_near_a2)) { return false; } - calculate_dists(b1v, b2v, norm2, i1, dist_b1_b2, dist_b1_i1); + calculate_dists(b1v, b2v, plane2, ip, dist_b1_b2, dist_b1_ip); bool is_on_b = false, is_near_b1 = false, is_near_b2 = false; - if (! is_potentially_crossing(dist_b1_b2, dist_b1_i1, is_on_b, is_near_b1, is_near_b2)) + if (! is_potentially_crossing(dist_b1_b2, dist_b1_ip, is_on_b, is_near_b1, is_near_b2)) { return false; } @@ -485,8 +518,8 @@ private: { if (is_near_b1 && equals_point_point(a1, b1)) { - dist_a1_i1 = 0; - dist_b1_i1 = 0; + dist_a1_ip = 0; + dist_b1_ip = 0; //i1 = a1v; ip_flag = ipi_at_a1; return true; @@ -494,8 +527,8 @@ private: if (is_near_b2 && equals_point_point(a1, b2)) { - dist_a1_i1 = 0; - dist_b1_i1 = dist_b1_b2; + dist_a1_ip = 0; + dist_b1_ip = dist_b1_b2; //i1 = a1v; ip_flag = ipi_at_a1; return true; @@ -506,8 +539,8 @@ private: { if (is_near_b1 && equals_point_point(a2, b1)) { - dist_a1_i1 = dist_a1_a2; - dist_b1_i1 = 0; + dist_a1_ip = dist_a1_a2; + dist_b1_ip = 0; //i1 = a2v; ip_flag = ipi_at_a2; return true; @@ -515,8 +548,8 @@ private: if (is_near_b2 && equals_point_point(a2, b2)) { - dist_a1_i1 = dist_a1_a2; - dist_b1_i1 = dist_b1_b2; + dist_a1_ip = dist_a1_a2; + dist_b1_ip = dist_b1_b2; //i1 = a2v; ip_flag = ipi_at_a2; return true; @@ -530,7 +563,7 @@ private: { if (is_near_b1 && sides.template get<1, 0>() == 0) // b1 wrt a { - dist_b1_i1 = 0; + dist_b1_ip = 0; //i1 = b1v; ip_flag = ipi_at_b1; return true; @@ -538,7 +571,7 @@ private: if (is_near_b2 && sides.template get<1, 1>() == 0) // b2 wrt a { - dist_b1_i1 = dist_b1_b2; + dist_b1_ip = dist_b1_b2; //i1 = b2v; ip_flag = ipi_at_b2; return true; @@ -549,7 +582,7 @@ private: { if (is_near_a1 && sides.template get<0, 0>() == 0) // a1 wrt b { - dist_a1_i1 = 0; + dist_a1_ip = 0; //i1 = a1v; ip_flag = ipi_at_a1; return true; @@ -557,7 +590,7 @@ private: if (is_near_a2 && sides.template get<0, 1>() == 0) // a2 wrt b { - dist_a1_i1 = dist_a1_a2; + dist_a1_ip = dist_a1_a2; //i1 = a2v; ip_flag = ipi_at_a2; return true; @@ -569,24 +602,26 @@ private: return is_on_a && is_on_b; } - template - static inline void calculate_dists(Vec3d const& a1v, // in - Vec3d const& a2v, // in - Vec3d const& norm1, // in - Vec3d const& i1, // in - CalcT& dist_a1_a2, CalcT& dist_a1_i1) // out + template + static inline void calculate_dists(Vec3d const& a1v, // in + Vec3d const& a2v, // in + Plane const& plane1, // in + Vec3d const& i1, // in + CalcT& dist_a1_a2, // out + CalcT& dist_a1_i1) // out { - CalcT const c0 = 0; + //CalcT const c0 = 0; CalcT const c1 = 1; CalcT const c2 = 2; CalcT const c4 = 4; - CalcT cos_a1_a2 = dot_product(a1v, a2v); + CalcT cos_a1_a2 = plane1.cos_angle_between(a1v, a2v); dist_a1_a2 = -cos_a1_a2 + c1; // [1, -1] -> [0, 2] representing [0, pi] - CalcT cos_a1_i1 = dot_product(a1v, i1); + bool is_forward = true; + CalcT cos_a1_i1 = plane1.cos_angle_between(a1v, i1, is_forward); dist_a1_i1 = -cos_a1_i1 + c1; // [0, 2] representing [0, pi] - if (dot_product(norm1, cross_product(a1v, i1)) < c0) // left or right of a1 on a + if (! is_forward) // left or right of a1 on a { dist_a1_i1 = -dist_a1_i1; // [0, 2] -> [0, -2] representing [0, -pi] } @@ -666,6 +701,291 @@ private: } }; +struct relate_spherical_segments_calc_policy +{ + template + static Point from_cart3d(Point3d const& point_3d) + { + return formula::cart3d_to_sph(point_3d); + } + + template + static Point3d to_cart3d(Point const& point) + { + return formula::sph_to_cart3d(point); + } + + template + struct plane + { + typedef typename coordinate_type::type coord_t; + + // not normalized + plane(Point3d const& p1, Point3d const& p2) + : normal(cross_product(p1, p2)) + {} + + int side_value(Point3d const& pt) const + { + return formula::sph_side_value(normal, pt); + } + + static coord_t cos_angle_between(Point3d const& p1, Point3d const& p2) + { + return dot_product(p1, p2); + } + + coord_t cos_angle_between(Point3d const& p1, Point3d const& p2, bool & is_forward) const + { + coord_t const c0 = 0; + is_forward = dot_product(normal, cross_product(p1, p2)) >= c0; + return dot_product(p1, p2); + } + + Point3d normal; + }; + + template + static plane get_plane(Point3d const& p1, Point3d const& p2) + { + return plane(p1, p2); + } + + template + static bool intersection_points(plane const& plane1, + plane const& plane2, + Point3d & ip1, Point3d & ip2) + { + typedef typename coordinate_type::type coord_t; + + ip1 = cross_product(plane1.normal, plane2.normal); + // NOTE: the length should be greater than 0 at this point + // if the normals were not normalized and their dot product + // not checked before this function is called the length + // should be checked here (math::equals(len, c0)) + coord_t const len = math::sqrt(dot_product(ip1, ip1)); + divide_value(ip1, len); // normalize i1 + + ip2 = ip1; + multiply_value(ip2, coord_t(-1)); + + return true; + } +}; + +template +struct relate_great_elliptic_segments_calc_policy + : relate_spherical_segments_calc_policy +{ + explicit relate_great_elliptic_segments_calc_policy(Spheroid const& spheroid = Spheroid()) + : m_spheroid(spheroid) + {} + + template + Point from_cart3d(Point3d const& point_3d) const + { + return formula::cart3d_to_geo(point_3d, m_spheroid); + } + + template + Point3d to_cart3d(Point const& point) const + { + return formula::geo_to_cart3d(point, m_spheroid); + } + + // relate_xxx_calc_policy must live londer than plane because it contains + // Spheroid object and plane keeps the reference to that object. + template + struct plane + { + typedef typename coordinate_type::type coord_t; + + // not normalized + plane(Point3d const& p1, Point3d const& p2) + : normal(cross_product(p1, p2)) + {} + + int side_value(Point3d const& pt) const + { + return formula::sph_side_value(normal, pt); + } + + coord_t cos_angle_between(Point3d const& p1, Point3d const& p2) const + { + Point3d v1 = p1; + detail::vec_normalize(v1); + Point3d v2 = p2; + detail::vec_normalize(v2); + + return dot_product(v1, v2); + } + + coord_t cos_angle_between(Point3d const& p1, Point3d const& p2, bool & is_forward) const + { + coord_t const c0 = 0; + + Point3d v1 = p1; + detail::vec_normalize(v1); + Point3d v2 = p2; + detail::vec_normalize(v2); + + is_forward = dot_product(normal, cross_product(v1, v2)) >= c0; + return dot_product(v1, v2); + } + + Point3d normal; + }; + + template + plane get_plane(Point3d const& p1, Point3d const& p2) const + { + return plane(p1, p2); + } + + template + bool intersection_points(plane const& plane1, + plane const& plane2, + Point3d & ip1, Point3d & ip2) const + { + typedef typename coordinate_type::type coord_t; + + Point3d id = cross_product(plane1.normal, plane2.normal); + // NOTE: the length should be greater than 0 at this point + // NOTE: no need to normalize in this case + + ip1 = formula::projected_to_surface(id, m_spheroid); + + ip2 = ip1; + multiply_value(ip2, coord_t(-1)); + + return true; + } + +private: + Spheroid m_spheroid; +}; + +template +struct relate_experimental_elliptic_segments_calc_policy +{ + explicit relate_experimental_elliptic_segments_calc_policy(Spheroid const& spheroid = Spheroid()) + : m_spheroid(spheroid) + {} + + template + Point from_cart3d(Point3d const& point_3d) const + { + return formula::cart3d_to_geo(point_3d, m_spheroid); + } + + template + Point3d to_cart3d(Point const& point) const + { + return formula::geo_to_cart3d(point, m_spheroid); + } + + // relate_xxx_calc_policy must live londer than plane because it contains + // Spheroid object and plane keeps the reference to that object. + template + struct plane + { + typedef typename coordinate_type::type coord_t; + + // not normalized + plane(Point3d const& p1, Point3d const& p2, Spheroid const& spheroid) + : m_spheroid(spheroid) + { + formula::experimental_elliptic_plane(p1, p2, origin, normal, m_spheroid); + } + + int side_value(Point3d const& pt) const + { + return formula::elliptic_side_value(origin, normal, pt); + } + + coord_t cos_angle_between(Point3d const& p1, Point3d const& p2) const + { + Point3d const v1 = normalized_vec(p1); + Point3d const v2 = normalized_vec(p2); + return dot_product(v1, v2); + } + + coord_t cos_angle_between(Point3d const& p1, Point3d const& p2, bool & is_forward) const + { + coord_t const c0 = 0; + + Point3d const v1 = normalized_vec(p1); + Point3d const v2 = normalized_vec(p2); + + is_forward = dot_product(normal, cross_product(v1, v2)) >= c0; + return dot_product(v1, v2); + } + + Point3d origin; + Point3d normal; + + private: + Point3d normalized_vec(Point3d const& p) const + { + Point3d v = p; + subtract_point(v, origin); + detail::vec_normalize(v); + return v; + } + + Spheroid const& m_spheroid; + }; + + template + plane get_plane(Point3d const& p1, Point3d const& p2) const + { + return plane(p1, p2, m_spheroid); + } + + template + bool intersection_points(plane const& plane1, + plane const& plane2, + Point3d & ip1, Point3d & ip2) const + { + return formula::planes_spheroid_intersection(plane1.origin, plane1.normal, + plane2.origin, plane2.normal, + ip1, ip2, m_spheroid); + } + +private: + Spheroid m_spheroid; +}; + +template +struct relate_spherical_segments + : relate_ecef_segments + < + Policy, + relate_spherical_segments_calc_policy, + CalculationType + > +{}; + +template , typename CalculationType = void> +struct relate_great_elliptic_segments + : relate_ecef_segments + < + Policy, + relate_great_elliptic_segments_calc_policy, + CalculationType + > +{}; + +template , typename CalculationType = void> +struct relate_experimental_elliptic_segments + : relate_ecef_segments + < + Policy, + relate_experimental_elliptic_segments_calc_policy, + CalculationType + > +{}; + #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS namespace services @@ -686,6 +1006,11 @@ struct default_strategy template struct default_strategy { + // NOTE: Spherical strategy returns the same result as the geographic one + // representing segments as great elliptic arcs. If the elliptic arcs are + // not great elliptic arcs (the origin not in the center of the coordinate + // system) then there may be problems with consistency of the side and + // intersection strategies. typedef relate_spherical_segments type; }; From 4d9d2714c3eea8998eb203de9543a0e27637f1d9 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 24 Aug 2016 17:46:58 +0200 Subject: [PATCH 11/59] [strategies] Move geographic policies of ECEF intersection strategy into a separate file. --- .../strategies/geographic/intersection.hpp | 237 ++++++++++++++++++ .../strategies/spherical/intersection.hpp | 204 --------------- .../boost/geometry/strategies/strategies.hpp | 1 + 3 files changed, 238 insertions(+), 204 deletions(-) create mode 100644 include/boost/geometry/strategies/geographic/intersection.hpp diff --git a/include/boost/geometry/strategies/geographic/intersection.hpp b/include/boost/geometry/strategies/geographic/intersection.hpp new file mode 100644 index 000000000..a191eec62 --- /dev/null +++ b/include/boost/geometry/strategies/geographic/intersection.hpp @@ -0,0 +1,237 @@ +// Boost.Geometry + +// Copyright (c) 2016, Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_INTERSECTION_HPP +#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_INTERSECTION_HPP + + +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace intersection +{ + +template +struct relate_great_elliptic_segments_calc_policy + : relate_spherical_segments_calc_policy +{ + explicit relate_great_elliptic_segments_calc_policy(Spheroid const& spheroid = Spheroid()) + : m_spheroid(spheroid) + {} + + template + Point from_cart3d(Point3d const& point_3d) const + { + return formula::cart3d_to_geo(point_3d, m_spheroid); + } + + template + Point3d to_cart3d(Point const& point) const + { + return formula::geo_to_cart3d(point, m_spheroid); + } + + // relate_xxx_calc_policy must live londer than plane because it contains + // Spheroid object and plane keeps the reference to that object. + template + struct plane + { + typedef typename coordinate_type::type coord_t; + + // not normalized + plane(Point3d const& p1, Point3d const& p2) + : normal(cross_product(p1, p2)) + {} + + int side_value(Point3d const& pt) const + { + return formula::sph_side_value(normal, pt); + } + + coord_t cos_angle_between(Point3d const& p1, Point3d const& p2) const + { + Point3d v1 = p1; + detail::vec_normalize(v1); + Point3d v2 = p2; + detail::vec_normalize(v2); + + return dot_product(v1, v2); + } + + coord_t cos_angle_between(Point3d const& p1, Point3d const& p2, bool & is_forward) const + { + coord_t const c0 = 0; + + Point3d v1 = p1; + detail::vec_normalize(v1); + Point3d v2 = p2; + detail::vec_normalize(v2); + + is_forward = dot_product(normal, cross_product(v1, v2)) >= c0; + return dot_product(v1, v2); + } + + Point3d normal; + }; + + template + plane get_plane(Point3d const& p1, Point3d const& p2) const + { + return plane(p1, p2); + } + + template + bool intersection_points(plane const& plane1, + plane const& plane2, + Point3d & ip1, Point3d & ip2) const + { + typedef typename coordinate_type::type coord_t; + + Point3d id = cross_product(plane1.normal, plane2.normal); + // NOTE: the length should be greater than 0 at this point + // NOTE: no need to normalize in this case + + ip1 = formula::projected_to_surface(id, m_spheroid); + + ip2 = ip1; + multiply_value(ip2, coord_t(-1)); + + return true; + } + +private: + Spheroid m_spheroid; +}; + +template +struct relate_experimental_elliptic_segments_calc_policy +{ + explicit relate_experimental_elliptic_segments_calc_policy(Spheroid const& spheroid = Spheroid()) + : m_spheroid(spheroid) + {} + + template + Point from_cart3d(Point3d const& point_3d) const + { + return formula::cart3d_to_geo(point_3d, m_spheroid); + } + + template + Point3d to_cart3d(Point const& point) const + { + return formula::geo_to_cart3d(point, m_spheroid); + } + + // relate_xxx_calc_policy must live londer than plane because it contains + // Spheroid object and plane keeps the reference to that object. + template + struct plane + { + typedef typename coordinate_type::type coord_t; + + // not normalized + plane(Point3d const& p1, Point3d const& p2, Spheroid const& spheroid) + : m_spheroid(spheroid) + { + formula::experimental_elliptic_plane(p1, p2, origin, normal, m_spheroid); + } + + int side_value(Point3d const& pt) const + { + return formula::elliptic_side_value(origin, normal, pt); + } + + coord_t cos_angle_between(Point3d const& p1, Point3d const& p2) const + { + Point3d const v1 = normalized_vec(p1); + Point3d const v2 = normalized_vec(p2); + return dot_product(v1, v2); + } + + coord_t cos_angle_between(Point3d const& p1, Point3d const& p2, bool & is_forward) const + { + coord_t const c0 = 0; + + Point3d const v1 = normalized_vec(p1); + Point3d const v2 = normalized_vec(p2); + + is_forward = dot_product(normal, cross_product(v1, v2)) >= c0; + return dot_product(v1, v2); + } + + Point3d origin; + Point3d normal; + + private: + Point3d normalized_vec(Point3d const& p) const + { + Point3d v = p; + subtract_point(v, origin); + detail::vec_normalize(v); + return v; + } + + Spheroid const& m_spheroid; + }; + + template + plane get_plane(Point3d const& p1, Point3d const& p2) const + { + return plane(p1, p2, m_spheroid); + } + + template + bool intersection_points(plane const& plane1, + plane const& plane2, + Point3d & ip1, Point3d & ip2) const + { + return formula::planes_spheroid_intersection(plane1.origin, plane1.normal, + plane2.origin, plane2.normal, + ip1, ip2, m_spheroid); + } + +private: + Spheroid m_spheroid; +}; + + +template , typename CalculationType = void> +struct relate_great_elliptic_segments + : relate_ecef_segments + < + Policy, + relate_great_elliptic_segments_calc_policy, + CalculationType + > +{}; + +template , typename CalculationType = void> +struct relate_experimental_elliptic_segments + : relate_ecef_segments + < + Policy, + relate_experimental_elliptic_segments_calc_policy, + CalculationType + > +{}; + + +}} // namespace strategy::intersection + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_INTERSECTION_HPP diff --git a/include/boost/geometry/strategies/spherical/intersection.hpp b/include/boost/geometry/strategies/spherical/intersection.hpp index 6ab085ab8..6974d453a 100644 --- a/include/boost/geometry/strategies/spherical/intersection.hpp +++ b/include/boost/geometry/strategies/spherical/intersection.hpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -28,7 +27,6 @@ #include #include #include -#include #include #include @@ -773,188 +771,6 @@ struct relate_spherical_segments_calc_policy } }; -template -struct relate_great_elliptic_segments_calc_policy - : relate_spherical_segments_calc_policy -{ - explicit relate_great_elliptic_segments_calc_policy(Spheroid const& spheroid = Spheroid()) - : m_spheroid(spheroid) - {} - - template - Point from_cart3d(Point3d const& point_3d) const - { - return formula::cart3d_to_geo(point_3d, m_spheroid); - } - - template - Point3d to_cart3d(Point const& point) const - { - return formula::geo_to_cart3d(point, m_spheroid); - } - - // relate_xxx_calc_policy must live londer than plane because it contains - // Spheroid object and plane keeps the reference to that object. - template - struct plane - { - typedef typename coordinate_type::type coord_t; - - // not normalized - plane(Point3d const& p1, Point3d const& p2) - : normal(cross_product(p1, p2)) - {} - - int side_value(Point3d const& pt) const - { - return formula::sph_side_value(normal, pt); - } - - coord_t cos_angle_between(Point3d const& p1, Point3d const& p2) const - { - Point3d v1 = p1; - detail::vec_normalize(v1); - Point3d v2 = p2; - detail::vec_normalize(v2); - - return dot_product(v1, v2); - } - - coord_t cos_angle_between(Point3d const& p1, Point3d const& p2, bool & is_forward) const - { - coord_t const c0 = 0; - - Point3d v1 = p1; - detail::vec_normalize(v1); - Point3d v2 = p2; - detail::vec_normalize(v2); - - is_forward = dot_product(normal, cross_product(v1, v2)) >= c0; - return dot_product(v1, v2); - } - - Point3d normal; - }; - - template - plane get_plane(Point3d const& p1, Point3d const& p2) const - { - return plane(p1, p2); - } - - template - bool intersection_points(plane const& plane1, - plane const& plane2, - Point3d & ip1, Point3d & ip2) const - { - typedef typename coordinate_type::type coord_t; - - Point3d id = cross_product(plane1.normal, plane2.normal); - // NOTE: the length should be greater than 0 at this point - // NOTE: no need to normalize in this case - - ip1 = formula::projected_to_surface(id, m_spheroid); - - ip2 = ip1; - multiply_value(ip2, coord_t(-1)); - - return true; - } - -private: - Spheroid m_spheroid; -}; - -template -struct relate_experimental_elliptic_segments_calc_policy -{ - explicit relate_experimental_elliptic_segments_calc_policy(Spheroid const& spheroid = Spheroid()) - : m_spheroid(spheroid) - {} - - template - Point from_cart3d(Point3d const& point_3d) const - { - return formula::cart3d_to_geo(point_3d, m_spheroid); - } - - template - Point3d to_cart3d(Point const& point) const - { - return formula::geo_to_cart3d(point, m_spheroid); - } - - // relate_xxx_calc_policy must live londer than plane because it contains - // Spheroid object and plane keeps the reference to that object. - template - struct plane - { - typedef typename coordinate_type::type coord_t; - - // not normalized - plane(Point3d const& p1, Point3d const& p2, Spheroid const& spheroid) - : m_spheroid(spheroid) - { - formula::experimental_elliptic_plane(p1, p2, origin, normal, m_spheroid); - } - - int side_value(Point3d const& pt) const - { - return formula::elliptic_side_value(origin, normal, pt); - } - - coord_t cos_angle_between(Point3d const& p1, Point3d const& p2) const - { - Point3d const v1 = normalized_vec(p1); - Point3d const v2 = normalized_vec(p2); - return dot_product(v1, v2); - } - - coord_t cos_angle_between(Point3d const& p1, Point3d const& p2, bool & is_forward) const - { - coord_t const c0 = 0; - - Point3d const v1 = normalized_vec(p1); - Point3d const v2 = normalized_vec(p2); - - is_forward = dot_product(normal, cross_product(v1, v2)) >= c0; - return dot_product(v1, v2); - } - - Point3d origin; - Point3d normal; - - private: - Point3d normalized_vec(Point3d const& p) const - { - Point3d v = p; - subtract_point(v, origin); - detail::vec_normalize(v); - return v; - } - - Spheroid const& m_spheroid; - }; - - template - plane get_plane(Point3d const& p1, Point3d const& p2) const - { - return plane(p1, p2, m_spheroid); - } - - template - bool intersection_points(plane const& plane1, - plane const& plane2, - Point3d & ip1, Point3d & ip2) const - { - return formula::planes_spheroid_intersection(plane1.origin, plane1.normal, - plane2.origin, plane2.normal, - ip1, ip2, m_spheroid); - } - -private: - Spheroid m_spheroid; -}; template struct relate_spherical_segments @@ -966,26 +782,6 @@ struct relate_spherical_segments > {}; -template , typename CalculationType = void> -struct relate_great_elliptic_segments - : relate_ecef_segments - < - Policy, - relate_great_elliptic_segments_calc_policy, - CalculationType - > -{}; - -template , typename CalculationType = void> -struct relate_experimental_elliptic_segments - : relate_ecef_segments - < - Policy, - relate_experimental_elliptic_segments_calc_policy, - CalculationType - > -{}; - #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS namespace services diff --git a/include/boost/geometry/strategies/strategies.hpp b/include/boost/geometry/strategies/strategies.hpp index 342485cc4..fe5c4c720 100644 --- a/include/boost/geometry/strategies/strategies.hpp +++ b/include/boost/geometry/strategies/strategies.hpp @@ -68,6 +68,7 @@ #include #include #include +//#include //#include //#include //#include From 502a42746fe8bbf7819bb3c91c6e3648fa40c594 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 24 Aug 2016 17:47:43 +0200 Subject: [PATCH 12/59] [test][formula] Change function name. --- test/formulas/intersection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/formulas/intersection.cpp b/test/formulas/intersection.cpp index 15c41137a..773974500 100644 --- a/test/formulas/intersection.cpp +++ b/test/formulas/intersection.cpp @@ -89,7 +89,7 @@ void test_all(expected_results const& results) point_3d resv(0, 0); point_geo res(0, 0); - bg::formula::elliptic_intersection(a1v, a2v, b1v, b2v, resv, spheroid); + bg::formula::experimental_elliptic_intersection(a1v, a2v, b1v, b2v, resv, spheroid); res = bg::formula::cart3d_to_geo(resv, spheroid); result.lon = bg::get<0>(res); result.lat = bg::get<1>(res); From ca13a924c76eac1ecc0a7038ebb127552bbb157f Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 24 Aug 2016 17:48:18 +0200 Subject: [PATCH 13/59] [test][strategies] Add test for geographic intersection strategies. Reuse large portion of spherical intersection strategy test so move the common code into separate file. --- test/strategies/Jamfile.v2 | 1 + test/strategies/segment_intersection_geo.cpp | 357 +++++++++++++++++++ test/strategies/segment_intersection_sph.cpp | 135 +------ test/strategies/segment_intersection_sph.hpp | 159 +++++++++ 4 files changed, 524 insertions(+), 128 deletions(-) create mode 100644 test/strategies/segment_intersection_geo.cpp create mode 100644 test/strategies/segment_intersection_sph.hpp diff --git a/test/strategies/Jamfile.v2 b/test/strategies/Jamfile.v2 index 7848ccbf5..e7ceac952 100644 --- a/test/strategies/Jamfile.v2 +++ b/test/strategies/Jamfile.v2 @@ -30,6 +30,7 @@ test-suite boost-geometry-strategies [ run pythagoras_point_box.cpp : : : : strategies_pythagoras_point_box ] [ run spherical_side.cpp : : : : strategies_spherical_side ] [ run segment_intersection_collinear.cpp : : : : strategies_segment_intersection_collinear ] + [ run segment_intersection_geo.cpp : : : : strategies_segment_intersection_geo ] [ run segment_intersection_sph.cpp : : : : strategies_segment_intersection_sph ] [ run side_of_intersection.cpp : : : : strategies_side_of_intersection ] [ run thomas.cpp : : : : strategies_thomas ] diff --git a/test/strategies/segment_intersection_geo.cpp b/test/strategies/segment_intersection_geo.cpp new file mode 100644 index 000000000..563b0075d --- /dev/null +++ b/test/strategies/segment_intersection_geo.cpp @@ -0,0 +1,357 @@ +// Boost.Geometry +// Unit Test + +// Copyright (c) 2016, Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + + +#include "segment_intersection_sph.hpp" + + +#include + + +template +void test_default_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") +{ + typedef typename bg::coordinate_type

::type coord_t; + typedef bg::policies::relate::segments_tupled + < + bg::policies::relate::segments_intersection_points + < + bg::segment_intersection_points > + >, + bg::policies::relate::segments_direction + > policy_t; + + typename bg::strategy::intersection::services::default_strategy + < + bg::geographic_tag, + policy_t, + void + >::type strategy; + + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); +} + +template +void test_great_elliptic_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") +{ + typedef typename bg::coordinate_type

::type coord_t; + typedef bg::policies::relate::segments_tupled + < + bg::policies::relate::segments_intersection_points + < + bg::segment_intersection_points > + >, + bg::policies::relate::segments_direction + > policy_t; + + bg::strategy::intersection::relate_great_elliptic_segments + < + policy_t + > strategy; + + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); +} + +template +void test_experimental_elliptic_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") +{ + typedef typename bg::coordinate_type

::type coord_t; + typedef bg::policies::relate::segments_tupled + < + bg::policies::relate::segments_intersection_points + < + bg::segment_intersection_points > + >, + bg::policies::relate::segments_direction + > policy_t; + + bg::strategy::intersection::relate_experimental_elliptic_segments + < + policy_t + > strategy; + + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); +} + +template +void test_geographic_strategies(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") +{ + test_default_strategy(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); + test_great_elliptic_strategy(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); + test_experimental_elliptic_strategy(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); +} + +template +void test_geographic() +{ + typedef bg::model::point > point_t; + typedef bg::model::segment segment_t; + + // crossing X + test_geographic_strategies( + "SEGMENT(-45 -45, 45 45)", "SEGMENT(-45 45, 45 -45)", 'i', 1, "POINT(0 0)"); + test_geographic_strategies( + "SEGMENT(-45 -45, 45 45)", "SEGMENT(45 -45, -45 45)", 'i', 1, "POINT(0 0)"); + test_geographic_strategies( + "SEGMENT(45 45, -45 -45)", "SEGMENT(-45 45, 45 -45)", 'i', 1, "POINT(0 0)"); + test_geographic_strategies( + "SEGMENT(45 45, -45 -45)", "SEGMENT(45 -45, -45 45)", 'i', 1, "POINT(0 0)"); + // crossing X + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 1, 1 -1)", 'i', 1, "POINT(0 0)"); + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(1 -1, -1 1)", 'i', 1, "POINT(0 0)"); + test_geographic_strategies( + "SEGMENT(1 1, -1 -1)", "SEGMENT(-1 1, 1 -1)", 'i', 1, "POINT(0 0)"); + test_geographic_strategies( + "SEGMENT(1 1, -1 -1)", "SEGMENT(1 -1, -1 1)", 'i', 1, "POINT(0 0)"); + + // equal + // // + test_geographic_strategies( + "SEGMENT(-45 -45, 45 45)", "SEGMENT(-45 -45, 45 45)", 'e', 2, "POINT(-45 -45)", "POINT(45 45)"); + // // + test_geographic_strategies( + "SEGMENT(-45 -45, 45 45)", "SEGMENT(45 45, -45 -45)", 'e', 2, "POINT(-45 -45)", "POINT(45 45)"); + + // starting outside s1 + // / + // | + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, -1 -1)", 'a', 1, "POINT(-1 -1)"); + // / + // /| + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, 0 0)", 'm', 1, "POINT(0 0)"); + // /| + // / | + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, 1 1)", 't', 1, "POINT(1 1)"); + // |/ + // /| + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, 2 2)", 'i', 1, "POINT(0 0)"); + // ------ + // ------ + test_geographic_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, -1 0)", 'a', 1, "POINT(-1 0)"); + // ------ + // ------ + test_geographic_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, 0 0)", 'c', 2, "POINT(-1 0)", "POINT(0 0)"); + // ------ + // --------- + test_geographic_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, 1 0)", 'c', 2, "POINT(-1 0)", "POINT(1 0)"); + // ------ + // ------------ + test_geographic_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, 2 0)", 'c', 2, "POINT(-1 0)", "POINT(1 0)"); + + // starting at s1 + // / + // // + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, 0 0)", 'c', 2, "POINT(-1 -1)", "POINT(0 0)"); + // // + // // + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, 1 1)", 'e', 2, "POINT(-1 -1)", "POINT(1 1)"); + // | / + // |/ + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, 2 2)", 'f', 1, "POINT(-1 -1)"); + // ------ + // --- + test_geographic_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, 0 0)", 'c', 2, "POINT(-1 0)", "POINT(0 0)"); + // ------ + // ------ + test_geographic_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, 1 0)", 'e', 2, "POINT(-1 0)", "POINT(1 0)"); + // ------ + // --------- + test_geographic_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, 2 0)", 'c', 2, "POINT(-1 0)", "POINT(1 0)"); + + // starting inside + // // + // / + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(0 0, 1 1)", 'c', 2, "POINT(0 0)", "POINT(1 1)"); + // |/ + // / + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(0 0, 2 2)", 's', 1, "POINT(0 0)"); + // ------ + // --- + test_geographic_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(0 0, 1 0)", 'c', 2, "POINT(0 0)", "POINT(1 0)"); + // ------ + // ------ + test_geographic_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(0 0, 2 0)", 'c', 2, "POINT(0 0)", "POINT(1 0)"); + + // starting at p2 + // | + // / + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(1 1, 2 2)", 'a', 1, "POINT(1 1)"); + // ------ + // --- + test_geographic_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(1 0, 2 0)", 'a', 1, "POINT(1 0)"); + + // disjoint, crossing + // / + // | + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-3 -3, -2 -2)", 'd', 0); + // | + // / + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(2 2, 3 3)", 'd', 0); + // disjoint, collinear + // ------ + // ------ + test_geographic_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-3 0, -2 0)", 'd', 0); + // ------ + // ------ + test_geographic_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(2 0, 3 0)", 'd', 0); + + // degenerated + // / + // * + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, -2 -2)", 'd', 0); + // / + // * + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, -1 -1)", '0', 1, "POINT(-1 -1)"); + // / + // * + // / + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(0 0, 0 0)", '0', 1, "POINT(0 0)"); + // * + // / + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(1 1, 1 1)", '0', 1, "POINT(1 1)"); + // * + // / + test_geographic_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(2 2, 2 2)", 'd', 0); + // similar to above, collinear + // * ------ + test_geographic_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, -2 0)", 'd', 0); + // *------ + test_geographic_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, -1 0)", '0', 1, "POINT(-1 0)"); + // ---*--- + test_geographic_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(0 0, 0 0)", '0', 1, "POINT(0 0)"); + // ------* + test_geographic_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(1 0, 1 0)", '0', 1, "POINT(1 0)"); + // ------ * + test_geographic_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(2 0, 2 0)", 'd', 0); + + // Northern hemisphere + // --- ------ + test_geographic_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-3 50, -2 50)", 'd', 0); + // ------ + // --- + test_geographic_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, -1 50)", 'a', 1, "POINT(-1 50)"); + // \/ + // /\ (avoid multi-line comment) + test_default_strategy( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 0 50)", 'i', 1, "POINT(-0.5 50.0032229484023)"); + test_great_elliptic_strategy( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 0 50)", 'i', 1, "POINT(-0.5 50.0032229484023)"); + test_experimental_elliptic_strategy( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 0 50)", 'i', 1, "POINT(-0.5 50.0032319221120)"); + // ________ + // / _____\ (avoid multi-line comment) + test_geographic_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 1 50)", 't', 1, "POINT(1 50)"); + // _________ + // / _____ \ (avoid multi-line comment) + test_geographic_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 2 50)", 'd', 0); + // ______ + // /___ \ (avoid multi-line comment) + test_geographic_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-1 50, 0 50)", 'f', 1, "POINT(-1 50)"); + // ------ + // ------ + test_geographic_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-1 50, 1 50)", 'e', 2, "POINT(-1 50)", "POINT(1 50)"); + // ________ + // /_____ \ (avoid multi-line comment) + test_geographic_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-1 50, 2 50)", 'f', 1, "POINT(-1 50)"); + // ______ + // / ___\ (avoid multi-line comment) + test_geographic_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(0 50, 1 50)", 't', 1, "POINT(1 50)"); + // \/ + // /\ (avoid multi-line comment) + test_default_strategy( + "SEGMENT(-1 50, 1 50)", "SEGMENT(0 50, 2 50)", 'i', 1, "POINT(0.5 50.0032229484023)"); + test_great_elliptic_strategy( + "SEGMENT(-1 50, 1 50)", "SEGMENT(0 50, 2 50)", 'i', 1, "POINT(0.5 50.0032229484023)"); + test_experimental_elliptic_strategy( + "SEGMENT(-1 50, 1 50)", "SEGMENT(0 50, 2 50)", 'i', 1, "POINT(0.5 50.0032319221120)"); + // ------ + // --- + test_geographic_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(1 50, 2 50)", 'a', 1, "POINT(1 50)"); + // ------ --- + test_geographic_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(2 50, 3 50)", 'd', 0); + + // ___| + test_geographic_strategies( + "SEGMENT(0 0, 1 0)", "SEGMENT(1 0, 1 1)", 'a', 1, "POINT(1 0)"); + // ___| + test_geographic_strategies( + "SEGMENT(1 0, 1 1)", "SEGMENT(0 0, 1 0)", 'a', 1, "POINT(1 0)"); + + // |/ + // /| + test_geographic_strategies( + "SEGMENT(10 -1, 20 1)", "SEGMENT(12.5 -1, 12.5 1)", 'i', 1, "POINT(12.5 -0.50051443471392)"); + // |/ + // /| + test_geographic_strategies( + "SEGMENT(10 -1, 20 1)", "SEGMENT(17.5 -1, 17.5 1)", 'i', 1, "POINT(17.5 0.50051443471392)"); +} + +int test_main(int, char* []) +{ + //test_geographic(); + test_geographic(); + + return 0; +} diff --git a/test/strategies/segment_intersection_sph.cpp b/test/strategies/segment_intersection_sph.cpp index ae94e1e22..9d9e7cdc2 100644 --- a/test/strategies/segment_intersection_sph.cpp +++ b/test/strategies/segment_intersection_sph.cpp @@ -9,44 +9,16 @@ // http://www.boost.org/LICENSE_1_0.txt) -#include +#include "segment_intersection_sph.hpp" -#include - -#include -#include - -#include -#include - -#include -#include -#include #include -template -bool equals_relaxed(P1 const& p1, P2 const& p2, T const& eps_scale) -{ - typedef typename bg::select_coordinate_type::type calc_t; - calc_t c1 = 1; - calc_t p10 = bg::get<0>(p1); - calc_t p11 = bg::get<1>(p1); - calc_t p20 = bg::get<0>(p2); - calc_t p21 = bg::get<1>(p2); - calc_t relaxed_eps = std::numeric_limits::epsilon() - * bg::math::detail::greatest(c1, bg::math::abs(p10), bg::math::abs(p11), bg::math::abs(p20), bg::math::abs(p21)) - * eps_scale; - calc_t diff0 = p10 - p20; - // needed e.g. for -179.999999 - 180.0 - if (diff0 < -180) - diff0 += 360; - return bg::math::abs(diff0) <= relaxed_eps - && bg::math::abs(p11 - p21) <= relaxed_eps; -} -template -void test_spherical_strategy_one(S1 const& s1, S2 const& s2, char m, std::size_t expected_count, P const& ip0 = P(), P const& ip1 = P()) +template +void test_spherical_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") { typedef typename bg::coordinate_type

::type coord_t; typedef bg::policies::relate::segments_tupled @@ -61,102 +33,9 @@ void test_spherical_strategy_one(S1 const& s1, S2 const& s2, char m, std::size_t < policy_t > strategy_t; - typedef typename strategy_t::return_type return_type; + strategy_t strategy; - // NOTE: robust policy is currently ignored - return_type res = strategy_t::apply(s1, s2, 0); - - size_t const res_count = boost::get<0>(res).count; - char const res_method = boost::get<1>(res).how; - - BOOST_CHECK_MESSAGE(res_method == m, - "IP method: " << res_method << " different than expected: " << m - << " for " << bg::wkt(s1) << " and " << bg::wkt(s2)); - - BOOST_CHECK_MESSAGE(res_count == expected_count, - "IP count: " << res_count << " different than expected: " << expected_count - << " for " << bg::wkt(s1) << " and " << bg::wkt(s2)); - - // The EPS is scaled because during the conversion various angles may be not converted - // to cartesian 3d the same way which results in a different intersection point - // For more information read the info in spherical intersection strategy file. - - int eps_scale = res_method != 'i' ? 1 : 1000; - - if (res_count > 0 && expected_count > 0) - { - P const& res_i0 = boost::get<0>(res).intersections[0]; - BOOST_CHECK_MESSAGE(equals_relaxed(res_i0, ip0, eps_scale), - "IP0: " << std::setprecision(16) << bg::wkt(res_i0) << " different than expected: " << bg::wkt(ip0) - << " for " << bg::wkt(s1) << " and " << bg::wkt(s2)); - } - if (res_count > 1 && expected_count > 1) - { - P const& res_i1 = boost::get<0>(res).intersections[1]; - BOOST_CHECK_MESSAGE(equals_relaxed(res_i1, ip1, eps_scale), - "IP1: " << std::setprecision(16) << bg::wkt(res_i1) << " different than expected: " << bg::wkt(ip1) - << " for " << bg::wkt(s1) << " and " << bg::wkt(s2)); - } -} - -template -T translated(T v, double t) -{ - v += T(t); - if (v > 180) - v -= 360; - return v; -} - -template -void test_spherical_strategy(S1 const& s1, S2 const& s2, char m, std::size_t expected_count, P const& ip0 = P(), P const& ip1 = P()) -{ - S1 s1t = s1; - S2 s2t = s2; - P ip0t = ip0; - P ip1t = ip1; - - double t = 0; - for (int i = 0; i < 4; ++i, t += 90) - { - bg::set<0, 0>(s1t, translated(bg::get<0, 0>(s1), t)); - bg::set<1, 0>(s1t, translated(bg::get<1, 0>(s1), t)); - bg::set<0, 0>(s2t, translated(bg::get<0, 0>(s2), t)); - bg::set<1, 0>(s2t, translated(bg::get<1, 0>(s2), t)); - if (expected_count > 0) - bg::set<0>(ip0t, translated(bg::get<0>(ip0), t)); - if (expected_count > 1) - bg::set<0>(ip1t, translated(bg::get<0>(ip1), t)); - - test_spherical_strategy_one(s1t, s2t, m, expected_count, ip0t, ip1t); - } -} - -template -void test_spherical_strategy(std::string const& s1_wkt, std::string const& s2_wkt, - char m, std::size_t expected_count, - std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") -{ - S1 s1; - S2 s2; - P ip0, ip1; - - bg::read_wkt(s1_wkt, s1); - bg::read_wkt(s2_wkt, s2); - if (! ip0_wkt.empty()) - bg::read_wkt(ip0_wkt, ip0); - if (!ip1_wkt.empty()) - bg::read_wkt(ip1_wkt, ip1); - - test_spherical_strategy(s1, s2, m, expected_count, ip0, ip1); -} - -template -void test_spherical_strategy(std::string const& s1_wkt, std::string const& s2_wkt, - char m, std::size_t expected_count, - std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") -{ - test_spherical_strategy(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); } template diff --git a/test/strategies/segment_intersection_sph.hpp b/test/strategies/segment_intersection_sph.hpp new file mode 100644 index 000000000..b3a6dc067 --- /dev/null +++ b/test/strategies/segment_intersection_sph.hpp @@ -0,0 +1,159 @@ +// Boost.Geometry +// Unit Test + +// Copyright (c) 2016, Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_TEST_STRATEGIES_SEGMENT_INTERSECTION_SPH_HPP +#define BOOST_GEOMETRY_TEST_STRATEGIES_SEGMENT_INTERSECTION_SPH_HPP + + +#include + +#include + +#include +#include + +#include +#include + +#include +#include +#include + + +template +bool equals_relaxed(P1 const& p1, P2 const& p2, T const& eps_scale) +{ + typedef typename bg::select_coordinate_type::type calc_t; + calc_t c1 = 1; + calc_t p10 = bg::get<0>(p1); + calc_t p11 = bg::get<1>(p1); + calc_t p20 = bg::get<0>(p2); + calc_t p21 = bg::get<1>(p2); + calc_t relaxed_eps = std::numeric_limits::epsilon() + * bg::math::detail::greatest(c1, bg::math::abs(p10), bg::math::abs(p11), bg::math::abs(p20), bg::math::abs(p21)) + * eps_scale; + calc_t diff0 = p10 - p20; + // needed e.g. for -179.999999 - 180.0 + if (diff0 < -180) + diff0 += 360; + return bg::math::abs(diff0) <= relaxed_eps + && bg::math::abs(p11 - p21) <= relaxed_eps; +} + +template +void test_strategy_one(S1 const& s1, S2 const& s2, + Strategy const& strategy, + char m, std::size_t expected_count, + P const& ip0 = P(), P const& ip1 = P()) +{ + typedef typename Strategy::return_type return_type; + + // NOTE: robust policy is currently ignored + return_type res = strategy.apply(s1, s2, 0); + + size_t const res_count = boost::get<0>(res).count; + char const res_method = boost::get<1>(res).how; + + BOOST_CHECK_MESSAGE(res_method == m, + "IP method: " << res_method << " different than expected: " << m + << " for " << bg::wkt(s1) << " and " << bg::wkt(s2)); + + BOOST_CHECK_MESSAGE(res_count == expected_count, + "IP count: " << res_count << " different than expected: " << expected_count + << " for " << bg::wkt(s1) << " and " << bg::wkt(s2)); + + // The EPS is scaled because during the conversion various angles may be not converted + // to cartesian 3d the same way which results in a different intersection point + // For more information read the info in spherical intersection strategy file. + + int eps_scale = res_method != 'i' ? 1 : 1000; + + if (res_count > 0 && expected_count > 0) + { + P const& res_i0 = boost::get<0>(res).intersections[0]; + BOOST_CHECK_MESSAGE(equals_relaxed(res_i0, ip0, eps_scale), + "IP0: " << std::setprecision(16) << bg::wkt(res_i0) << " different than expected: " << bg::wkt(ip0) + << " for " << bg::wkt(s1) << " and " << bg::wkt(s2)); + } + if (res_count > 1 && expected_count > 1) + { + P const& res_i1 = boost::get<0>(res).intersections[1]; + BOOST_CHECK_MESSAGE(equals_relaxed(res_i1, ip1, eps_scale), + "IP1: " << std::setprecision(16) << bg::wkt(res_i1) << " different than expected: " << bg::wkt(ip1) + << " for " << bg::wkt(s1) << " and " << bg::wkt(s2)); + } +} + +template +T translated(T v, double t) +{ + v += T(t); + if (v > 180) + v -= 360; + return v; +} + +template +void test_strategy(S1 const& s1, S2 const& s2, + Strategy const& strategy, + char m, std::size_t expected_count, + P const& ip0 = P(), P const& ip1 = P()) +{ + S1 s1t = s1; + S2 s2t = s2; + P ip0t = ip0; + P ip1t = ip1; + + double t = 0; + for (int i = 0; i < 4; ++i, t += 90) + { + bg::set<0, 0>(s1t, translated(bg::get<0, 0>(s1), t)); + bg::set<1, 0>(s1t, translated(bg::get<1, 0>(s1), t)); + bg::set<0, 0>(s2t, translated(bg::get<0, 0>(s2), t)); + bg::set<1, 0>(s2t, translated(bg::get<1, 0>(s2), t)); + if (expected_count > 0) + bg::set<0>(ip0t, translated(bg::get<0>(ip0), t)); + if (expected_count > 1) + bg::set<0>(ip1t, translated(bg::get<0>(ip1), t)); + + test_strategy_one(s1t, s2t, strategy, m, expected_count, ip0t, ip1t); + } +} + +template +void test_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + Strategy const& strategy, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") +{ + S1 s1; + S2 s2; + P ip0, ip1; + + bg::read_wkt(s1_wkt, s1); + bg::read_wkt(s2_wkt, s2); + if (! ip0_wkt.empty()) + bg::read_wkt(ip0_wkt, ip0); + if (!ip1_wkt.empty()) + bg::read_wkt(ip1_wkt, ip1); + + test_strategy(s1, s2, strategy, m, expected_count, ip0, ip1); +} + +template +void test_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + Strategy const& strategy, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") +{ + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); +} + +#endif // BOOST_GEOMETRY_TEST_STRATEGIES_SEGMENT_INTERSECTION_SPH_HPP From 3624d840d670697635c13ecd63902cca9de3d1c5 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 24 Aug 2016 19:47:01 +0200 Subject: [PATCH 14/59] [test][formulas] Change the name of a member of intersection expected results. --- test/formulas/intersection.cpp | 4 ++-- test/formulas/intersection_cases.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/formulas/intersection.cpp b/test/formulas/intersection.cpp index 773974500..4c88f65e7 100644 --- a/test/formulas/intersection.cpp +++ b/test/formulas/intersection.cpp @@ -86,14 +86,14 @@ void test_all(expected_results const& results) point_3d a2v = bg::formula::geo_to_cart3d(a2, spheroid); point_3d b1v = bg::formula::geo_to_cart3d(b1, spheroid); point_3d b2v = bg::formula::geo_to_cart3d(b2, spheroid); - point_3d resv(0, 0); + point_3d resv(0, 0, 0); point_geo res(0, 0); bg::formula::experimental_elliptic_intersection(a1v, a2v, b1v, b2v, resv, spheroid); res = bg::formula::cart3d_to_geo(resv, spheroid); result.lon = bg::get<0>(res); result.lat = bg::get<1>(res); - check_inverse(result, results.elliptic, results.gnomonic_karney, 0.0001); + check_inverse(result, results.experimental_elliptic, results.gnomonic_karney, 0.0001); bg::formula::great_elliptic_intersection(a1v, a2v, b1v, b2v, resv, spheroid); res = bg::formula::cart3d_to_geo(resv, spheroid); diff --git a/test/formulas/intersection_cases.hpp b/test/formulas/intersection_cases.hpp index 277e2b698..4b52182d2 100644 --- a/test/formulas/intersection_cases.hpp +++ b/test/formulas/intersection_cases.hpp @@ -37,7 +37,7 @@ struct expected_results expected_result sjoberg_vincenty; expected_result sjoberg_thomas; expected_result sjoberg_andoyer; - expected_result elliptic; + expected_result experimental_elliptic; expected_result great_elliptic; }; From a40bbbc69cf5bda768778bcf24ade957a79863a4 Mon Sep 17 00:00:00 2001 From: Mats Taraldsvik Date: Mon, 5 Sep 2016 20:16:16 +0200 Subject: [PATCH 15/59] [algorithms] Added equals for MultiPoint --- include/boost/geometry/algorithms/equals.hpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/include/boost/geometry/algorithms/equals.hpp b/include/boost/geometry/algorithms/equals.hpp index 0f0bdde58..36c648829 100644 --- a/include/boost/geometry/algorithms/equals.hpp +++ b/include/boost/geometry/algorithms/equals.hpp @@ -242,6 +242,20 @@ struct equals > {}; +template +struct equals + : detail::equals::equals_by_relate +{}; + +template +struct equals + : detail::equals::equals_by_relate +{}; + +template +struct equals + : detail::equals::equals_by_relate +{}; template struct equals @@ -285,7 +299,6 @@ struct equals struct equals - //: detail::equals::equals_by_collection : detail::equals::equals_by_relate {}; From 7555eb3ccc6285217d5479713a1964528085360b Mon Sep 17 00:00:00 2001 From: Mats Taraldsvik Date: Mon, 5 Sep 2016 20:14:39 +0200 Subject: [PATCH 16/59] [io][wkb] Implement support for reading Multi-geometries Adds support for reading Multi-geometries in the WKB data format. --- extensions/test/gis/io/wkb/read_wkb.cpp | 239 ++++++++++++++++- .../extensions/gis/io/wkb/detail/ogc.hpp | 25 +- .../extensions/gis/io/wkb/detail/parser.hpp | 49 +++- .../extensions/gis/io/wkb/read_wkb.hpp | 36 +-- .../multi/gis/io/wkb/detail/parser.hpp | 248 ++++++++++++++++++ .../extensions/multi/gis/io/wkb/read_wkb.hpp | 62 +++++ 6 files changed, 613 insertions(+), 46 deletions(-) create mode 100644 include/boost/geometry/extensions/multi/gis/io/wkb/detail/parser.hpp create mode 100644 include/boost/geometry/extensions/multi/gis/io/wkb/read_wkb.hpp diff --git a/extensions/test/gis/io/wkb/read_wkb.cpp b/extensions/test/gis/io/wkb/read_wkb.cpp index eae9301e8..95e7f525a 100644 --- a/extensions/test/gis/io/wkb/read_wkb.cpp +++ b/extensions/test/gis/io/wkb/read_wkb.cpp @@ -27,6 +27,12 @@ #include #include +#include +#include +#include + +#include + namespace bg = boost::geometry; namespace { // anonymous @@ -42,6 +48,15 @@ void test_geometry_wrong_wkb(std::string const& wkbhex, std::string const& wkt) BOOST_MESSAGE("read_wkb: " << bg::read_wkb(wkb.begin(), wkb.end(), g_wkb)); } +template +void test_geometry_parse_failure(std::string const& wkbhex, std::string const& wkt) +{ + byte_vector wkb; + BOOST_CHECK( bg::hex2wkb(wkbhex, std::back_inserter(wkb)) ); + Geometry g_wkb; + BOOST_CHECK( bg::read_wkb(wkb.begin(), wkb.end(), g_wkb) == false ); +} + template void test_geometry_equals_old(std::string const& wkbhex, std::string const& wkt) { @@ -130,16 +145,17 @@ int test_main(int, char* []) // "POINT (1.234 5.678)"); } + + typedef bg::model::point point_type; + typedef bg::model::point point3d_type; + + typedef bg::model::linestring linestring_type; + //typedef bg::model::linestring linestring3d_type; + + typedef bg::model::polygon polygon_type; + //typedef bg::model::polygon polygon3d_type; { - typedef bg::model::point point_type; - typedef bg::model::point point3d_type; - - typedef bg::model::linestring linestring_type; - //typedef bg::model::linestring linestring3d_type; - - typedef bg::model::polygon polygon_type; - //typedef bg::model::polygon polygon3d_type; // // POINT @@ -165,6 +181,16 @@ int test_main(int, char* []) ); } + { + point3d_type point(1.234, 5.678, 99.0); + + test_geometry_equals + ( + point, + "01e90300005839b4c876bef33f83c0caa145b616400000000000c05840" + ); + } + // // LINESTRING // @@ -185,6 +211,30 @@ int test_main(int, char* []) "010200000003000000000000000000F03F00000000000000400000000000000040000000000000084000000000000010400000000000001440" ); } + + + { + linestring_type linestring; + + bg::append(linestring, + boost::assign::list_of + (point_type(1.234, 5.678)) + (point_type(9.1011, 10.1112)) + (point_type(13.1415, 16.1718)) + ); + + test_geometry_equals + ( + linestring, + "0102000000030000005839B4C876BEF33F83C0CAA145B616404F401361C333224062A1D634EF3824409CC420B072482A40EB73B515FB2B3040" + ); + + test_geometry_equals + ( + linestring, + "0102000080030000005839B4C876BEF33F83C0CAA145B616400000000000C058404F401361C333224062A1D634EF3824400000000000C058409CC420B072482A40EB73B515FB2B30400000000000C05840" + ); + } // { // linestring3d_type linestring; @@ -225,7 +275,26 @@ int test_main(int, char* []) "010300000001000000050000000000000000004940000000000000494000000000000049400000000000005940000000000000594000000000000059400000000000005940000000000000494000000000000049400000000000004940" ); } + + { + polygon_type polygon; + bg::append(polygon, + boost::assign::list_of + (point_type(100.0, 200.0)) + (point_type(200.0, 200.0)) + (point_type(200.0, 400.0)) + (point_type(100.0, 400.0)) + (point_type(100.0, 200.0)) + ); + + test_geometry_equals + ( + polygon, + "010300000001000000050000000000000000005940000000000000694000000000000069400000000000006940000000000000694000000000000079400000000000005940000000000000794000000000000059400000000000006940" + ); + } + // { // polygon3d_type polygon; // @@ -251,7 +320,7 @@ int test_main(int, char* []) ); // Create an interior ring (append does not do this automatically) - boost::geometry::interior_rings(polygon).resize(1); + polygon.inners().resize(1); bg::append(polygon, boost::assign::list_of @@ -269,6 +338,158 @@ int test_main(int, char* []) } } + + // + // Multi Geometries + // + + typedef bg::model::multi_point multipoint_type; + typedef bg::model::multi_linestring multilinestring_type; + typedef bg::model::multi_polygon multipolygon_type; + + // + // MultiPoint + // + + { + multipoint_type multipoint; + + bg::append(multipoint, + boost::assign::list_of + (point_type(1.234, 5.678)) + ); + test_geometry_equals + ( + multipoint, + "01040000000100000001010000005839b4c876bef33f83c0caa145b61640" + ); + } + + { + multipoint_type multipoint; + + bg::append(multipoint, + boost::assign::list_of + (point_type(1.234, 5.678)) + (point_type(10.1112, 13.1415)) + ); + + test_geometry_equals + ( + multipoint, + "01040000000200000001010000005839b4c876bef33f83c0caa145b61640010100000062a1d634ef3824409cc420b072482a40" + ); + } + + // + // MultiLineString + // + + { + multilinestring_type multilinestring; + + // Create linestrings (append does not do this automatically) + multilinestring.resize(1); + + bg::append(multilinestring[0], + boost::assign::list_of + (point_type(1.234, 5.678)) + (point_type(9.1011, 10.1112)) + (point_type(13.1415, 16.1718)) + ); + + test_geometry_equals + ( + multilinestring, + "0105000000010000000102000000030000005839b4c876bef33f83c0caa145b616404f401361c333224062a1d634ef3824409cc420b072482a40eb73b515fb2b3040" + ); + } + + { + multilinestring_type multilinestring; + + // Create linestrings (append does not do this automatically) + multilinestring.resize(2); + + bg::append(multilinestring[0], + boost::assign::list_of + (point_type(1.234, 5.678)) + (point_type(9.1011, 10.1112)) + (point_type(13.1415, 16.1718)) + ); + + bg::append(multilinestring[1], + boost::assign::list_of + (point_type(19.2, 21.22)) + (point_type(23.24, 25.26)) + ); + + test_geometry_equals + ( + multilinestring, +"0105000000020000000102000000030000005839b4c876bef33f83c0caa145b616404f401361c333224062a1d634ef3824409cc420b072482a40eb73b515fb2b30400102000000020000003333333333333340b81e85eb513835403d0ad7a3703d3740c3f5285c8f423940" + ); + } + + // + // MultiPolygon + // + + { + multipolygon_type multipolygon; + + // Create polygons (append does not do this automatically) + multipolygon.resize(1); + + bg::append(multipolygon[0], + boost::assign::list_of + (point_type(100, 200)) + (point_type(200, 200)) + (point_type(200, 400)) + (point_type(100, 400)) + (point_type(100, 200)) + ); + + test_geometry_equals + ( + multipolygon, +"010600000001000000010300000001000000050000000000000000005940000000000000694000000000000069400000000000006940000000000000694000000000000079400000000000005940000000000000794000000000000059400000000000006940" + ); + } + + { + multipolygon_type multipolygon; + + // Create polygons (append does not do this automatically) + multipolygon.resize(1); + // Create an interior ring (append does not do this automatically) + multipolygon[0].inners().resize(1); + + bg::append(multipolygon[0], + boost::assign::list_of + (point_type(35, 10)) + (point_type(10, 20)) + (point_type(15, 40)) + (point_type(45, 45)) + (point_type(35, 10)) + ); + + bg::append(multipolygon[0], + boost::assign::list_of + (point_type(20, 30)) + (point_type(35, 35)) + (point_type(30, 20)) + (point_type(20, 30)), + 0 + ); + + test_geometry_equals + ( + multipolygon, +"0106000000010000000103000000020000000500000000000000008041400000000000002440000000000000244000000000000034400000000000002e40000000000000444000000000008046400000000000804640000000000080414000000000000024400400000000000000000034400000000000003e40000000000080414000000000008041400000000000003e40000000000000344000000000000034400000000000003e40" + ); + } + return 0; } diff --git a/include/boost/geometry/extensions/gis/io/wkb/detail/ogc.hpp b/include/boost/geometry/extensions/gis/io/wkb/detail/ogc.hpp index b85447347..523461009 100644 --- a/include/boost/geometry/extensions/gis/io/wkb/detail/ogc.hpp +++ b/include/boost/geometry/extensions/gis/io/wkb/detail/ogc.hpp @@ -68,12 +68,10 @@ struct geometry_type_ogc { point = 1, linestring = 2, - polygon = 3 - - // TODO: Not implemented - //multipoint = 4, - //multilinestring = 5, - //multipolygon = 6, + polygon = 3, + multipoint = 4, + multilinestring = 5, + multipolygon = 6, //collection = 7 }; }; @@ -169,6 +167,21 @@ struct geometry_type : geometry_type_impl {}; +template +struct geometry_type + : geometry_type_impl +{}; + +template +struct geometry_type + : geometry_type_impl +{}; + +template +struct geometry_type + : geometry_type_impl +{}; + }} // namespace detail::wkb #endif // DOXYGEN_NO_IMPL diff --git a/include/boost/geometry/extensions/gis/io/wkb/detail/parser.hpp b/include/boost/geometry/extensions/gis/io/wkb/detail/parser.hpp index 8c649d659..0e6758e03 100644 --- a/include/boost/geometry/extensions/gis/io/wkb/detail/parser.hpp +++ b/include/boost/geometry/extensions/gis/io/wkb/detail/parser.hpp @@ -15,6 +15,8 @@ #include #include +#include + #include #include #include @@ -33,6 +35,23 @@ namespace boost { namespace geometry { +/*! +\brief Read WKB Exception +\ingroup core +\details The read_wkb_exception is thrown when there is an error in wkb parsing + */ +class read_wkb_exception : public geometry::exception +{ +public: + + inline read_wkb_exception() {} + + virtual char const* what() const throw() + { + return "Boost.Geometry Read WKB exception"; + } +}; + #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace wkb { @@ -191,9 +210,12 @@ struct point_container_parser { return false; } - + typedef typename std::iterator_traits::difference_type size_type; - BOOST_GEOMETRY_ASSERT(num_points <= boost::uint32_t( (std::numeric_limits::max)() ) ); + if(num_points > (std::numeric_limits::max)() ) + { + throw boost::geometry::read_wkb_exception(); + } size_type const container_size = static_cast(num_points); size_type const point_size = dimension::value * sizeof(double); @@ -237,7 +259,11 @@ struct linestring_parser return false; } - BOOST_GEOMETRY_ASSERT(it != end); + if(it == end) + { + throw boost::geometry::read_wkb_exception(); + } + return point_container_parser::parse(it, end, linestring, order); } }; @@ -259,15 +285,15 @@ struct polygon_parser { return false; } - - typedef typename ring_type::type ring_type; + + typedef typename boost::geometry::ring_return_type::type ring_type; std::size_t rings_parsed = 0; while (rings_parsed < num_rings && it != end) { if (0 == rings_parsed) { - ring_type& ring0 = exterior_ring(polygon); + ring_type ring0 = exterior_ring(polygon); if (!point_container_parser::parse(it, end, ring0, order)) { return false; @@ -275,19 +301,16 @@ struct polygon_parser } else { - interior_rings(polygon).resize(rings_parsed); - ring_type& ringN = interior_rings(polygon).back(); + boost::geometry::range::resize(interior_rings(polygon), rings_parsed); + ring_type ringN = boost::geometry::range::back(interior_rings(polygon)); + if (!point_container_parser::parse(it, end, ringN, order)) { return false; } } - ++rings_parsed; - } - if (num_rings != rings_parsed) - { - return false; + ++rings_parsed; } return true; diff --git a/include/boost/geometry/extensions/gis/io/wkb/read_wkb.hpp b/include/boost/geometry/extensions/gis/io/wkb/read_wkb.hpp index 69662b584..73b4cd220 100644 --- a/include/boost/geometry/extensions/gis/io/wkb/read_wkb.hpp +++ b/include/boost/geometry/extensions/gis/io/wkb/read_wkb.hpp @@ -27,38 +27,38 @@ namespace dispatch template struct read_wkb {}; -template -struct read_wkb +template +struct read_wkb { template - static inline bool parse(Iterator& it, Iterator end, G& geometry, + static inline bool parse(Iterator& it, Iterator end, Geometry& geometry, detail::wkb::byte_order_type::enum_t order) { - return detail::wkb::point_parser::parse(it, end, geometry, order); + return detail::wkb::point_parser::parse(it, end, geometry, order); } }; -template -struct read_wkb +template +struct read_wkb { template - static inline bool parse(Iterator& it, Iterator end, G& geometry, + static inline bool parse(Iterator& it, Iterator end, Geometry& geometry, detail::wkb::byte_order_type::enum_t order) { geometry::clear(geometry); - return detail::wkb::linestring_parser::parse(it, end, geometry, order); + return detail::wkb::linestring_parser::parse(it, end, geometry, order); } }; -template -struct read_wkb +template +struct read_wkb { template - static inline bool parse(Iterator& it, Iterator end, G& geometry, + static inline bool parse(Iterator& it, Iterator end, Geometry& geometry, detail::wkb::byte_order_type::enum_t order) { geometry::clear(geometry); - return detail::wkb::polygon_parser::parse(it, end, geometry, order); + return detail::wkb::polygon_parser::parse(it, end, geometry, order); } }; @@ -66,8 +66,8 @@ struct read_wkb #endif // DOXYGEN_NO_DISPATCH -template -inline bool read_wkb(Iterator begin, Iterator end, G& geometry) +template +inline bool read_wkb(Iterator begin, Iterator end, Geometry& geometry) { // Stream of bytes can only be parsed using random access iterator. BOOST_STATIC_ASSERT(( @@ -82,16 +82,16 @@ inline bool read_wkb(Iterator begin, Iterator end, G& geometry) { return dispatch::read_wkb < - typename tag::type, - G + typename tag::type, + Geometry >::parse(begin, end, geometry, byte_order); } return false; } -template -inline bool read_wkb(ByteType const* bytes, std::size_t length, G& geometry) +template +inline bool read_wkb(ByteType const* bytes, std::size_t length, Geometry& geometry) { BOOST_STATIC_ASSERT((boost::is_integral::value)); BOOST_STATIC_ASSERT((sizeof(boost::uint8_t) == sizeof(ByteType))); diff --git a/include/boost/geometry/extensions/multi/gis/io/wkb/detail/parser.hpp b/include/boost/geometry/extensions/multi/gis/io/wkb/detail/parser.hpp new file mode 100644 index 000000000..30009af4c --- /dev/null +++ b/include/boost/geometry/extensions/multi/gis/io/wkb/detail/parser.hpp @@ -0,0 +1,248 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_MULTI_IO_WKB_DETAIL_PARSER_HPP +#define BOOST_GEOMETRY_MULTI_IO_WKB_DETAIL_PARSER_HPP + +#include +#include +#include + +#include + +#include +#include +#include + + +#include +#include +#include + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace wkb +{ + +template +struct multipoint_parser +{ + template + static bool parse(Iterator& it, Iterator end, MultiPoint& multipoint, byte_order_type::enum_t order) + { + if (!geometry_type_parser::parse(it, end, order)) + { + return false; + } + + boost::uint32_t num_points(0); + if (!value_parser::parse(it, end, num_points, order)) + { + return false; + } + + // Check that the number of double values in the stream is equal + // or greater than the number of expected values in the multipoint + + typedef typename std::iterator_traits::difference_type size_type; + + typedef typename point_type::type point_type; + + size_type const container_byte_size = dimension::value * num_points * sizeof(double) + + num_points * sizeof(boost::uint8_t) + + num_points * sizeof(boost::uint32_t); + + size_type const stream_byte_size = std::distance(it,end); + + if(stream_byte_size < container_byte_size) + { + throw boost::geometry::read_wkb_exception(); + } + + point_type point_buffer; + std::back_insert_iterator output(std::back_inserter(multipoint)); + + typedef typename std::iterator_traits::difference_type size_type; + if(num_points > (std::numeric_limits::max)() ) + { + throw boost::geometry::read_wkb_exception(); + } + + size_type points_parsed = 0; + while (points_parsed < num_points && it != end) + { + detail::wkb::byte_order_type::enum_t point_byte_order; + if (!detail::wkb::byte_order_parser::parse(it, end, point_byte_order)) + { + return false; + } + + if (!geometry_type_parser::parse(it, end, point_byte_order)) + { + return false; + } + + parsing_assigner::value>::run(it, end, point_buffer, point_byte_order); + + output = point_buffer; + + ++output; + ++points_parsed; + } + return true; + } +}; + +template +struct multilinestring_parser +{ + template + static bool parse(Iterator& it, Iterator end, MultiLinestring& multilinestring, byte_order_type::enum_t order) + { + typedef typename MultiLinestring::value_type linestring_type; + typedef typename point_type::type point_type; + + if (!geometry_type_parser::parse(it, end, order)) + { + return false; + } + + boost::uint32_t num_linestrings(0); + if (!value_parser::parse(it, end, num_linestrings, order)) + { + return false; + } + + std::back_insert_iterator output(std::back_inserter(multilinestring)); + + typedef typename std::iterator_traits::difference_type size_type; + if(num_linestrings > (std::numeric_limits::max)() ) + { + throw boost::geometry::read_wkb_exception(); + } + + size_type linestrings_parsed = 0; + while (linestrings_parsed < num_linestrings && it != end) + { + linestring_type linestring_buffer; + + detail::wkb::byte_order_type::enum_t linestring_byte_order; + if (!detail::wkb::byte_order_parser::parse(it, end, linestring_byte_order)) + { + return false; + } + + if (!geometry_type_parser::parse(it, end, order)) + { + return false; + } + + if(!point_container_parser::parse(it, end, linestring_buffer, linestring_byte_order)) + { + return false; + } + + output = linestring_buffer; + + ++output; + ++linestrings_parsed; + } + return true; + } +}; + +template +struct multipolygon_parser +{ + template + static bool parse(Iterator& it, Iterator end, MultiPolygon& multipolygon, byte_order_type::enum_t order) + { + typedef typename boost::range_value::type polygon_type; + + if (!geometry_type_parser::parse(it, end, order)) + { + return false; + } + + boost::uint32_t num_polygons(0); + if (!value_parser::parse(it, end, num_polygons, order)) + { + return false; + } + + std::back_insert_iterator output(std::back_inserter(multipolygon)); + + std::size_t polygons_parsed = 0; + while(polygons_parsed < num_polygons && it != end) + { + polygon_type polygon_buffer; + + detail::wkb::byte_order_type::enum_t polygon_byte_order; + if (!detail::wkb::byte_order_parser::parse(it, end, polygon_byte_order)) + { + return false; + } + + if (!geometry_type_parser::parse(it, end, order)) + { + return false; + } + + boost::uint32_t num_rings(0); + if (!value_parser::parse(it, end, num_rings, polygon_byte_order)) + { + return false; + } + + std::size_t rings_parsed = 0; + + while (rings_parsed < num_rings && it != end) + { + typedef typename boost::geometry::ring_return_type::type ring_type; + + if (0 == rings_parsed) + { + ring_type ring0 = exterior_ring(polygon_buffer); + + if (!point_container_parser::parse(it, end, ring0, polygon_byte_order)) + { + return false; + } + } + else + { + boost::geometry::range::resize(interior_rings(polygon_buffer), rings_parsed); + ring_type ringN = boost::geometry::range::back(interior_rings(polygon_buffer)); + + if (!point_container_parser::parse(it, end, ringN, polygon_byte_order)) + { + return false; + } + } + ++rings_parsed; + } + + output = polygon_buffer; + ++output; + } + + return true; + } +}; + +}} // namespace detail::wkb +#endif // DOXYGEN_NO_IMPL + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_MULTI_IO_WKB_DETAIL_PARSER_HPP diff --git a/include/boost/geometry/extensions/multi/gis/io/wkb/read_wkb.hpp b/include/boost/geometry/extensions/multi/gis/io/wkb/read_wkb.hpp new file mode 100644 index 000000000..b8e3e65bd --- /dev/null +++ b/include/boost/geometry/extensions/multi/gis/io/wkb/read_wkb.hpp @@ -0,0 +1,62 @@ +// Boost.Geometry + +// Copyright (c) 2015 Mats Taraldsvik + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_MULTI_IO_WKB_READ_WKB_HPP +#define BOOST_GEOMETRY_MULTI_IO_WKB_READ_WKB_HPP + +#include + +#include +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +struct read_wkb +{ + template + static inline bool parse(Iterator& it, Iterator end, Geometry& geometry, + detail::wkb::byte_order_type::enum_t order) + { + return detail::wkb::multipoint_parser::parse(it, end, geometry, order); + } +}; + +template +struct read_wkb +{ + template + static inline bool parse(Iterator& it, Iterator end, Geometry& geometry, + detail::wkb::byte_order_type::enum_t order) + { + return detail::wkb::multilinestring_parser::parse(it, end, geometry, order); + } +}; + +template +struct read_wkb +{ + template + static inline bool parse(Iterator& it, Iterator end, Geometry& geometry, + detail::wkb::byte_order_type::enum_t order) + { + return detail::wkb::multipolygon_parser::parse(it, end, geometry, order); + } +}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_MULTI_IO_WKB_READ_WKB_HPP From f41ec1880f34cb8ff5d378ad88add61421b4fc2e Mon Sep 17 00:00:00 2001 From: Mats Taraldsvik Date: Wed, 26 Aug 2015 20:15:41 +0200 Subject: [PATCH 17/59] [io][wkb] Implement support for writing Multi-geometries --- extensions/test/gis/io/wkb/write_wkb.cpp | 177 +++++++++++++++++- .../multi/gis/io/wkb/detail/writer.hpp | 145 ++++++++++++++ .../extensions/multi/gis/io/wkb/write_wkb.hpp | 66 +++++++ 3 files changed, 380 insertions(+), 8 deletions(-) create mode 100644 include/boost/geometry/extensions/multi/gis/io/wkb/detail/writer.hpp create mode 100644 include/boost/geometry/extensions/multi/gis/io/wkb/write_wkb.hpp diff --git a/extensions/test/gis/io/wkb/write_wkb.cpp b/extensions/test/gis/io/wkb/write_wkb.cpp index 3b71c0f17..1e87eae90 100644 --- a/extensions/test/gis/io/wkb/write_wkb.cpp +++ b/extensions/test/gis/io/wkb/write_wkb.cpp @@ -27,9 +27,17 @@ #include +#include +#include +#include + +#include + #include #include +#include + namespace bg = boost::geometry; namespace { // anonymous @@ -42,10 +50,11 @@ void test_geometry_equals(Geometry const& geometry, std::string const& wkbhex) std::string hex_out; BOOST_CHECK( bg::wkb2hex(wkb_out.begin(), wkb_out.end(), hex_out) ); - + + boost::algorithm::to_lower(hex_out); + BOOST_CHECK_EQUAL( wkbhex, hex_out); } - } // namespace anonymous int test_main(int, char* []) @@ -67,7 +76,7 @@ int test_main(int, char* []) test_geometry_equals ( point, - "0101000000000000000000F03F0000000000000040" + "0101000000000000000000f03f0000000000000040" ); } @@ -77,7 +86,7 @@ int test_main(int, char* []) test_geometry_equals ( point, - "01E9030000000000000000F03F00000000000000400000000000000840" + "01e9030000000000000000f03f00000000000000400000000000000840" ); } @@ -98,7 +107,7 @@ int test_main(int, char* []) test_geometry_equals ( linestring, - "010200000003000000000000000000F03F00000000000000400000000000000040000000000000084000000000000010400000000000001440" + "010200000003000000000000000000f03f00000000000000400000000000000040000000000000084000000000000010400000000000001440" ); } @@ -115,7 +124,7 @@ int test_main(int, char* []) test_geometry_equals ( linestring, - "01EA03000003000000000000000000F03F00000000000000400000000000000840000000000000004000000000000008400000000000001040000000000000104000000000000014400000000000001840" + "01ea03000003000000000000000000f03f00000000000000400000000000000840000000000000004000000000000008400000000000001040000000000000104000000000000014400000000000001840" ); } @@ -157,7 +166,7 @@ int test_main(int, char* []) test_geometry_equals ( polygon, - "01EB0300000100000005000000000000000000494000000000000049400000000000001440000000000000494000000000000059400000000000002440000000000000594000000000000059400000000000001440000000000000594000000000000049400000000000002440000000000000494000000000000049400000000000001440" + "01eb0300000100000005000000000000000000494000000000000049400000000000001440000000000000494000000000000059400000000000002440000000000000594000000000000059400000000000001440000000000000594000000000000049400000000000002440000000000000494000000000000049400000000000001440" ); } @@ -187,9 +196,161 @@ int test_main(int, char* []) test_geometry_equals ( polygon, - "0103000000020000000500000000000000008041400000000000002440000000000080464000000000008046400000000000002E40000000000000444000000000000024400000000000003440000000000080414000000000000024400400000000000000000034400000000000003E40000000000080414000000000008041400000000000003E40000000000000344000000000000034400000000000003E40" + "0103000000020000000500000000000000008041400000000000002440000000000080464000000000008046400000000000002e40000000000000444000000000000024400000000000003440000000000080414000000000000024400400000000000000000034400000000000003e40000000000080414000000000008041400000000000003e40000000000000344000000000000034400000000000003e40" ); } + // + // Multi Geometries + // + + typedef bg::model::multi_point multipoint_type; + typedef bg::model::multi_linestring multilinestring_type; + typedef bg::model::multi_polygon multipolygon_type; + + // + // MultiPoint + // + + { + multipoint_type multipoint; + + bg::append(multipoint, + boost::assign::list_of + (point_type(1.234, 5.678)) + ); + + test_geometry_equals + ( + multipoint, + "01040000000100000001010000005839b4c876bef33f83c0caa145b61640" + ); + } + + { + multipoint_type multipoint; + + bg::append(multipoint, + boost::assign::list_of + (point_type(1.234, 5.678)) + (point_type(10.1112, 13.1415)) + ); + + test_geometry_equals + ( + multipoint, + "01040000000200000001010000005839b4c876bef33f83c0caa145b61640010100000062a1d634ef3824409cc420b072482a40" + ); + } + + // + // MultiLineString + // + + { + multilinestring_type multilinestring; + + // Create linestrings (append does not do this automatically) + multilinestring.resize(1); + + bg::append(multilinestring[0], + boost::assign::list_of + (point_type(1.234, 5.678)) + (point_type(9.1011, 10.1112)) + (point_type(13.1415, 16.1718)) + ); + + test_geometry_equals + ( + multilinestring, + "0105000000010000000102000000030000005839b4c876bef33f83c0caa145b616404f401361c333224062a1d634ef3824409cc420b072482a40eb73b515fb2b3040" + ); + } + + { + multilinestring_type multilinestring; + + // Create linestrings (append does not do this automatically) + multilinestring.resize(2); + + bg::append(multilinestring[0], + boost::assign::list_of + (point_type(1.234, 5.678)) + (point_type(9.1011, 10.1112)) + (point_type(13.1415, 16.1718)) + ); + + bg::append(multilinestring[1], + boost::assign::list_of + (point_type(19.2, 21.22)) + (point_type(23.24, 25.26)) + ); + + test_geometry_equals + ( + multilinestring, +"0105000000020000000102000000030000005839b4c876bef33f83c0caa145b616404f401361c333224062a1d634ef3824409cc420b072482a40eb73b515fb2b30400102000000020000003333333333333340b81e85eb513835403d0ad7a3703d3740c3f5285c8f423940" + ); + } + + // + // MultiPolygon + // + + { + multipolygon_type multipolygon; + + // Create polygons (append does not do this automatically) + multipolygon.resize(1); + + bg::append(multipolygon[0], + boost::assign::list_of + (point_type(100, 200)) + (point_type(200, 200)) + (point_type(200, 400)) + (point_type(100, 400)) + (point_type(100, 200)) + ); + + test_geometry_equals + ( + multipolygon, +"010600000001000000010300000001000000050000000000000000005940000000000000694000000000000069400000000000006940000000000000694000000000000079400000000000005940000000000000794000000000000059400000000000006940" + ); + } + + { + multipolygon_type multipolygon; + + // Create polygons (append does not do this automatically) + multipolygon.resize(1); + // Create an interior ring (append does not do this automatically) + multipolygon[0].inners().resize(1); + + bg::append(multipolygon[0], + boost::assign::list_of + (point_type(35, 10)) + (point_type(10, 20)) + (point_type(15, 40)) + (point_type(45, 45)) + (point_type(35, 10)) + ); + + bg::append(multipolygon[0], + boost::assign::list_of + (point_type(20, 30)) + (point_type(35, 35)) + (point_type(30, 20)) + (point_type(20, 30)), + 0 + ); + + test_geometry_equals + ( + multipolygon, +"0106000000010000000103000000020000000500000000000000008041400000000000002440000000000000244000000000000034400000000000002e40000000000000444000000000008046400000000000804640000000000080414000000000000024400400000000000000000034400000000000003e40000000000080414000000000008041400000000000003e40000000000000344000000000000034400000000000003e40" + ); + } + return 0; } diff --git a/include/boost/geometry/extensions/multi/gis/io/wkb/detail/writer.hpp b/include/boost/geometry/extensions/multi/gis/io/wkb/detail/writer.hpp new file mode 100644 index 000000000..5b826ce1a --- /dev/null +++ b/include/boost/geometry/extensions/multi/gis/io/wkb/detail/writer.hpp @@ -0,0 +1,145 @@ +// Boost.Geometry +// +// Copyright (c) 2015 Mats Taraldsvik. +// +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_MULTI_IO_WKB_DETAIL_WRITER_HPP +#define BOOST_GEOMETRY_MULTI_IO_WKB_DETAIL_WRITER_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace wkb +{ + template + struct multipoint_writer + { + template + static bool write(MultiPoint const& multipoint, + OutputIterator& iter, + byte_order_type::enum_t byte_order) + { + // write endian type + value_writer::write(byte_order, iter, byte_order); + + // write geometry type + uint32_t type = geometry_type::get(); + value_writer::write(type, iter, byte_order); + + // write num points + uint32_t num_points = boost::size(multipoint); + value_writer::write(num_points, iter, byte_order); + + typedef typename point_type::type point_type; + + for(typename boost::range_iterator::type + point_iter = boost::begin(multipoint); + point_iter != boost::end(multipoint); + ++point_iter) + { + detail::wkb::point_writer::write(*point_iter, iter, byte_order); + } + + return true; + } + }; + + template + struct multilinestring_writer + { + template + static bool write(MultiLinestring const& multilinestring, + OutputIterator& iter, + byte_order_type::enum_t byte_order) + { + // write endian type + value_writer::write(byte_order, iter, byte_order); + + // write geometry type + uint32_t type = geometry_type::get(); + value_writer::write(type, iter, byte_order); + + // write num linestrings + uint32_t num_linestrings = boost::size(multilinestring); + value_writer::write(num_linestrings, iter, byte_order); + + typedef typename boost::range_value::type linestring_type; + + for(typename boost::range_iterator::type + linestring_iter = boost::begin(multilinestring); + linestring_iter != boost::end(multilinestring); + ++linestring_iter) + { + detail::wkb::linestring_writer::write(*linestring_iter, iter, byte_order); + } + + return true; + } + }; + + template + struct multipolygon_writer + { + template + static bool write(MultiPolygon const& multipolygon, + OutputIterator& iter, + byte_order_type::enum_t byte_order) + { + // write endian type + value_writer::write(byte_order, iter, byte_order); + + // write geometry type + uint32_t type = geometry_type::get(); + value_writer::write(type, iter, byte_order); + + // write num polygons + uint32_t num_polygons = boost::size(multipolygon); + value_writer::write(num_polygons, iter, byte_order); + + typedef typename boost::range_value::type polygon_type; + + for(typename boost::range_iterator::type + polygon_iter = boost::begin(multipolygon); + polygon_iter != boost::end(multipolygon); + ++polygon_iter) + { + detail::wkb::polygon_writer::write(*polygon_iter, iter, byte_order); + } + + return true; + } + }; + +}} // namespace detail::wkb +#endif // DOXYGEN_NO_IMPL + +}} // namespace boost::geometry +#endif // BOOST_GEOMETRY_MULTI_IO_WKB_DETAIL_WRITER_HPP diff --git a/include/boost/geometry/extensions/multi/gis/io/wkb/write_wkb.hpp b/include/boost/geometry/extensions/multi/gis/io/wkb/write_wkb.hpp new file mode 100644 index 000000000..1deed3ed3 --- /dev/null +++ b/include/boost/geometry/extensions/multi/gis/io/wkb/write_wkb.hpp @@ -0,0 +1,66 @@ +// Boost.Geometry +// +// Copyright (c) 2015 Mats Taraldsvik. +// +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_MULTI_IO_WKB_WRITE_WKB_HPP +#define BOOST_GEOMETRY_MULTI_IO_WKB_WRITE_WKB_HPP + +#include + +#include +#include + +#include + +#include +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +struct write_wkb +{ + template + static inline bool write(const Geometry& geometry, OutputIterator iter, + detail::wkb::byte_order_type::enum_t byte_order) + { + return detail::wkb::multipoint_writer::write(geometry, iter, byte_order); + } +}; + +template +struct write_wkb +{ + template + static inline bool write(const Geometry& geometry, OutputIterator iter, + detail::wkb::byte_order_type::enum_t byte_order) + { + return detail::wkb::multilinestring_writer::write(geometry, iter, byte_order); + } +}; + +template +struct write_wkb +{ + template + static inline bool write(const Geometry& geometry, OutputIterator iter, + detail::wkb::byte_order_type::enum_t byte_order) + { + return detail::wkb::multipolygon_writer::write(geometry, iter, byte_order); + } +}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +}} // namespace boost::geometry +#endif // BOOST_GEOMETRY_MULTI_IO_WKB_WRITE_WKB_HPP From c328e12e7f1fcadd444142d1b31ecf68f1593925 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 3 Sep 2016 13:04:52 +0200 Subject: [PATCH 18/59] [formulas] Handle vertical segments and other special cases in sjoberg intersection formula. --- .../formulas/sjoberg_intersection.hpp | 290 ++++++++++++------ 1 file changed, 203 insertions(+), 87 deletions(-) diff --git a/include/boost/geometry/formulas/sjoberg_intersection.hpp b/include/boost/geometry/formulas/sjoberg_intersection.hpp index c8e13dd48..795300307 100644 --- a/include/boost/geometry/formulas/sjoberg_intersection.hpp +++ b/include/boost/geometry/formulas/sjoberg_intersection.hpp @@ -77,8 +77,6 @@ public: { // coordinates in radians - // TODO - handle special cases like degenerated segments, equator, poles, etc. - CT const c0 = 0; CT const c1 = 1; CT const c2 = 2; @@ -135,116 +133,226 @@ public: return true; } - CT const t01 = sqrt_1_C1_sqr / C1; - CT const t02 = sqrt_1_C2_sqr / C2; + // vertical segments + bool const is_C1_zero = math::equals(C1, c0); + bool const is_C2_zero = math::equals(C2, c0); + if (is_C1_zero && is_C2_zero) + { + return false; + } - CT const asin_t1_t01 = asin(tan_beta1 / t01); - CT const asin_t2_t02 = asin(tan_beta2 / t02); - CT const t01_t02 = t01 * t02; - CT const t01_t02_2 = c2 * t01_t02; - CT const sqr_t01_sqr_t02 = math::sqr(t01) + math::sqr(t02); + CT t01 = 0; + CT t02 = 0; + CT asin_t1_t01 = 0; + CT asin_t2_t02 = 0; + + if (! is_C1_zero) + { + t01 = sqrt_1_C1_sqr / C1; + asin_t1_t01 = asin(tan_beta1 / t01); + } + + if (! is_C2_zero) + { + t02 = sqrt_1_C2_sqr / C2; + asin_t2_t02 = asin(tan_beta2 / t02); + } + + // (lon1 - lon2) normalized to (-180, 180] + CT const dlon = math::longitude_distance_signed(lon2, lon1); CT t = tan_beta1; - int t_id = 0; - - // find the initial t using simplified spherical solution - // though not entirely since the reduced latitudes and azimuths are spheroidal - // [Sjoberg07] - CT const k_base = lon1 - lon2 + asin_t2_t02 - asin_t1_t01; + int t_id = -1; + CT k_base = dlon; + if (is_C1_zero) { + k_base = dlon + asin_t2_t02; + t = sin(k_base) * t02; + t_id = 0; + } + else if (is_C2_zero) + { + k_base = dlon - asin_t1_t01; + t = sin(-k_base) * t01; + t_id = 0; + } + else + { + // find the initial t using simplified spherical solution + // though not entirely since the reduced latitudes and azimuths are spheroidal + // [Sjoberg07] + k_base = dlon + asin_t2_t02 - asin_t1_t01; + CT const K = sin(k_base); + + CT const t01_t02 = t01 * t02; + CT const t01_t02_2 = c2 * t01_t02; + CT const sqr_t01_sqr_t02 = math::sqr(t01) + math::sqr(t02); + CT const d1 = sqr_t01_sqr_t02; //CT const d2 = t01_t02_2 * math::sqrt(c1 - math::sqr(K)); CT const d2 = t01_t02_2 * cos(k_base); - CT const D1 = math::sqrt(d1 - d2); - CT const D2 = math::sqrt(d1 + d2); CT const K_t01_t02 = K * t01_t02; - CT const T1 = K_t01_t02 / D1; - CT const T2 = K_t01_t02 / D2; - CT asin_T1_t01 = 0; - CT asin_T1_t02 = 0; - CT asin_T2_t01 = 0; - CT asin_T2_t02 = 0; - // test 4 possible results CT l1 = 0, l2 = 0, dl = 0; - bool found = check_t<0>( T1, - lon1, asin_T1_t01 = asin(T1 / t01), asin_t1_t01, - lon2, asin_T1_t02 = asin(T1 / t02), asin_t2_t02, - t, l1, l2, dl, t_id) - || check_t<1>(-T1, - lon1, -asin_T1_t01 , asin_t1_t01, - lon2, -asin_T1_t02 , asin_t2_t02, - t, l1, l2, dl, t_id) - || check_t<2>( T2, - lon1, asin_T2_t01 = asin(T2 / t01), asin_t1_t01, - lon2, asin_T2_t02 = asin(T2 / t02), asin_t2_t02, - t, l1, l2, dl, t_id) - || check_t<3>(-T2, - lon1, -asin_T2_t01 , asin_t1_t01, - lon2, -asin_T2_t02 , asin_t2_t02, - t, l1, l2, dl, t_id); - boost::ignore_unused(found); + CT const d1_minus_d2 = d1 - d2; + if (d1_minus_d2 > c0) + { + CT const D1 = math::sqrt(d1_minus_d2); + if (! math::equals(D1, c0)) + { + CT const T1 = K_t01_t02 / D1; + CT const asin_T1_t01 = asin(T1 / t01); + CT const asin_T1_t02 = asin(T1 / t02); + + check_t<0>(T1, + lon1, asin_T1_t01, asin_t1_t01, + lon2, asin_T1_t02, asin_t2_t02, + t, l1, l2, dl, t_id); + check_t<1>(-T1, + lon1, -asin_T1_t01, asin_t1_t01, + lon2, -asin_T1_t02, asin_t2_t02, + t, l1, l2, dl, t_id); + } + } + + CT const d1_plus_d2 = d1 + d2; + if (d1_plus_d2 > c0) + { + CT const D2 = math::sqrt(d1 + d2); + if (! math::equals(D2, c0)) + { + CT const T2 = K_t01_t02 / D2; + CT const asin_T2_t01 = asin(T2 / t01); + CT const asin_T2_t02 = asin(T2 / t02); + + check_t<2>(T2, + lon1, asin_T2_t01, asin_t1_t01, + lon2, asin_T2_t02, asin_t2_t02, + t, l1, l2, dl, t_id); + check_t<3>(-T2, + lon1, -asin_T2_t01, asin_t1_t01, + lon2, -asin_T2_t02, asin_t2_t02, + t, l1, l2, dl, t_id); + } + } } - - // [Sjoberg07] - //int const d2_sign = t_id < 2 ? -1 : 1; - int const t_sign = (t_id % 2) ? -1 : 1; - // [Sjoberg02] + + if (t_id < 0) + { + return false; + } + CT const C1_sqr = math::sqr(C1); CT const C2_sqr = math::sqr(C2); CT beta = atan(t); - CT dL1 = 0, dL2 = 0; CT asin_t_t01 = 0; CT asin_t_t02 = 0; + CT dL1 = 0; + CT dL2 = 0; + CT const t_orig = t; + CT const beta_orig = beta; + CT dbeta_abs_first = 0; + int dbeta_sign = 1; + + // [Sjoberg02] converges faster than solution in [Sjoberg07] + // Newton–Raphson method for (int i = 0; i < 10; ++i) { CT const sin_beta = sin(beta); - - // integrals approximation - dL1 = d_lambda_e_sqr(sin_beta1, sin_beta, C1, sqrt_1_C1_sqr, e_sqr); - dL2 = d_lambda_e_sqr(sin_beta2, sin_beta, C2, sqrt_1_C2_sqr, e_sqr); - - // [Sjoberg07] - /*CT const k = k_base + dL1 - dL2; - CT const K = sin(k); - CT const d1 = sqr_t01_sqr_t02; - //CT const d2 = t01_t02_2 * math::sqrt(c1 - math::sqr(K)); - CT const d2 = t01_t02_2 * cos(k); - CT const D = math::sqrt(d1 + d2_sign * d2); - CT const t_new = t_sign * K * t01_t02 / D; - CT const dt = math::abs(t_new - t); - t = t_new; - CT const new_beta = atan(t); - CT const dbeta = math::abs(new_beta - beta); - beta = new_beta;*/ - - // [Sjoberg02] - it converges faster - // Newton–Raphson method - asin_t_t01 = asin(t / t01); - asin_t_t02 = asin(t / t02); - CT const R1 = asin_t_t01 + dL1; - CT const R2 = asin_t_t02 + dL2; CT const cos_beta = cos(beta); CT const cos_beta_sqr = math::sqr(cos_beta); CT const G = c1 - e_sqr * cos_beta_sqr; - CT const f1 = C1 / cos_beta * math::sqrt(G / (cos_beta_sqr - C1_sqr)); - CT const f2 = C2 / cos_beta * math::sqrt(G / (cos_beta_sqr - C2_sqr)); - CT const abs_f1 = math::abs(f1); - CT const abs_f2 = math::abs(f2); - CT const dbeta = t_sign * (k_base - R2 + R1) / (abs_f1 + abs_f2); - + + CT R1 = 0; + CT R2 = 0; + CT f1 = 0; + CT f2 = 0; + + if (! is_C1_zero) + { + CT const t_t01 = t / t01; + + // If the argument of asin() is not in [-1, 1] it may indicate that + // the result isn't converging so change the sign and try again + if (t_t01 < -c1 || t_t01 > c1) + { + if (dbeta_sign == 1) + { + reset_loop(beta, t, dbeta_sign, i, beta_orig, t_orig); + continue; + } + else + { + return false; + } + } + + asin_t_t01 = asin(t_t01); + dL1 = d_lambda_e_sqr(sin_beta1, sin_beta, C1, sqrt_1_C1_sqr, e_sqr); + R1 = asin_t_t01 + dL1; + f1 = C1 / cos_beta * math::sqrt(G / (cos_beta_sqr - C1_sqr)); + } + + if (! is_C2_zero) + { + CT const t_t02 = t / t02; + + // If the argument of asin() is not in [-1, 1] it may indicate that + // the result isn't converging so change the sign and try again + if (t_t02 < -c1 || t_t02 > c1) + { + if (dbeta_sign == 1) + { + reset_loop(beta, t, dbeta_sign, i, beta_orig, t_orig); + continue; + } + else + { + return false; + } + } + + asin_t_t02 = asin(t_t02); + dL2 = d_lambda_e_sqr(sin_beta2, sin_beta, C2, sqrt_1_C2_sqr, e_sqr); + R2 = asin_t_t02 + dL2; + f2 = C2 / cos_beta * math::sqrt(G / (cos_beta_sqr - C2_sqr)); + } + + // The sign of dbeta is changed WRT [Sjoberg02] + CT const dbeta = dbeta_sign * (k_base - R2 + R1) / (f1 - f2); + if (math::equals(dbeta, CT(0))) { break; } + // check if the result is converging properly, otherwise change the sign + if (i == 0) + { + dbeta_abs_first = math::abs(dbeta); + } + else if (i == 1 && math::abs(dbeta) > dbeta_abs_first) + { + if (dbeta_sign == 1) + { + reset_loop(beta, t, dbeta_sign, i, beta_orig, t_orig); + continue; + } + else + { + return false; + } + } + + // Because the sign of dbeta is changed WRT [Sjoberg02] dbeta is subtracted here beta = beta - dbeta; + t = tan(beta); } @@ -355,28 +463,36 @@ private: static inline bool check_t(CT const& t, CT const& lon_a1, CT const& asin_t_t01, CT const& asin_t1_t01, CT const& lon_b1, CT const& asin_t_t02, CT const& asin_t2_t02, - CT & current_t, CT & current_lon1, CT & current_lon2, CT & current_dlon, - int & t_id) + CT & choosen_t, CT & choosen_lon1, CT & choosen_lon2, CT & choosen_dlon, + int & t_id) // in/out { CT const lon1 = lon_a1 + asin_t_t01 - asin_t1_t01; CT const lon2 = lon_b1 + asin_t_t02 - asin_t2_t02; - // TODO - true angle difference - CT const dlon = math::abs(lon2 - lon1); + CT const dlon = math::abs(math::longitude_distance_signed(lon2, lon1)); bool are_equal = math::equals(dlon, CT(0)); - if ((TId == 0) || are_equal || dlon < current_dlon) + if (t_id < 0 || are_equal || dlon < choosen_dlon) { - current_t = t; - current_lon1 = lon1; - current_lon2 = lon2; - current_dlon = dlon; + choosen_t = t; + choosen_lon1 = lon1; + choosen_lon2 = lon2; + choosen_dlon = dlon; t_id = TId; } return are_equal; } + + template + static inline void reset_loop(CT & beta, CT & t, int & dbeta_sign, int & i, CT const& beta_orig, CT const& t_orig) + { + dbeta_sign = -1; + beta = beta_orig; + t = t_orig; + i = 0; + } }; }}} // namespace boost::geometry::formula From 8e1bafb0b9dd293dab5a86a5ebb4b4cc125d87ba Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 30 Sep 2016 15:31:13 +0200 Subject: [PATCH 19/59] [formulas] Move spherical azimuth and azimuth side code to formulas. --- .../geometry/algorithms/detail/azimuth.hpp | 20 +--- include/boost/geometry/formulas/spherical.hpp | 109 ++++++++++++++++-- .../strategies/geographic/side_detail.hpp | 37 +----- 3 files changed, 104 insertions(+), 62 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/azimuth.hpp b/include/boost/geometry/algorithms/detail/azimuth.hpp index 7e0d1691e..d6fae32af 100644 --- a/include/boost/geometry/algorithms/detail/azimuth.hpp +++ b/include/boost/geometry/algorithms/detail/azimuth.hpp @@ -23,6 +23,7 @@ #include +#include #include namespace boost { namespace geometry @@ -69,22 +70,9 @@ struct azimuth template static inline ReturnType apply(P1 const& p1, P2 const& p2, Sphere const& /*unused*/) { - // http://williams.best.vwh.net/avform.htm#Crs - ReturnType dlon = get_as_radian<0>(p2) - get_as_radian<0>(p1); - ReturnType cos_p2lat = cos(get_as_radian<1>(p2)); - - // An optimization which should kick in often for Boxes - //if ( math::equals(dlon, ReturnType(0)) ) - //if ( get<0>(p1) == get<0>(p2) ) - //{ - // return - sin(get_as_radian<1>(p1)) * cos_p2lat); - //} - - // "An alternative formula, not requiring the pre-computation of d" - // In the formula below dlon is used as "d" - return atan2(sin(dlon) * cos_p2lat, - cos(get_as_radian<1>(p1)) * sin(get_as_radian<1>(p2)) - - sin(get_as_radian<1>(p1)) * cos_p2lat * cos(dlon)); + return geometry::formula::sph_azimuth( + get_as_radian<0>(p1), get_as_radian<1>(p1), + get_as_radian<0>(p2), get_as_radian<1>(p2)); } template diff --git a/include/boost/geometry/formulas/spherical.hpp b/include/boost/geometry/formulas/spherical.hpp index 2195bbbe1..f73683662 100644 --- a/include/boost/geometry/formulas/spherical.hpp +++ b/include/boost/geometry/formulas/spherical.hpp @@ -27,38 +27,55 @@ namespace boost { namespace geometry { namespace formula { +template +static inline void sph_to_cart3d(T const& lon, T const& lat, T & x, T & y, T & z) +{ + T const cos_lat = cos(lat); + x = cos_lat * cos(lon); + y = cos_lat * sin(lon); + z = sin(lat); +} + template static inline Point3d sph_to_cart3d(PointSph const& point_sph) { typedef typename coordinate_type::type calc_t; + calc_t const lon = get_as_radian<0>(point_sph); + calc_t const lat = get_as_radian<1>(point_sph); + calc_t x, y, z; + sph_to_cart3d(lon, lat, x, y, z); + Point3d res; - - calc_t lon = get_as_radian<0>(point_sph); - calc_t lat = get_as_radian<1>(point_sph); - - calc_t const cos_lat = cos(lat); - set<0>(res, cos_lat * cos(lon)); - set<1>(res, cos_lat * sin(lon)); - set<2>(res, sin(lat)); + set<0>(res, x); + set<1>(res, y); + set<2>(res, z); return res; } +template +static inline void cart3d_to_sph(T const& x, T const& y, T const& z, T & lon, T & lat) +{ + lon = atan2(y, x); + lat = asin(z); +} + template static inline PointSph cart3d_to_sph(Point3d const& point_3d) { typedef typename coordinate_type::type coord_t; typedef typename coordinate_type::type calc_t; - PointSph res; - calc_t const x = get<0>(point_3d); calc_t const y = get<1>(point_3d); calc_t const z = get<2>(point_3d); + calc_t lonr, latr; + cart3d_to_sph(x, y, z, lonr, latr); - set_from_radian<0>(res, atan2(y, x)); - set_from_radian<1>(res, asin(z)); + PointSph res; + set_from_radian<0>(res, lonr); + set_from_radian<1>(res, latr); coord_t lon = get<0>(res); coord_t lat = get<1>(res); @@ -89,6 +106,74 @@ static inline int sph_side_value(Point3d1 const& norm, Point3d2 const& pt) : -1; // d < 0 } +template +inline ReturnType sph_azimuth(T1 const& lon1, T1 const& lat1, + T2 const& lon2, T2 const& lat2) +{ + // http://williams.best.vwh.net/avform.htm#Crs + ReturnType const dlon = lon2 - lon1; + + // An optimization which should kick in often for Boxes + //if ( math::equals(dlon, ReturnType(0)) ) + //if ( get<0>(p1) == get<0>(p2) ) + //{ + // return - sin(get_as_radian<1>(p1)) * cos_p2lat); + //} + + // "An alternative formula, not requiring the pre-computation of d" + // In the formula below dlon is used as "d" + ReturnType const cos_lat2 = cos(lat2); + ReturnType const y = sin(dlon) * cos_lat2; + ReturnType const x = cos(lat1) * sin(lat2) - sin(lat1) * cos_lat2 * cos(dlon); + return atan2(y, x); +} + +template +inline T sph_azimuth(T const& lon1, T const& lat1, T const& lon2, T const& lat2) +{ + return sph_azimuth(lon1, lat1, lon2, lat2); +} + +template +inline int azimuth_side_value(T const& azi_a1_p, T const& azi_a1_a2) +{ + T const pi = math::pi(); + T const two_pi = math::two_pi(); + + // instead of the formula from XTD + //calc_t a_diff = asin(sin(azi_a1_p - azi_a1_a2)); + + T a_diff = azi_a1_p - azi_a1_a2; + // normalize, angle in [-pi, pi] + while (a_diff > pi) + a_diff -= two_pi; + while (a_diff < -pi) + a_diff += two_pi; + + // NOTE: in general it shouldn't be required to support the pi/-pi case + // because in non-cartesian systems it makes sense to check the side + // only "between" the endpoints. + // However currently the winding strategy calls the side strategy + // for vertical segments to check if the point is "between the endpoints. + // This could be avoided since the side strategy is not required for that + // because meridian is the shortest path. So a difference of + // longitudes would be sufficient (of course normalized to [-pi, pi]). + + // NOTE: with the above said, the pi/-pi check is temporary + // however in case if this was required + // the geodesics on ellipsoid aren't "symmetrical" + // therefore instead of comparing a_diff to pi and -pi + // one should probably use inverse azimuths and compare + // the difference to 0 as well + + // positive azimuth is on the right side + return math::equals(a_diff, 0) + || math::equals(a_diff, pi) + || math::equals(a_diff, -pi) ? 0 + : a_diff > 0 ? -1 // right + : 1; // left +} + } // namespace formula }} // namespace boost::geometry diff --git a/include/boost/geometry/strategies/geographic/side_detail.hpp b/include/boost/geometry/strategies/geographic/side_detail.hpp index ce1b47c88..ece87cfc2 100644 --- a/include/boost/geometry/strategies/geographic/side_detail.hpp +++ b/include/boost/geometry/strategies/geographic/side_detail.hpp @@ -19,6 +19,8 @@ #include #include +#include + #include #include #include @@ -73,40 +75,7 @@ public: calc_t a1p = azimuth(p1, p, m_model); calc_t a12 = azimuth(p1, p2, m_model); - calc_t const pi = math::pi(); - - // instead of the formula from XTD - //calc_t a_diff = asin(sin(a1p - a12)); - - calc_t a_diff = a1p - a12; - // normalize, angle in [-pi, pi] - while ( a_diff > pi ) - a_diff -= calc_t(2) * pi; - while ( a_diff < -pi ) - a_diff += calc_t(2) * pi; - - // NOTE: in general it shouldn't be required to support the pi/-pi case - // because in non-cartesian systems it makes sense to check the side - // only "between" the endpoints. - // However currently the winding strategy calls the side strategy - // for vertical segments to check if the point is "between the endpoints. - // This could be avoided since the side strategy is not required for that - // because meridian is the shortest path. So a difference of - // longitudes would be sufficient (of course normalized to [-pi, pi]). - - // NOTE: with the above said, the pi/-pi check is temporary - // however in case if this was required - // the geodesics on ellipsoid aren't "symmetrical" - // therefore instead of comparing a_diff to pi and -pi - // one should probably use inverse azimuths and compare - // the difference to 0 as well - - // positive azimuth is on the right side - return math::equals(a_diff, 0) - || math::equals(a_diff, pi) - || math::equals(a_diff, -pi) ? 0 - : a_diff > 0 ? -1 // right - : 1; // left + return formula::azimuth_side_value(a1p, a12); } private: From 98d755614c900129494d8c996365238a59c7aaf1 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 30 Sep 2016 15:32:48 +0200 Subject: [PATCH 20/59] [util] Add longitude_distance_directed() function calculating Lon/LonInterval distance. --- .../util/normalize_spheroidal_coordinates.hpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp index 377051eef..c6d9888d2 100644 --- a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp +++ b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp @@ -282,6 +282,25 @@ inline CoordinateType longitude_distance_unsigned(CoordinateType const& longitud return diff; } +template +inline CoordinateType longitude_distance_directed(CoordinateType const& longitude_a1, + CoordinateType const& longitude_a2, + CoordinateType const& longitude_b) +{ + CoordinateType const c0 = 0; + CoordinateType dist_a12 = longitude_distance_signed(longitude_a1, longitude_a2); + CoordinateType dist_a1b = longitude_distance_signed(longitude_a1, longitude_b); + if (dist_a12 < c0) + { + dist_a12 = -dist_a12; + dist_a1b = -dist_a1b; + } + + return dist_a1b < c0 ? dist_a1b + : dist_a1b > dist_a12 ? dist_a1b - dist_a12 + : c0; +} + } // namespace math From fadd77ff7ab4a1c970ed484721f7ecb24634cc32 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 30 Sep 2016 15:34:31 +0200 Subject: [PATCH 21/59] [formulas] Improve robustness of sjoberg_intersection formula. Handle special cases like vertical segments or intersection points behind the geodesic vertex. Calculate starting point of approximation with Sjoberg02 spherical intersection formula. First try to use Sjoberg02 Newton's method, if it's unable to calculate the result use more robust Sjoberg07 method. Add the sjoberg_geodesic representation and enclose geodesic formulas inside. Use this representation consistently in various methods in sjoberg_intersection. --- .../formulas/sjoberg_intersection.hpp | 1359 +++++++++++++---- 1 file changed, 1026 insertions(+), 333 deletions(-) diff --git a/include/boost/geometry/formulas/sjoberg_intersection.hpp b/include/boost/geometry/formulas/sjoberg_intersection.hpp index 795300307..745c2968d 100644 --- a/include/boost/geometry/formulas/sjoberg_intersection.hpp +++ b/include/boost/geometry/formulas/sjoberg_intersection.hpp @@ -21,18 +21,605 @@ #include #include +#include namespace boost { namespace geometry { namespace formula { +/*! +\brief The intersection of two great circles as proposed by Sjoberg. +\see See + - [Sjoberg02] Lars E. Sjoberg, Intersections on the sphere and ellipsoid, 2002 + http://link.springer.com/article/10.1007/s00190-001-0230-9 +*/ +template +struct sjoberg_intersection_spherical_02 +{ + // TODO: if it will be used as standalone formula + // support segments on equator and endpoints on poles + + static inline bool apply(CT const& lon1, CT const& lat1, CT const& lon_a2, CT const& lat_a2, + CT const& lon2, CT const& lat2, CT const& lon_b2, CT const& lat_b2, + CT & lon, CT & lat) + { + CT tan_lat = 0; + bool res = apply_alt(lon1, lat1, lon_a2, lat_a2, + lon2, lat2, lon_b2, lat_b2, + lon, tan_lat); + + if (res) + { + lat = atan(tan_lat); + } + + return res; + } + + static inline bool apply_alt(CT const& lon1, CT const& lat1, CT const& lon_a2, CT const& lat_a2, + CT const& lon2, CT const& lat2, CT const& lon_b2, CT const& lat_b2, + CT & lon, CT & tan_lat) + { + CT const cos_lon1 = cos(lon1); + CT const sin_lon1 = sin(lon1); + CT const cos_lon2 = cos(lon2); + CT const sin_lon2 = sin(lon2); + CT const sin_lat1 = sin(lat1); + CT const sin_lat2 = sin(lat2); + CT const cos_lat1 = cos(lat1); + CT const cos_lat2 = cos(lat2); + + CT const tan_lat_a2 = tan(lat_a2); + CT const tan_lat_b2 = tan(lat_b2); + + return apply(lon1, lon_a2, lon2, lon_b2, + sin_lon1, cos_lon1, sin_lat1, cos_lat1, + sin_lon2, cos_lon2, sin_lat2, cos_lat2, + tan_lat_a2, tan_lat_b2, + lon, tan_lat); + } + +private: + static inline bool apply(CT const& lon1, CT const& lon_a2, CT const& lon2, CT const& lon_b2, + CT const& sin_lon1, CT const& cos_lon1, CT const& sin_lat1, CT const& cos_lat1, + CT const& sin_lon2, CT const& cos_lon2, CT const& sin_lat2, CT const& cos_lat2, + CT const& tan_lat_a2, CT const& tan_lat_b2, + CT & lon, CT & tan_lat) + { + // NOTE: + // cos_lat_ = 0 <=> segment on equator + // tan_alpha_ = 0 <=> segment vertical + + CT const tan_lat1 = sin_lat1 / cos_lat1; //tan(lat1); + CT const tan_lat2 = sin_lat2 / cos_lat2; //tan(lat2); + + CT const dlon1 = lon_a2 - lon1; + CT const sin_dlon1 = sin(dlon1); + CT const dlon2 = lon_b2 - lon2; + CT const sin_dlon2 = sin(dlon2); + + CT const c0 = 0; + bool const is_vertical1 = math::equals(sin_dlon1, c0); + bool const is_vertical2 = math::equals(sin_dlon2, c0); + + CT tan_alpha1 = 0; + CT tan_alpha2 = 0; + + if (is_vertical1 && is_vertical2) + { + // circles intersect at one of the poles or are collinear + return false; + } + else if (is_vertical1) + { + CT const cos_dlon2 = cos(dlon2); + CT const tan_alpha2_x = cos_lat2 * tan_lat_b2 - sin_lat2 * cos_dlon2; + tan_alpha2 = sin_dlon2 / tan_alpha2_x; + + lon = lon1; + } + else if (is_vertical2) + { + CT const cos_dlon1 = cos(dlon1); + CT const tan_alpha1_x = cos_lat1 * tan_lat_a2 - sin_lat1 * cos_dlon1; + tan_alpha1 = sin_dlon1 / tan_alpha1_x; + + lon = lon2; + } + else + { + CT const cos_dlon1 = cos(dlon1); + CT const cos_dlon2 = cos(dlon2); + + CT const tan_alpha1_x = cos_lat1 * tan_lat_a2 - sin_lat1 * cos_dlon1; + CT const tan_alpha2_x = cos_lat2 * tan_lat_b2 - sin_lat2 * cos_dlon2; + tan_alpha1 = sin_dlon1 / tan_alpha1_x; + tan_alpha2 = sin_dlon2 / tan_alpha2_x; + + CT const T1 = tan_alpha1 * cos_lat1; + CT const T2 = tan_alpha2 * cos_lat2; + CT const T1T2 = T1*T2; + CT const tan_lon_y = T1 * sin_lon2 - T2 * sin_lon1 + T1T2 * (tan_lat1 * cos_lon1 - tan_lat2 * cos_lon2); + CT const tan_lon_x = T1 * cos_lon2 - T2 * cos_lon1 - T1T2 * (tan_lat1 * sin_lon1 - tan_lat2 * sin_lon2); + + lon = atan2(tan_lon_y, tan_lon_x); + } + + // choose closer result + CT const pi = math::pi(); + CT const lon_2 = lon > c0 ? lon - pi : lon + pi; + CT const lon_dist1 = (std::max)((std::min)(math::longitude_difference(lon1, lon), + math::longitude_difference(lon_a2, lon)), + (std::min)(math::longitude_difference(lon2, lon), + math::longitude_difference(lon_b2, lon))); + CT const lon_dist2 = (std::max)((std::min)(math::longitude_difference(lon1, lon_2), + math::longitude_difference(lon_a2, lon_2)), + (std::min)(math::longitude_difference(lon2, lon_2), + math::longitude_difference(lon_b2, lon_2))); + if (lon_dist2 < lon_dist1) + { + lon = lon_2; + } + + CT const sin_lon = sin(lon); + CT const cos_lon = cos(lon); + + if (math::abs(tan_alpha1) >= math::abs(tan_alpha2)) // pick less vertical segment + { + CT const sin_dlon_1 = sin_lon * cos_lon1 - cos_lon * sin_lon1; + CT const cos_dlon_1 = cos_lon * cos_lon1 + sin_lon * sin_lon1; + CT const lat_y_1 = sin_dlon_1 + tan_alpha1 * sin_lat1 * cos_dlon_1; + CT const lat_x_1 = tan_alpha1 * cos_lat1; + tan_lat = lat_y_1 / lat_x_1; + } + else + { + CT const sin_dlon_2 = sin_lon * cos_lon2 - cos_lon * sin_lon2; + CT const cos_dlon_2 = cos_lon * cos_lon2 + sin_lon * sin_lon2; + CT const lat_y_2 = sin_dlon_2 + tan_alpha2 * sin_lat2 * cos_dlon_2; + CT const lat_x_2 = tan_alpha2 * cos_lat2; + tan_lat = lat_y_2 / lat_x_2; + } + + return true; + } +}; + + +/*! Approximation of dLambda_j [Sjoberg07], expanded into taylor series in e^2 + Maxima script: + dLI_j(c_j, sinB_j, sinB) := integrate(1 / (sqrt(1 - c_j ^ 2 - x ^ 2)*(1 + sqrt(1 - e2*(1 - x ^ 2)))), x, sinB_j, sinB); + dL_j(c_j, B_j, B) := -e2 * c_j * dLI_j(c_j, B_j, B); + S: taylor(dLI_j(c_j, sinB_j, sinB), e2, 0, 3); + assume(c_j < 1); + assume(c_j > 0); + L1: factor(integrate(sqrt(-x ^ 2 - c_j ^ 2 + 1) / (x ^ 2 + c_j ^ 2 - 1), x)); + L2: factor(integrate(((x ^ 2 - 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x)); + L3: factor(integrate(((x ^ 4 - 2 * x ^ 2 + 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x)); + L4: factor(integrate(((x ^ 6 - 3 * x ^ 4 + 3 * x ^ 2 - 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x)); + +\see See + - [Sjoberg07] Lars E. Sjoberg, Geodetic intersection on the ellipsoid, 2007 + http://link.springer.com/article/10.1007/s00190-007-0204-7 +*/ +template +inline CT sjoberg_d_lambda_e_sqr(CT const& sin_betaj, CT const& sin_beta, + CT const& Cj, CT const& sqrt_1_Cj_sqr, + CT const& e_sqr) +{ + using math::detail::bounded; + + if (Order == 0) + { + return 0; + } + + CT const c1 = 1; + CT const c2 = 2; + + CT const asin_B = asin(bounded(sin_beta / sqrt_1_Cj_sqr, -c1, c1)); + CT const asin_Bj = asin(sin_betaj / sqrt_1_Cj_sqr); + CT const L0 = (asin_B - asin_Bj) / c2; + + if (Order == 1) + { + return -Cj * e_sqr * L0; + } + + CT const c0 = 0; + CT const c16 = 16; + + CT const X = sin_beta; + CT const Xj = sin_betaj; + CT const X_sqr = math::sqr(X); + CT const Xj_sqr = math::sqr(Xj); + CT const Cj_sqr = math::sqr(Cj); + CT const Cj_sqr_plus_one = Cj_sqr + c1; + CT const one_minus_Cj_sqr = c1 - Cj_sqr; + CT const sqrt_Y = math::sqrt(bounded(-X_sqr + one_minus_Cj_sqr, c0)); + CT const sqrt_Yj = math::sqrt(-Xj_sqr + one_minus_Cj_sqr); + CT const L1 = (Cj_sqr_plus_one * (asin_B - asin_Bj) + X * sqrt_Y - Xj * sqrt_Yj) / c16; + + if (Order == 2) + { + return -Cj * e_sqr * (L0 + e_sqr * L1); + } + + CT const c3 = 3; + CT const c5 = 5; + CT const c128 = 128; + + CT const E = Cj_sqr * (c3 * Cj_sqr + c2) + c3; + CT const F = X * (-c2 * X_sqr + c3 * Cj_sqr + c5); + CT const Fj = Xj * (-c2 * Xj_sqr + c3 * Cj_sqr + c5); + CT const L2 = (E * (asin_B - asin_Bj) + F * sqrt_Y - Fj * sqrt_Yj) / c128; + + if (Order == 3) + { + return -Cj * e_sqr * (L0 + e_sqr * (L1 + e_sqr * L2)); + } + + CT const c8 = 8; + CT const c9 = 9; + CT const c10 = 10; + CT const c15 = 15; + CT const c24 = 24; + CT const c26 = 26; + CT const c33 = 33; + CT const c6144 = 6144; + + CT const G = Cj_sqr * (Cj_sqr * (Cj_sqr * c15 + c9) + c9) + c15; + CT const H = -c10 * Cj_sqr - c26; + CT const I = Cj_sqr * (Cj_sqr * c15 + c24) + c33; + CT const J = X_sqr * (X * (c8 * X_sqr + H)) + X * I; + CT const Jj = Xj_sqr * (Xj * (c8 * Xj_sqr + H)) + Xj * I; + CT const L3 = (G * (asin_B - asin_Bj) + J * sqrt_Y - Jj * sqrt_Yj) / c6144; + + // Order 4 and higher + return -Cj * e_sqr * (L0 + e_sqr * (L1 + e_sqr * (L2 + e_sqr * L3))); +} + +/*! +\brief The representation of geodesic as proposed by Sjoberg. +\see See + - [Sjoberg07] Lars E. Sjoberg, Geodetic intersection on the ellipsoid, 2007 + http://link.springer.com/article/10.1007/s00190-007-0204-7 + - [Sjoberg12] Lars E. Sjoberg, Solutions to the ellipsoidal Clairaut constant + and the inverse geodetic problem by numerical integration, 2012 + https://www.degruyter.com/view/j/jogs.2012.2.issue-3/v10156-011-0037-4/v10156-011-0037-4.xml +*/ +template +class sjoberg_geodesic +{ + sjoberg_geodesic() {} + + static int sign_C(CT const& alphaj) + { + CT const c0 = 0; + CT const c2 = 2; + CT const pi = math::pi(); + CT const pi_half = pi / c2; + + return (pi_half < alphaj && alphaj < pi) || (-pi_half < alphaj && alphaj < c0) ? -1 : 1; + } + +public: + sjoberg_geodesic(CT const& lon, CT const& lat, CT const& alpha, CT const& f) + : lonj(lon) + , latj(lat) + , alphaj(alpha) + { + CT const c0 = 0; + CT const c1 = 1; + CT const c2 = 2; + //CT const pi = math::pi(); + //CT const pi_half = pi / c2; + + one_minus_f = c1 - f; + e_sqr = f * (c2 - f); + + tan_latj = tan(lat); + tan_betaj = one_minus_f * tan_latj; + betaj = atan(tan_betaj); + sin_betaj = sin(betaj); + + cos_betaj = cos(betaj); + sin_alphaj = sin(alphaj); + // Clairaut constant (lower-case in the paper) + Cj = sign_C(alphaj) * cos_betaj * sin_alphaj; + Cj_sqr = math::sqr(Cj); + sqrt_1_Cj_sqr = math::sqrt(c1 - Cj_sqr); + + sign_lon_diff = alphaj >= 0 ? 1 : -1; // || alphaj == -pi ? + //sign_lon_diff = 1; + + is_on_equator = math::equals(sqrt_1_Cj_sqr, c0); + is_Cj_zero = math::equals(Cj, c0); + + t0j = c0; + asin_tj_t0j = c0; + + if (! is_Cj_zero) + { + t0j = sqrt_1_Cj_sqr / Cj; + } + + if (! is_on_equator) + { + //asin_tj_t0j = asin(tan_betaj / t0j); + asin_tj_t0j = asin(tan_betaj * Cj / sqrt_1_Cj_sqr); + } + } + + struct vertex_data + { + //CT beta0j; + CT sin_beta0j; + CT dL0j; + CT lon0j; + }; + + vertex_data get_vertex_data() const + { + CT const c2 = 2; + CT const pi = math::pi(); + CT const pi_half = pi / c2; + + vertex_data res; + + if (! is_Cj_zero) + { + //res.beta0j = atan(t0j); + //res.sin_beta0j = sin(res.beta0j); + res.sin_beta0j = math::sign(t0j) * sqrt_1_Cj_sqr; + res.dL0j = d_lambda(res.sin_beta0j); + res.lon0j = lonj + sign_lon_diff * (pi_half - asin_tj_t0j + res.dL0j); + } + else + { + //res.beta0j = pi_half; + //res.sin_beta0j = betaj >= 0 ? 1 : -1; + res.sin_beta0j = 1; + res.dL0j = 0; + res.lon0j = lonj; + } + + return res; + } + + bool is_sin_beta_ok(CT const& sin_beta) const + { + CT const c1 = 1; + return math::abs(sin_beta / sqrt_1_Cj_sqr) <= c1; + } + + bool k_diff(CT const& sin_beta, + CT & delta_k) const + { + if (is_Cj_zero) + { + delta_k = 0; + return true; + } + + // beta out of bounds and not close + if (! (is_sin_beta_ok(sin_beta) + || math::equals(math::abs(sin_beta), sqrt_1_Cj_sqr)) ) + { + return false; + } + + // NOTE: beta may be slightly out of bounds here but d_lambda handles that + CT const dLj = d_lambda(sin_beta); + delta_k = sign_lon_diff * (/*asin_t_t0j*/ - asin_tj_t0j + dLj); + + return true; + } + + bool lon_diff(CT const& sin_beta, CT const& t, + CT & delta_lon) const + { + using math::detail::bounded; + CT const c1 = 1; + + if (is_Cj_zero) + { + delta_lon = 0; + return true; + } + + CT delta_k = 0; + if (! k_diff(sin_beta, delta_k)) + { + return false; + } + + CT const t_t0j = t / t0j; + // NOTE: t may be slightly out of bounds here + CT const asin_t_t0j = asin(bounded(t_t0j, -c1, c1)); + delta_lon = sign_lon_diff * asin_t_t0j + delta_k; + + return true; + } + + bool k_diffs(CT const& sin_beta, vertex_data const& vd, + CT & delta_k_before, CT & delta_k_behind, + bool check_sin_beta = true) const + { + CT const pi = math::pi(); + + if (is_Cj_zero) + { + delta_k_before = 0; + delta_k_behind = sign_lon_diff * pi; + return true; + } + + // beta out of bounds and not close + if (check_sin_beta + && ! (is_sin_beta_ok(sin_beta) + || math::equals(math::abs(sin_beta), sqrt_1_Cj_sqr)) ) + { + return false; + } + + // NOTE: beta may be slightly out of bounds here but d_lambda handles that + CT const dLj = d_lambda(sin_beta); + delta_k_before = sign_lon_diff * (/*asin_t_t0j*/ - asin_tj_t0j + dLj); + + // This version require no additional dLj calculation + delta_k_behind = sign_lon_diff * (pi /*- asin_t_t0j*/ - asin_tj_t0j + vd.dL0j + (vd.dL0j - dLj)); + + // [Sjoberg12] + //CT const dL101 = d_lambda(sin_betaj, vd.sin_beta0j); + // WARNING: the following call might not work if beta was OoB because only the second argument is bounded + //CT const dL_01 = d_lambda(sin_beta, vd.sin_beta0j); + //delta_k_behind = sign_lon_diff * (pi /*- asin_t_t0j*/ - asin_tj_t0j + dL101 + dL_01); + + return true; + } + + bool lon_diffs(CT const& sin_beta, CT const& t, vertex_data const& vd, + CT & delta_lon_before, CT & delta_lon_behind) const + { + using math::detail::bounded; + CT const c1 = 1; + CT const pi = math::pi(); + + if (is_Cj_zero) + { + delta_lon_before = 0; + delta_lon_behind = sign_lon_diff * pi; + return true; + } + + CT delta_k_before = 0, delta_k_behind = 0; + if (! k_diffs(sin_beta, vd, delta_k_before, delta_k_behind)) + { + return false; + } + + CT const t_t0j = t / t0j; + // NOTE: t may be slightly out of bounds here + CT const asin_t_t0j = asin(bounded(t_t0j, -c1, c1)); + CT const sign_asin_t_t0j = sign_lon_diff * asin_t_t0j; + delta_lon_before = sign_asin_t_t0j + delta_k_before; + delta_lon_behind = -sign_asin_t_t0j + delta_k_behind; + + return true; + } + + bool lon(CT const& sin_beta, CT const& t, vertex_data const& vd, + CT & lon_before, CT & lon_behind) const + { + using math::detail::bounded; + CT const c1 = 1; + CT const pi = math::pi(); + + if (is_Cj_zero) + { + lon_before = lonj; + lon_behind = lonj + sign_lon_diff * pi; + return true; + } + + if (! (is_sin_beta_ok(sin_beta) + || math::equals(math::abs(sin_beta), sqrt_1_Cj_sqr)) ) + { + return false; + } + + CT const t_t0j = t / t0j; + CT const asin_t_t0j = asin(bounded(t_t0j, -c1, c1)); + CT const dLj = d_lambda(sin_beta); + lon_before = lonj + sign_lon_diff * (asin_t_t0j - asin_tj_t0j + dLj); + lon_behind = vd.lon0j + (vd.lon0j - lon_before); + + return true; + } + + + CT lon(CT const& delta_lon) const + { + return lonj + delta_lon; + } + + CT lat(CT const& t) const + { + // t = tan(beta) = (1-f)tan(lat) + return atan(t / one_minus_f); + } + + void vertex(CT & lon, CT & lat) const + { + lon = get_vertex_data().lon0j; + if (! is_Cj_zero) + { + lat = sjoberg_geodesic::lat(t0j); + } + else + { + CT const c2 = 2; + lat = math::pi() / c2; + } + } + + CT lon_of_equator_intersection() const + { + CT const c0 = 0; + CT const dLj = d_lambda(c0); + CT const asin_tj_t0j = asin(Cj * tan_betaj / sqrt_1_Cj_sqr); + return lonj - asin_tj_t0j + dLj; + } + + CT d_lambda(CT const& sin_beta) const + { + return sjoberg_d_lambda_e_sqr(sin_betaj, sin_beta, Cj, sqrt_1_Cj_sqr, e_sqr); + } + + // [Sjoberg12] + /*CT d_lambda(CT const& sin_beta1, CT const& sin_beta2) const + { + return sjoberg_d_lambda_e_sqr(sin_beta1, sin_beta2, Cj, sqrt_1_Cj_sqr, e_sqr); + }*/ + + CT lonj; + CT latj; + CT alphaj; + + CT one_minus_f; + CT e_sqr; + + CT tan_latj; + CT tan_betaj; + CT betaj; + CT sin_betaj; + CT cos_betaj; + CT sin_alphaj; + CT Cj; + CT Cj_sqr; + CT sqrt_1_Cj_sqr; + + int sign_lon_diff; + + bool is_on_equator; + bool is_Cj_zero; + + CT t0j; + CT asin_tj_t0j; +}; + + /*! \brief The intersection of two geodesics as proposed by Sjoberg. -\author See +\see See - [Sjoberg02] Lars E. Sjoberg, Intersections on the sphere and ellipsoid, 2002 http://link.springer.com/article/10.1007/s00190-001-0230-9 - [Sjoberg07] Lars E. Sjoberg, Geodetic intersection on the ellipsoid, 2007 http://link.springer.com/article/10.1007/s00190-007-0204-7 + - [Sjoberg12] Lars E. Sjoberg, Solutions to the ellipsoidal Clairaut constant + and the inverse geodetic problem by numerical integration, 2012 + https://www.degruyter.com/view/j/jogs.2012.2.issue-3/v10156-011-0037-4/v10156-011-0037-4.xml */ template < @@ -42,9 +629,14 @@ template > class sjoberg_intersection { + typedef sjoberg_geodesic geodesic_type; typedef Inverse inverse_type; typedef typename inverse_type::result_type inverse_result; + static bool const enable_02 = true; + static int const max_iterations_02 = 10; + static int const max_iterations_07 = 20; + public: template static inline bool apply(T1 const& lona1, T1 const& lata1, @@ -63,15 +655,17 @@ public: CT const lon_b2 = lonb2; CT const lat_b2 = latb2; - CT const alpha1 = inverse_type::apply(lon_a1, lat_a1, lon_a2, lat_a2, spheroid).azimuth; - CT const alpha2 = inverse_type::apply(lon_b1, lat_b1, lon_b2, lat_b2, spheroid).azimuth; + inverse_result const res1 = inverse_type::apply(lon_a1, lat_a1, lon_a2, lat_a2, spheroid); + inverse_result const res2 = inverse_type::apply(lon_b1, lat_b1, lon_b2, lat_b2, spheroid); - return apply(lon_a1, lat_a1, alpha1, lon_b1, lat_b1, alpha2, lon, lat, spheroid); + return apply(lon_a1, lat_a1, lon_a2, lat_a2, res1.azimuth, + lon_b1, lat_b1, lon_b2, lat_b2, res2.azimuth, + lon, lat, spheroid); } - + template - static inline bool apply(CT const& lon1, CT const& lat1, CT const& alpha1, - CT const& lon2, CT const& lat2, CT const& alpha2, + static inline bool apply(CT const& lon_a1, CT const& lat_a1, CT const& lon_a2, CT const& lat_a2, CT const& alpha_a1, + CT const& lon_b1, CT const& lat_b1, CT const& lon_b2, CT const& lat_b2, CT const& alpha_b1, CT & lon, CT & lat, Spheroid const& spheroid) { @@ -79,377 +673,498 @@ public: CT const c0 = 0; CT const c1 = 1; - CT const c2 = 2; - CT const pi = math::pi(); - CT const pi_half = pi / c2; CT const f = formula::flattening(spheroid); CT const one_minus_f = c1 - f; - CT const e_sqr = f * (c2 - f); - - CT const sin_alpha1 = sin(alpha1); - CT const sin_alpha2 = sin(alpha2); - CT const tan_beta1 = one_minus_f * tan(lat1); - CT const tan_beta2 = one_minus_f * tan(lat2); - CT const beta1 = atan(tan_beta1); - CT const beta2 = atan(tan_beta2); - CT const cos_beta1 = cos(beta1); - CT const cos_beta2 = cos(beta2); - CT const sin_beta1 = sin(beta1); - CT const sin_beta2 = sin(beta2); + geodesic_type geod1(lon_a1, lat_a1, alpha_a1, f); + geodesic_type geod2(lon_b1, lat_b1, alpha_b1, f); - // Clairaut constants (lower-case in the paper) - int const sign_C1 = math::abs(alpha1) <= pi_half ? 1 : -1; - int const sign_C2 = math::abs(alpha2) <= pi_half ? 1 : -1; - // Cj = 1 if on equator - CT const C1 = sign_C1 * cos_beta1 * sin_alpha1; - CT const C2 = sign_C2 * cos_beta2 * sin_alpha2; + // Cj = 1 if on equator <=> sqrt_1_Cj_sqr = 0 + // Cj = 0 if vertical <=> sqrt_1_Cj_sqr = 1 - CT const sqrt_1_C1_sqr = math::sqrt(c1 - math::sqr(C1)); - CT const sqrt_1_C2_sqr = math::sqrt(c1 - math::sqr(C2)); - - // handle special case: segments on the equator - bool const on_equator1 = math::equals(sqrt_1_C1_sqr, c0); - bool const on_equator2 = math::equals(sqrt_1_C2_sqr, c0); - if (on_equator1 && on_equator2) + if (geod1.is_on_equator && geod2.is_on_equator) { return false; } - else if (on_equator1) + else if (geod1.is_on_equator) { - CT const dL2 = d_lambda_e_sqr(sin_beta2, c0, C2, sqrt_1_C2_sqr, e_sqr); - CT const asin_t2_t02 = asin(C2 * tan_beta2 / sqrt_1_C2_sqr); + lon = geod2.lon_of_equator_intersection(); lat = c0; - lon = lon2 - asin_t2_t02 + dL2; return true; } - else if (on_equator2) + else if (geod2.is_on_equator) { - CT const dL1 = d_lambda_e_sqr(sin_beta1, c0, C1, sqrt_1_C1_sqr, e_sqr); - CT const asin_t1_t01 = asin(C1 * tan_beta1 / sqrt_1_C1_sqr); + lon = geod1.lon_of_equator_intersection(); lat = c0; - lon = lon1 - asin_t1_t01 + dL1; return true; } // vertical segments - bool const is_C1_zero = math::equals(C1, c0); - bool const is_C2_zero = math::equals(C2, c0); - if (is_C1_zero && is_C2_zero) + if (geod1.is_Cj_zero && geod2.is_Cj_zero) { + //TODO: the geodesics may be parallel or intersect at one of the poles return false; } - - CT t01 = 0; - CT t02 = 0; - CT asin_t1_t01 = 0; - CT asin_t2_t02 = 0; - - if (! is_C1_zero) - { - t01 = sqrt_1_C1_sqr / C1; - asin_t1_t01 = asin(tan_beta1 / t01); - } - - if (! is_C2_zero) - { - t02 = sqrt_1_C2_sqr / C2; - asin_t2_t02 = asin(tan_beta2 / t02); - } - + // (lon1 - lon2) normalized to (-180, 180] - CT const dlon = math::longitude_distance_signed(lon2, lon1); + CT const lon1_minus_lon2 = math::longitude_distance_signed(geod2.lonj, geod1.lonj); - CT t = tan_beta1; - int t_id = -1; - CT k_base = dlon; + CT lon_sph = lon; + + // Starting tan(beta) + CT t = 0; + + /*if (geod1.is_Cj_zero) + { + CT const k_base = lon1_minus_lon2 + geod2.sign_lon_diff * geod2.asin_tj_t0j; + t = sin(k_base) * geod2.t0j; + lon_sph = vertical_intersection_longitude(geod1.lonj, lon_b1, lon_b2); + } + else if (geod2.is_Cj_zero) + { + CT const k_base = lon1_minus_lon2 - geod1.sign_lon_diff * geod1.asin_tj_t0j; + t = sin(-k_base) * geod1.t0j; + lon_sph = vertical_intersection_longitude(geod2.lonj, lon_a1, lon_a2); + } + else*/ + { + // TODO: Consider using betas instead of latitudes. + // Some function calls might be saved this way. + CT tan_lat_sph = 0; + sjoberg_intersection_spherical_02::apply_alt(lon_a1, lat_a1, lon_a2, lat_a2, + lon_b1, lat_b1, lon_b2, lat_b2, + lon_sph, tan_lat_sph); + t = one_minus_f * tan_lat_sph; // tan(beta) + } + + // TODO: no need to calculate atan here if reduced latitudes were used + // instead of latitudes above, in sjoberg_intersection_spherical_02 + CT const beta = atan(t); + + if (enable_02 && newton_method(geod1, geod2, beta, t, lon1_minus_lon2, lon, lat)) + { + return true; + } + + return converge_07(geod1, geod2, beta, t, lon1_minus_lon2, lon_sph, lon, lat); + } + +private: + static inline bool newton_method(geodesic_type const& geod1, geodesic_type const& geod2, // in + CT beta, CT t, CT const& lon1_minus_lon2, // in + CT & lon, CT & lat) // out + { + CT const c0 = 0; + CT const c1 = 1; + + CT const e_sqr = geod1.e_sqr; - if (is_C1_zero) - { - k_base = dlon + asin_t2_t02; - t = sin(k_base) * t02; - t_id = 0; - } - else if (is_C2_zero) - { - k_base = dlon - asin_t1_t01; - t = sin(-k_base) * t01; - t_id = 0; - } - else - { - // find the initial t using simplified spherical solution - // though not entirely since the reduced latitudes and azimuths are spheroidal - // [Sjoberg07] - k_base = dlon + asin_t2_t02 - asin_t1_t01; - - CT const K = sin(k_base); + CT lon1_diff = 0; + CT lon2_diff = 0; - CT const t01_t02 = t01 * t02; - CT const t01_t02_2 = c2 * t01_t02; - CT const sqr_t01_sqr_t02 = math::sqr(t01) + math::sqr(t02); - - CT const d1 = sqr_t01_sqr_t02; - //CT const d2 = t01_t02_2 * math::sqrt(c1 - math::sqr(K)); - CT const d2 = t01_t02_2 * cos(k_base); - CT const K_t01_t02 = K * t01_t02; - - // test 4 possible results - CT l1 = 0, l2 = 0, dl = 0; - - CT const d1_minus_d2 = d1 - d2; - if (d1_minus_d2 > c0) - { - CT const D1 = math::sqrt(d1_minus_d2); - if (! math::equals(D1, c0)) - { - CT const T1 = K_t01_t02 / D1; - CT const asin_T1_t01 = asin(T1 / t01); - CT const asin_T1_t02 = asin(T1 / t02); - - check_t<0>(T1, - lon1, asin_T1_t01, asin_t1_t01, - lon2, asin_T1_t02, asin_t2_t02, - t, l1, l2, dl, t_id); - check_t<1>(-T1, - lon1, -asin_T1_t01, asin_t1_t01, - lon2, -asin_T1_t02, asin_t2_t02, - t, l1, l2, dl, t_id); - } - } - - CT const d1_plus_d2 = d1 + d2; - if (d1_plus_d2 > c0) - { - CT const D2 = math::sqrt(d1 + d2); - if (! math::equals(D2, c0)) - { - CT const T2 = K_t01_t02 / D2; - CT const asin_T2_t01 = asin(T2 / t01); - CT const asin_T2_t02 = asin(T2 / t02); - - check_t<2>(T2, - lon1, asin_T2_t01, asin_t1_t01, - lon2, asin_T2_t02, asin_t2_t02, - t, l1, l2, dl, t_id); - check_t<3>(-T2, - lon1, -asin_T2_t01, asin_t1_t01, - lon2, -asin_T2_t02, asin_t2_t02, - t, l1, l2, dl, t_id); - } - } - } - - if (t_id < 0) - { - return false; - } - - CT const C1_sqr = math::sqr(C1); - CT const C2_sqr = math::sqr(C2); - - CT beta = atan(t); - CT asin_t_t01 = 0; - CT asin_t_t02 = 0; - CT dL1 = 0; - CT dL2 = 0; - - CT const t_orig = t; - CT const beta_orig = beta; - CT dbeta_abs_first = 0; - int dbeta_sign = 1; + CT abs_dbeta_last = 0; // [Sjoberg02] converges faster than solution in [Sjoberg07] - // Newton–Raphson method - for (int i = 0; i < 10; ++i) + // Newton-Raphson method + for (int i = 0; i < max_iterations_02; ++i) { CT const sin_beta = sin(beta); CT const cos_beta = cos(beta); CT const cos_beta_sqr = math::sqr(cos_beta); CT const G = c1 - e_sqr * cos_beta_sqr; - CT R1 = 0; - CT R2 = 0; CT f1 = 0; CT f2 = 0; - if (! is_C1_zero) + if (!geod1.is_Cj_zero) { - CT const t_t01 = t / t01; + bool is_beta_ok = geod1.lon_diff(sin_beta, t, lon1_diff); - // If the argument of asin() is not in [-1, 1] it may indicate that - // the result isn't converging so change the sign and try again - if (t_t01 < -c1 || t_t01 > c1) + if (is_beta_ok) { - if (dbeta_sign == 1) - { - reset_loop(beta, t, dbeta_sign, i, beta_orig, t_orig); - continue; - } - else - { - return false; - } - } - - asin_t_t01 = asin(t_t01); - dL1 = d_lambda_e_sqr(sin_beta1, sin_beta, C1, sqrt_1_C1_sqr, e_sqr); - R1 = asin_t_t01 + dL1; - f1 = C1 / cos_beta * math::sqrt(G / (cos_beta_sqr - C1_sqr)); - } - - if (! is_C2_zero) - { - CT const t_t02 = t / t02; - - // If the argument of asin() is not in [-1, 1] it may indicate that - // the result isn't converging so change the sign and try again - if (t_t02 < -c1 || t_t02 > c1) - { - if (dbeta_sign == 1) - { - reset_loop(beta, t, dbeta_sign, i, beta_orig, t_orig); - continue; - } - else - { - return false; - } - } - - asin_t_t02 = asin(t_t02); - dL2 = d_lambda_e_sqr(sin_beta2, sin_beta, C2, sqrt_1_C2_sqr, e_sqr); - R2 = asin_t_t02 + dL2; - f2 = C2 / cos_beta * math::sqrt(G / (cos_beta_sqr - C2_sqr)); - } - - // The sign of dbeta is changed WRT [Sjoberg02] - CT const dbeta = dbeta_sign * (k_base - R2 + R1) / (f1 - f2); - - if (math::equals(dbeta, CT(0))) - { - break; - } - - // check if the result is converging properly, otherwise change the sign - if (i == 0) - { - dbeta_abs_first = math::abs(dbeta); - } - else if (i == 1 && math::abs(dbeta) > dbeta_abs_first) - { - if (dbeta_sign == 1) - { - reset_loop(beta, t, dbeta_sign, i, beta_orig, t_orig); - continue; + CT const H = cos_beta_sqr - geod1.Cj_sqr; + f1 = geod1.Cj / cos_beta * math::sqrt(G / H); } else { return false; } } - + + if (!geod2.is_Cj_zero) + { + bool is_beta_ok = geod2.lon_diff(sin_beta, t, lon2_diff); + + if (is_beta_ok) + { + CT const H = cos_beta_sqr - geod2.Cj_sqr; + f2 = geod2.Cj / cos_beta * math::sqrt(G / H); + } + else + { + return false; + } + } + + // NOTE: Things may go wrong if the IP is near the vertex + // 1. May converge into the wrong direction (from the other way around). + // This happens when the starting point is on the other side than the vertex + // 2. During converging may "jump" into the other side of the vertex. + // In this case sin_beta/sqrt_1_Cj_sqr and t/t0j is not in [-1, 1] + // 3. f1-f2 may be 0 which means that the intermediate point is on the vertex + // In this case it's not possible to check if this is the correct result + + CT const dbeta_denom = f1 - f2; + //CT const dbeta_denom = math::abs(f1) + math::abs(f2); + + if (math::equals(dbeta_denom, c0)) + { + return false; + } + + // The sign of dbeta is changed WRT [Sjoberg02] + CT const dbeta = (lon1_minus_lon2 + lon1_diff - lon2_diff) / dbeta_denom; + + CT const abs_dbeta = math::abs(dbeta); + if (i > 0 && abs_dbeta > abs_dbeta_last) + { + // The algorithm is not converging + // The intersection may be on the other side of the vertex + return false; + } + abs_dbeta_last = abs_dbeta; + + if (math::equals(dbeta, c0)) + { + // Result found + break; + } + // Because the sign of dbeta is changed WRT [Sjoberg02] dbeta is subtracted here beta = beta - dbeta; t = tan(beta); } - - // t = tan(beta) = (1-f)tan(lat) - lat = atan(t / one_minus_f); - CT const l1 = lon1 + asin_t_t01 - asin_t1_t01 + dL1; - //CT const l2 = lon2 + asin_t_t02 - asin_t2_t02 + dL2; - lon = l1; + lat = geod1.lat(t); + // NOTE: if Cj is 0 then the result is lonj or lonj+180 + lon = ! geod1.is_Cj_zero + ? geod1.lon(lon1_diff) + : geod2.lon(lon2_diff); return true; } -private: - /*! Approximation of dLambda_j [Sjoberg07], expanded into taylor series in e^2 - Maxima script: - dLI_j(c_j, sinB_j, sinB) := integrate(1 / (sqrt(1 - c_j ^ 2 - x ^ 2)*(1 + sqrt(1 - e2*(1 - x ^ 2)))), x, sinB_j, sinB); - dL_j(c_j, B_j, B) := -e2 * c_j * dLI_j(c_j, B_j, B); - S: taylor(dLI_j(c_j, sinB_j, sinB), e2, 0, 3); - assume(c_j < 1); - assume(c_j > 0); - L1: factor(integrate(sqrt(-x ^ 2 - c_j ^ 2 + 1) / (x ^ 2 + c_j ^ 2 - 1), x)); - L2: factor(integrate(((x ^ 2 - 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x)); - L3: factor(integrate(((x ^ 4 - 2 * x ^ 2 + 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x)); - L4: factor(integrate(((x ^ 6 - 3 * x ^ 4 + 3 * x ^ 2 - 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x)); - */ - static inline CT d_lambda_e_sqr(CT const& sin_betaj, CT const& sin_beta, - CT const& Cj, CT const& sqrt_1_Cj_sqr, - CT const& e_sqr) + struct geodesics_type { - if (Order == 0) + geodesics_type(geodesic_type const& g1, geodesic_type const& g2) + : geod1(g1) + , geod2(g2) + , vertex1(geod1.get_vertex_data()) + , vertex2(geod2.get_vertex_data()) + {} + + geodesic_type const& geod1; + geodesic_type const& geod2; + typename geodesic_type::vertex_data vertex1; + typename geodesic_type::vertex_data vertex2; + }; + + struct converge_07_result + { + converge_07_result() + : lon1(0), lon2(0), k1_diff(0), k2_diff(0), t1(0), t2(0) + {} + + CT lon1, lon2; + CT k1_diff, k2_diff; + CT t1, t2; + }; + + static inline bool converge_07(geodesic_type const& geod1, geodesic_type const& geod2, + CT beta, CT t, + CT const& lon1_minus_lon2, CT const& lon_sph, + CT & lon, CT & lat) + { + //CT const c0 = 0; + //CT const c1 = 1; + //CT const c2 = 2; + //CT const pi = math::pi(); + + geodesics_type geodesics(geod1, geod2); + converge_07_result result; + + // calculate first pair of longitudes + if (!converge_07_step_one(CT(sin(beta)), t, lon1_minus_lon2, geodesics, lon_sph, result, false)) { - return 0; + return false; } - CT const c2 = 2; - - CT const asin_B = asin(sin_beta / sqrt_1_Cj_sqr); - CT const asin_Bj = asin(sin_betaj / sqrt_1_Cj_sqr); - CT const L0 = (asin_B - asin_Bj) / c2; + int t_direction = 0; - if (Order == 1) + CT lon_diff_prev = math::longitude_difference(result.lon1, result.lon2); + + // [Sjoberg07] + for (int i = 2; i < max_iterations_07; ++i) { - return -Cj * e_sqr * L0; + // pick t candidates from previous result based on dir + CT t_cand1 = result.t1; + CT t_cand2 = result.t2; + // if direction is 0 the closer one is the first + if (t_direction < 0) + { + t_cand1 = (std::min)(result.t1, result.t2); + t_cand2 = (std::max)(result.t1, result.t2); + } + else if (t_direction > 0) + { + t_cand1 = (std::max)(result.t1, result.t2); + t_cand2 = (std::min)(result.t1, result.t2); + } + else + { + t_direction = t_cand1 < t_cand2 ? -1 : 1; + } + + CT t1 = t; + CT beta1 = beta; + // check if the further calculation is needed + if (converge_07_update(t1, beta1, t_cand1)) + { + break; + } + + bool try_t2 = false; + converge_07_result result_curr; + if (converge_07_step_one(CT(sin(beta1)), t1, lon1_minus_lon2, geodesics, lon_sph, result_curr)) + { + CT const lon_diff1 = math::longitude_difference(result_curr.lon1, result_curr.lon2); + if (lon_diff_prev > lon_diff1) + { + t = t1; + beta = beta1; + lon_diff_prev = lon_diff1; + result = result_curr; + } + else if (t_cand1 != t_cand2) + { + try_t2 = true; + } + else + { + // the result is not fully correct but it won't be more accurate + break; + } + } + // ! converge_07_step_one + else + { + if (t_cand1 != t_cand2) + { + try_t2 = true; + } + else + { + return false; + } + } + + + if (try_t2) + { + CT t2 = t; + CT beta2 = beta; + // check if the further calculation is needed + if (converge_07_update(t2, beta2, t_cand2)) + { + break; + } + + if (! converge_07_step_one(CT(sin(beta2)), t2, lon1_minus_lon2, geodesics, lon_sph, result_curr)) + { + return false; + } + + CT const lon_diff2 = math::longitude_difference(result_curr.lon1, result_curr.lon2); + if (lon_diff_prev > lon_diff2) + { + t_direction *= -1; + t = t2; + beta = beta2; + lon_diff_prev = lon_diff2; + result = result_curr; + } + else + { + // the result is not fully correct but it won't be more accurate + break; + } + } } + lat = geod1.lat(t); + lon = ! geod1.is_Cj_zero ? result.lon1 : result.lon2; + math::normalize_longitude(lon); + + return true; + } + + static inline bool converge_07_update(CT & t, CT & beta, CT const& t_new) + { + CT const c0 = 0; + + CT const beta_new = atan(t_new); + CT const dbeta = beta_new - beta; + beta = beta_new; + t = t_new; + + return math::equals(dbeta, c0); + } + + static inline CT const& pick_t(CT const& t1, CT const& t2, int direction) + { + return direction < 0 ? (std::min)(t1, t2) : (std::max)(t1, t2); + } + + static inline bool converge_07_step_one(CT const& sin_beta, + CT const& t, + CT const& lon1_minus_lon2, + geodesics_type const& geodesics, + CT const& lon_sph, + converge_07_result & result, + bool check_sin_beta = true) + { + bool ok = converge_07_one_geod(sin_beta, t, geodesics.geod1, geodesics.vertex1, lon_sph, + result.lon1, result.k1_diff, check_sin_beta, 1) + && converge_07_one_geod(sin_beta, t, geodesics.geod2, geodesics.vertex2, lon_sph, + result.lon2, result.k2_diff, check_sin_beta, 2); + + if (!ok) + { + return false; + } + + CT const k = lon1_minus_lon2 + result.k1_diff - result.k2_diff; + + // get 2 possible ts one lesser and one greater than t + // t1 is the closer one + calc_ts(t, k, geodesics.geod1, geodesics.geod2, result.t1, result.t2); + + return true; + } + + static inline bool converge_07_one_geod(CT const& sin_beta, CT const& t, + geodesic_type const& geod, + typename geodesic_type::vertex_data const& vertex, + CT const& lon_sph, + CT & lon, CT & k_diff, + bool check_sin_beta, + int id) // TEMP + { + using math::detail::bounded; CT const c1 = 1; - CT const c16 = 16; + + CT k_diff_before = 0; + CT k_diff_behind = 0; - CT const X = sin_beta; - CT const Xj = sin_betaj; - CT const Cj_sqr = math::sqr(Cj); - CT const Cj_sqr_plus_one = Cj_sqr + c1; - CT const one_minus_Cj_sqr = c1 - Cj_sqr; - CT const sqrt_Y = math::sqrt(-math::sqr(X) + one_minus_Cj_sqr); - CT const sqrt_Yj = math::sqrt(-math::sqr(Xj) + one_minus_Cj_sqr); - CT const L1 = (Cj_sqr_plus_one * (asin_B - asin_Bj) + X * sqrt_Y - Xj * sqrt_Yj) / c16; + bool is_beta_ok = geod.k_diffs(sin_beta, vertex, k_diff_before, k_diff_behind, check_sin_beta); - if (Order == 2) + if (! is_beta_ok) { - return -Cj * e_sqr * (L0 + e_sqr * L1); + return false; } - CT const c3 = 3; - CT const c5 = 5; - CT const c128 = 128; + CT const asin_t_t0j = ! geod.is_Cj_zero ? asin(bounded(t / geod.t0j, -c1, c1)) : 0; + CT const sign_asin_t_t0j = geod.sign_lon_diff * asin_t_t0j; - CT const E = Cj_sqr * (c3 * Cj_sqr + c2) + c3; - CT const X_sqr = math::sqr(X); - CT const Xj_sqr = math::sqr(Xj); - CT const F = X * (-c2 * X_sqr + c3 * Cj_sqr + c5); - CT const Fj = Xj * (-c2 * Xj_sqr + c3 * Cj_sqr + c5); - CT const L2 = (E * (asin_B - asin_Bj) + F * sqrt_Y - Fj * sqrt_Yj) / c128; + CT const lon_before = geod.lonj + sign_asin_t_t0j + k_diff_before; + CT const lon_behind = geod.lonj - sign_asin_t_t0j + k_diff_behind; - if (Order == 3) + CT const lon_dist_before = math::longitude_distance_signed(lon_before, lon_sph); + CT const lon_dist_behind = math::longitude_distance_signed(lon_behind, lon_sph); + if (math::abs(lon_dist_before) <= math::abs(lon_dist_behind)) { - return -Cj * e_sqr * (L0 + e_sqr * (L1 + e_sqr * L2)); + k_diff = k_diff_before; + lon = lon_before; + } + else + { + k_diff = k_diff_behind; + lon = lon_behind; } - CT const c8 = 8; - CT const c9 = 9; - CT const c10 = 10; - CT const c15 = 15; - CT const c24 = 24; - CT const c26 = 26; - CT const c33 = 33; - CT const c6144 = 6144; + return true; + } - CT const G = Cj_sqr * (Cj_sqr * (Cj_sqr * c15 + c9) + c9) + c15; - CT const H = -c10 * Cj_sqr - c26; - CT const I = Cj_sqr * (Cj_sqr * c15 + c24) + c33; - CT const J = X_sqr * (X * (c8 * X_sqr + H)) + X * I; - CT const Jj = Xj_sqr * (Xj * (c8 * Xj_sqr + H)) + Xj * I; - CT const L3 = (G * (asin_B - asin_Bj) + J * sqrt_Y - Jj * sqrt_Yj) / c6144; + static inline void calc_ts(CT const& t, CT const& k, + geodesic_type const& geod1, geodesic_type const& geod2, + CT & t1, CT& t2) + { + CT const c1 = 1; + CT const c2 = 2; - // Order 4 and higher - return -Cj * e_sqr * (L0 + e_sqr * (L1 + e_sqr * (L2 + e_sqr * L3))); + CT const K = sin(k); + + BOOST_GEOMETRY_ASSERT(!geod1.is_Cj_zero || !geod2.is_Cj_zero); + if (geod1.is_Cj_zero) + { + t1 = K * geod2.t0j; + t2 = -t1; + } + else if (geod2.is_Cj_zero) + { + t1 = -K * geod1.t0j; + t2 = -t1; + } + else + { + CT const A = math::sqr(geod1.t0j) + math::sqr(geod2.t0j); + CT const B = c2 * geod1.t0j * geod2.t0j * math::sqrt(c1 - math::sqr(K)); + + CT const K_t01_t02 = K * geod1.t0j * geod2.t0j; + CT const D1 = math::sqrt(A + B); + CT const D2 = math::sqrt(A - B); + CT const t_new1 = K_t01_t02 / D1; + CT const t_new2 = K_t01_t02 / D2; + CT const t_new3 = -t_new1; + CT const t_new4 = -t_new2; + + // Pick 2 nearest t_new, one greater and one lesser than current t + CT const abs_t_new1 = math::abs(t_new1); + CT const abs_t_new2 = math::abs(t_new2); + CT const abs_t_max = (std::max)(abs_t_new1, abs_t_new2); + t1 = -abs_t_max; // lesser + t2 = abs_t_max; // greater + if (t1 < t) + { + if (t_new1 < t && t_new1 > t1) + t1 = t_new1; + if (t_new2 < t && t_new2 > t1) + t1 = t_new2; + if (t_new3 < t && t_new3 > t1) + t1 = t_new3; + if (t_new4 < t && t_new4 > t1) + t1 = t_new4; + } + if (t2 > t) + { + if (t_new1 > t && t_new1 < t2) + t2 = t_new1; + if (t_new2 > t && t_new2 < t2) + t2 = t_new2; + if (t_new3 > t && t_new3 < t2) + t2 = t_new3; + if (t_new4 > t && t_new4 < t2) + t2 = t_new4; + } + } + + // the first one is the closer one + if (math::abs(t - t2) < math::abs(t - t1)) + { + std::swap(t2, t1); + } } static inline CT fj(CT const& cos_beta, CT const& cos2_beta, CT const& Cj, CT const& e_sqr) @@ -459,40 +1174,18 @@ private: return Cj / cos_beta * math::sqrt((c1 - e_sqr * cos2_beta) / (cos2_beta - Cj_sqr)); } - template - static inline bool check_t(CT const& t, - CT const& lon_a1, CT const& asin_t_t01, CT const& asin_t1_t01, - CT const& lon_b1, CT const& asin_t_t02, CT const& asin_t2_t02, - CT & choosen_t, CT & choosen_lon1, CT & choosen_lon2, CT & choosen_dlon, - int & t_id) // in/out + /*static inline CT vertical_intersection_longitude(CT const& ip_lon, CT const& seg_lon1, CT const& seg_lon2) { - CT const lon1 = lon_a1 + asin_t_t01 - asin_t1_t01; - CT const lon2 = lon_b1 + asin_t_t02 - asin_t2_t02; + CT const c0 = 0; + CT const lon_2 = ip_lon > c0 ? ip_lon - pi : ip_lon + pi; - CT const dlon = math::abs(math::longitude_distance_signed(lon2, lon1)); - - bool are_equal = math::equals(dlon, CT(0)); - - if (t_id < 0 || are_equal || dlon < choosen_dlon) - { - choosen_t = t; - choosen_lon1 = lon1; - choosen_lon2 = lon2; - choosen_dlon = dlon; - t_id = TId; - } - - return are_equal; - } - - template - static inline void reset_loop(CT & beta, CT & t, int & dbeta_sign, int & i, CT const& beta_orig, CT const& t_orig) - { - dbeta_sign = -1; - beta = beta_orig; - t = t_orig; - i = 0; - } + return (std::min)(math::longitude_difference(ip_lon, seg_lon1), + math::longitude_difference(ip_lon, seg_lon2)) + <= + (std::min)(math::longitude_difference(lon_2, seg_lon1), + math::longitude_difference(lon_2, seg_lon2)) + ? ip_lon : lon_2; + }*/ }; }}} // namespace boost::geometry::formula From 3d76cde68879a89d25bbf6c5f8b2142678d869be Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 23 Nov 2016 17:44:34 +0100 Subject: [PATCH 22/59] [util] Add/modify functions bounded() and longitude_interval_distance_signed(). --- include/boost/geometry/util/math.hpp | 13 +++++++++++ .../util/normalize_spheroidal_coordinates.hpp | 23 +++++++++++++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/include/boost/geometry/util/math.hpp b/include/boost/geometry/util/math.hpp index 234cfa1ed..2addcb6ef 100644 --- a/include/boost/geometry/util/math.hpp +++ b/include/boost/geometry/util/math.hpp @@ -71,6 +71,19 @@ inline T const& greatest(T const& v1, T const& v2, T const& v3, T const& v4, T c } +template +inline T bounded(T const& v, T const& lower, T const& upper) +{ + return (std::min)((std::max)(v, lower), upper); +} + +template +inline T bounded(T const& v, T const& lower) +{ + return (std::max)(v, lower); +} + + template ::value> struct abs diff --git a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp index c6d9888d2..19d4d33d2 100644 --- a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp +++ b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp @@ -282,10 +282,25 @@ inline CoordinateType longitude_distance_unsigned(CoordinateType const& longitud return diff; } +/*! +\brief The abs difference between longitudes in range [0, 180]. +\tparam Units The units of the coordindate system in the spheroid +\tparam CoordinateType The type of the coordinates +\param longitude1 Longitude 1 +\param longitude2 Longitude 2 +\ingroup utility +*/ template -inline CoordinateType longitude_distance_directed(CoordinateType const& longitude_a1, - CoordinateType const& longitude_a2, - CoordinateType const& longitude_b) +inline CoordinateType longitude_difference(CoordinateType const& longitude1, + CoordinateType const& longitude2) +{ + return math::abs(math::longitude_distance_signed(longitude1, longitude2)); +} + +template +inline CoordinateType longitude_interval_distance_signed(CoordinateType const& longitude_a1, + CoordinateType const& longitude_a2, + CoordinateType const& longitude_b) { CoordinateType const c0 = 0; CoordinateType dist_a12 = longitude_distance_signed(longitude_a1, longitude_a2); @@ -295,7 +310,7 @@ inline CoordinateType longitude_distance_directed(CoordinateType const& longitud dist_a12 = -dist_a12; dist_a1b = -dist_a1b; } - + return dist_a1b < c0 ? dist_a1b : dist_a1b > dist_a12 ? dist_a1b - dist_a12 : c0; From 31ec8682f3263c91f3cf5af1ea1033d79c7692ee Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 23 Nov 2016 17:46:21 +0100 Subject: [PATCH 23/59] [test][formulas] Add test cases for sjoberg_intersection formula. --- test/formulas/intersection.cpp | 166 +++++++++++++------ test/formulas/intersection_cases.hpp | 236 ++++++++++++++++++++++++--- test/formulas/test_formula.hpp | 37 +++-- 3 files changed, 348 insertions(+), 91 deletions(-) diff --git a/test/formulas/intersection.cpp b/test/formulas/intersection.cpp index 4c88f65e7..85516a2e0 100644 --- a/test/formulas/intersection.cpp +++ b/test/formulas/intersection.cpp @@ -21,14 +21,23 @@ #include #include -void check_inverse(expected_result const& result, expected_result const& expected, expected_result const& reference, double reference_error) +void check_result(expected_result const& result, expected_result const& expected, + expected_result const& reference, double reference_error, + bool check_reference_only) { - check_one(result.lon, expected.lon, reference.lon, reference_error); - check_one(result.lat, expected.lat, reference.lat, reference_error); + //BOOST_CHECK_MESSAGE((false), "(" << result.lon << " " << result.lat << ") vs (" << expected.lon << " " << expected.lat << ")"); + check_one(result.lon, expected.lon, reference.lon, reference_error, false, check_reference_only); + check_one(result.lat, expected.lat, reference.lat, reference_error, false, check_reference_only); } -void test_all(expected_results const& results) +void test_formulas(expected_results const& results, bool check_reference_only) { + // reference result + if (results.sjoberg_vincenty.lon == ND) + { + return; + } + double const d2r = bg::math::d2r(); double const r2d = bg::math::r2d(); @@ -46,60 +55,115 @@ void test_all(expected_results const& results) // WGS84 bg::srs::spheroid spheroid(6378137.0, 6356752.3142451793); - bg::formula::gnomonic_intersection - ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); - result.lon *= r2d; - result.lat *= r2d; - check_inverse(result, results.gnomonic_vincenty, results.gnomonic_karney, 0.00000001); + if (results.gnomonic_vincenty.lon != ND) + { + bg::formula::gnomonic_intersection + ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); + result.lon *= r2d; + result.lat *= r2d; + check_result(result, results.gnomonic_vincenty, results.sjoberg_vincenty, 0.00000001, check_reference_only); + } - bg::formula::gnomonic_intersection - ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); - result.lon *= r2d; - result.lat *= r2d; - check_inverse(result, results.gnomonic_thomas, results.gnomonic_karney, 0.0000001); + if (results.gnomonic_thomas.lon != ND) + { + bg::formula::gnomonic_intersection + ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); + result.lon *= r2d; + result.lat *= r2d; + check_result(result, results.gnomonic_thomas, results.sjoberg_vincenty, 0.0000001, check_reference_only); + } - bg::formula::sjoberg_intersection - ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); - result.lon *= r2d; - result.lat *= r2d; - check_inverse(result, results.sjoberg_vincenty, results.sjoberg_karney, 0.00000001); + if (results.sjoberg_vincenty.lon != ND) + { + bg::formula::sjoberg_intersection + ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); + result.lon *= r2d; + result.lat *= r2d; + check_result(result, results.sjoberg_vincenty, results.sjoberg_vincenty, 0.00000001, check_reference_only); + } - bg::formula::sjoberg_intersection - ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); - result.lon *= r2d; - result.lat *= r2d; - check_inverse(result, results.sjoberg_thomas, results.sjoberg_karney, 0.0000001); + if (results.sjoberg_thomas.lon != ND) + { + bg::formula::sjoberg_intersection + ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); + result.lon *= r2d; + result.lat *= r2d; + check_result(result, results.sjoberg_thomas, results.sjoberg_vincenty, 0.0000001, check_reference_only); + } - bg::formula::sjoberg_intersection - ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); - result.lon *= r2d; - result.lat *= r2d; - check_inverse(result, results.sjoberg_andoyer, results.sjoberg_karney, 0.0001); + if (results.sjoberg_andoyer.lon != ND) + { + bg::formula::sjoberg_intersection + ::apply(lona1r, lata1r, lona2r, lata2r, lonb1r, latb1r, lonb2r, latb2r, result.lon, result.lat, spheroid); + result.lon *= r2d; + result.lat *= r2d; + check_result(result, results.sjoberg_andoyer, results.sjoberg_vincenty, 0.0001, check_reference_only); + } - typedef bg::model::point > point_geo; - typedef bg::model::point point_3d; - point_geo a1(results.p1.lon, results.p1.lat); - point_geo a2(results.p2.lon, results.p2.lat); - point_geo b1(results.q1.lon, results.q1.lat); - point_geo b2(results.q2.lon, results.q2.lat); - point_3d a1v = bg::formula::geo_to_cart3d(a1, spheroid); - point_3d a2v = bg::formula::geo_to_cart3d(a2, spheroid); - point_3d b1v = bg::formula::geo_to_cart3d(b1, spheroid); - point_3d b2v = bg::formula::geo_to_cart3d(b2, spheroid); - point_3d resv(0, 0, 0); - point_geo res(0, 0); + if (results.great_elliptic.lon != ND) + { + typedef bg::model::point > point_geo; + typedef bg::model::point point_3d; + point_geo a1(results.p1.lon, results.p1.lat); + point_geo a2(results.p2.lon, results.p2.lat); + point_geo b1(results.q1.lon, results.q1.lat); + point_geo b2(results.q2.lon, results.q2.lat); + point_3d a1v = bg::formula::geo_to_cart3d(a1, spheroid); + point_3d a2v = bg::formula::geo_to_cart3d(a2, spheroid); + point_3d b1v = bg::formula::geo_to_cart3d(b1, spheroid); + point_3d b2v = bg::formula::geo_to_cart3d(b2, spheroid); + point_3d resv(0, 0, 0); + point_geo res(0, 0); + bg::formula::great_elliptic_intersection(a1v, a2v, b1v, b2v, resv, spheroid); + res = bg::formula::cart3d_to_geo(resv, spheroid); + result.lon = bg::get<0>(res); + result.lat = bg::get<1>(res); + check_result(result, results.great_elliptic, results.sjoberg_vincenty, 0.01, check_reference_only); + } +} - bg::formula::experimental_elliptic_intersection(a1v, a2v, b1v, b2v, resv, spheroid); - res = bg::formula::cart3d_to_geo(resv, spheroid); - result.lon = bg::get<0>(res); - result.lat = bg::get<1>(res); - check_inverse(result, results.experimental_elliptic, results.gnomonic_karney, 0.0001); +void test_4_input_combinations(expected_results const& results, bool check_reference_only) +{ + test_formulas(results, check_reference_only); - bg::formula::great_elliptic_intersection(a1v, a2v, b1v, b2v, resv, spheroid); - res = bg::formula::cart3d_to_geo(resv, spheroid); - result.lon = bg::get<0>(res); - result.lat = bg::get<1>(res); - check_inverse(result, results.great_elliptic, results.gnomonic_karney, 0.0001); +#ifdef BOOST_GEOMETRY_TEST_GEO_INTERSECTION_TEST_SIMILAR + { + expected_results results_alt = results; + std::swap(results_alt.p1, results_alt.p2); + test_formulas(results_alt, true); + } + { + expected_results results_alt = results; + std::swap(results_alt.q1, results_alt.q2); + test_formulas(results_alt, true); + } + { + expected_results results_alt = results; + std::swap(results_alt.p1, results_alt.p2); + std::swap(results_alt.q1, results_alt.q2); + test_formulas(results_alt, true); + } +#endif +} + +void test_all(expected_results const& results) +{ + test_4_input_combinations(results, false); + +#ifdef BOOST_GEOMETRY_TEST_GEO_INTERSECTION_TEST_SIMILAR + expected_results results_alt = results; + results_alt.p1.lat *= -1; + results_alt.p2.lat *= -1; + results_alt.q1.lat *= -1; + results_alt.q2.lat *= -1; + results_alt.gnomonic_vincenty.lat *= -1; + results_alt.gnomonic_thomas.lat *= -1; + results_alt.sjoberg_vincenty.lat *= -1; + results_alt.sjoberg_thomas.lat *= -1; + results_alt.sjoberg_andoyer.lat *= -1; + results_alt.great_elliptic.lat *= -1; + test_4_input_combinations(results_alt, true); +#endif } int test_main(int, char*[]) diff --git a/test/formulas/intersection_cases.hpp b/test/formulas/intersection_cases.hpp index 4b52182d2..483d52b3b 100644 --- a/test/formulas/intersection_cases.hpp +++ b/test/formulas/intersection_cases.hpp @@ -30,115 +30,297 @@ struct expected_results coordinates p2; coordinates q1; coordinates q2; - expected_result gnomonic_karney; expected_result gnomonic_vincenty; expected_result gnomonic_thomas; - expected_result sjoberg_karney; expected_result sjoberg_vincenty; expected_result sjoberg_thomas; expected_result sjoberg_andoyer; - expected_result experimental_elliptic; expected_result great_elliptic; }; +#define ND 1000 + expected_results expected[] = { { { -1, -1 },{ 1, 1 }, { -1, 1 },{ 1, -1 }, - { 0.0000000000000000, 0.0000000000000002 }, { 0.0000000000000000, 0.0000000000000000 }, { 0.0000000000000000, 0.0000000000000000 }, - { 0.0000000000633173, 0.0000000000000003 }, { 0.0000000000626537, -0.0000000000000000 }, { -0.0000000187861002, -0.0000000000000000 }, { -0.0000055778585615, -0.0000000000000000 }, - { 0.0000000000000000, 0.0000000000000000 }, { -0.0000000000000000, -0.0000000000000000 } },{ { 1, 1 },{ -1, -1 }, { -1, 1 },{ 1, -1 }, - { 0.0000000000000000, 0.0000000000000002 }, { 0.0000000000000000, 0.0000000000000000 }, { 0.0000000000000000, 0.0000000000000000 }, - { 0.0000000000000000, 0.0000000000633274 }, { 0.0000000000000000, 0.0000000000626632 }, { -0.0000000000000006, -0.0000000187889745 }, { -0.0000000000000001, -0.0000055787431353 }, - { 0.0000000000000000, 0.0000000000000000 }, { -0.0000000000000000, 0.0000000000000000 } },{ { -1, -1 },{ 1, 1 }, { 1, -1 },{ -1, 1 }, - { 0.0000000000000000, 0.0000000000000002 }, { 0.0000000000000000, 0.0000000000000000 }, { 0.0000000000000000, 0.0000000000000000 }, - { -0.0000000000000000, -0.0000000000633268 }, { -0.0000000000000000, -0.0000000000626632 }, { -0.0000000000000004, 0.0000000187889746 }, { 0.0000000000000001, 0.0000055787431353 }, - { 0.0000000000000000, 0.0000000000000000 }, { 0.0000000000000000, 0.0000000000000000 } },{ { 1, 1 },{ -1, -1 }, { 1, -1 },{ -1, 1 }, - { 0.0000000000000000, 0.0000000000000002 }, { 0.0000000000000000, 0.0000000000000000 }, { 0.0000000000000000, 0.0000000000000000 }, - { -0.0000000000633173, 0.0000000000000003 }, { -0.0000000000626537, -0.0000000000000000 }, { 0.0000000187860994, 0.0000000000000001 }, { 0.0000055778585615, -0.0000000000000000 }, - { 0.0000000000000000, 0.0000000000000000 }, { -0.0000000000000000, 0.0000000000000000 } },{ { 0, 0 },{ 1, 1 }, { 0, 1 },{ 1, 0 }, - { 0.5000000000000000, 0.5000573755188484 }, { 0.5000000000000000, 0.5000573755188470 }, { 0.5000000000000000, 0.5000573755109839 }, - { 0.5000000000316606, 0.5000573755188390 }, { 0.5000000000313266, 0.5000573755188389 }, { 0.4999999906069524, 0.5000573755152582 }, { 0.4999972102164753, 0.5000573755151276 }, - { 0.5000000000000001, 0.5000573754962017 }, { 0.4999999999999999, 0.5000571197534014 } },{ { 1, 1 },{ 0, 0 }, { 0, 1 },{ 1, 0 }, - { 0.5000000000000000, 0.5000573755188484 }, { 0.5000000000000000, 0.5000573755188470 }, { 0.5000000000000000, 0.5000573755109839 }, - { 0.5000000000000000, 0.5000573755505008 }, { 0.5000000000000000, 0.5000573755501669 }, { 0.4999999999999996, 0.5000573661218464 }, { 0.4999999999999999, 0.5000545856093679 }, - { 0.5000000000000001, 0.5000573754962017 }, { 0.4999999999999999, 0.5000571197534014 } },{ { 0, 0 },{ 1, 1 }, { 1, 0 },{ 0, 1 }, - { 0.5000000000000000, 0.5000573755188484 }, { 0.5000000000000000, 0.5000573755188470 }, { 0.5000000000000000, 0.5000573755109839 }, - { 0.5000000000000001, 0.5000573754871773 }, { 0.4999999999999999, 0.5000573754875109 }, { 0.4999999999999999, 0.5000573849086647 }, { 0.5000000000000000, 0.5000601654208935 }, - { 0.5000000000000001, 0.5000573754962017 }, { 0.4999999999999999, 0.5000571197534014 } },{ { 1, 1 },{ 0, 0 }, { 1, 0 },{ 0, 1 }, - { 0.5000000000000000, 0.5000573755188484 }, { 0.5000000000000000, 0.5000573755188470 }, { 0.5000000000000000, 0.5000573755109839 }, - { 0.4999999999683394, 0.5000573755188390 }, { 0.4999999999686731, 0.5000573755188389 }, { 0.5000000093930521, 0.5000573755152582 }, { 0.5000027897835244, 0.5000573755151276 }, - { 0.5000000000000001, 0.5000573754962017 }, { 0.4999999999999999, 0.5000571197534014 } + },{ + { 1, 1 }, {2, 2}, + {-2, -1}, {-1, -1}, + { ND, ND }, + { ND, ND }, + { -0.9981650042162076, -0.999999718164758 }, + { ND, ND }, + { ND, ND }, + { -0.9981731758085121, -0.9999997213000095 } + },{ + {-25, -31}, {3, 44}, + {-66, -14}, {-1, -1}, + { ND, ND }, + { ND, ND }, + { -15.83269406235058, -4.87746450262433 }, + { ND, ND }, + { ND, ND }, + { -15.82846301029918, -4.869650527718342 } + },{ + {-47, -8}, {-1, -4}, + {-40, -5}, {-5, -5}, + { ND, ND }, + { ND, ND }, + { -10.28209064194141, -5.124101297536392 }, + { ND, ND }, + { ND, ND }, + { -10.29663941896089, -5.123527178300465 } + }, { + {-43, -8}, {3, -4}, + {-43, -5}, {3, -7}, + { ND, ND }, + { ND, ND }, + { -19.95519754153282, -6.521540589691206 }, + { ND, ND }, + { ND, ND }, + { -19.95535387979888, -6.517861579906892 } + }, { + {-1, -17}, {-5, 3}, + {-40, -5}, {-1, -5}, + { ND, ND }, + { ND, ND }, + { -3.424525475838061, -5.070550173747042 }, + { ND, ND }, + { ND, ND }, + { -3.424493504575876, -5.070060631936228 } + }, { + {-29, -4}, {-1, -5}, + {-40, -5}, {-1, -5}, + { ND, ND }, + { ND, ND }, + { -0.9999999978280006, -5.00000000000684 }, + { ND, ND }, + { ND, ND }, + { -0.9999999999999919, -5 } + }, { + {-4, -6}, {-40, -5}, + {-40, -5}, {-1, -5}, + { ND, ND }, + { ND, ND }, + { -40.00000000470697, -4.999999999854269 }, + { ND, ND }, + { ND, ND }, + { -40.00000000000003, -4.999999999999998 } + }, { + {5, -4}, {-25, -5}, + {-40, -5}, {-1, -5}, + { ND, ND }, + { ND, ND }, + { -40.04870859732954, -4.998490166905039 }, + { ND, ND }, + { ND, ND }, + { -39.87674154148858, -5.003778171392734 } + }, { + {-44, -1}, {38, -7}, + {-54, -10}, {27, 3}, + { ND, ND }, + { ND, ND }, + { -12.49994722584679, -4.492062263169279 }, + { ND, ND }, + { ND, ND }, + { -12.49786552878283, -4.48224349980724 } + }, { + {-29, -5}, {10, -5}, + {-40, -5}, {-1, -5}, + { ND, ND }, + { ND, ND }, + { -14.99999999880286, -5.280237387890117 }, + { ND, ND }, + { ND, ND }, + { -14.99999999999999, -5.278284829442087 } + }, { + {-29, -5}, {7, -5}, + {-40, -5}, {-1, -5}, + { ND, ND }, + { ND, ND }, + { -12.68814198534678, -5.255404335144863 }, + { ND, ND }, + { ND, ND }, + { -12.68883814704657, -5.253634733357015 } + }, { + {-29, -5}, {2, -5}, + {-40, -5}, {-1, -5}, + { ND, ND }, + { ND, ND }, + { -6.793546728871506, -5.153444544719724 }, + { ND, ND }, + { ND, ND }, + { -6.794933162619669, -5.152409434095275 } + }, { + {-29, -5}, {2, -5}, + {-30, -5}, {-1, -5}, + { ND, ND }, + { ND, ND }, + { -22.13104411461423, -5.130155316882458 }, + { ND, ND }, + { ND, ND }, + { -22.13016591567375, -5.129277412801533 } + }, { + {-29, -5}, {2, -5}, + {-32, -5}, {-1, -5}, + { ND, ND }, + { ND, ND }, + { -14.99999999908104, -5.187213913248173 }, + { ND, ND }, + { ND, ND }, + { -15.00000000000009, -5.185931965560752 } + }, { + {-92, -24}, {44, 19}, + {-78, -5}, {50, -5}, + { ND, ND }, + { ND, ND }, + { -31.65127861442256, -10.84411293410938 }, + { ND, ND }, + { ND, ND }, + { -31.65294079308247, -10.76785432928444 } + }, { + {-93, -15}, {22, 3}, + {-78, -5}, {50, -5}, + { ND, ND }, + { ND, ND }, + { -32.33383382062637, -10.80295061031259 }, + { ND, ND }, + { ND, ND }, + { -32.26069578187354, -10.73176059393484 } + }, { + {-93, -15}, {28, 3}, + {-78, -5}, {50, -5}, + { ND, ND }, + { ND, ND }, + { -25.71257598566304, -11.13752154787724 }, + { ND, ND }, + { ND, ND }, + { -25.67761999514295, -11.05881610957402 } + }, { + {-54, -21}, {20, 17}, + {-59, -5}, {13, -5}, + { ND, ND }, + { ND, ND }, + { -22.96397490169162, -6.181426780426698 }, + { ND, ND }, + { ND, ND }, + { -22.95999443662035, -6.172089364736989 } + }, { + {-31, -10}, {-31, -2}, + {-37, -10}, {-25, -2}, + { ND, ND }, + { ND, ND }, + { -31.00000000000001, -6.062729839503469 }, + { ND, ND }, + { ND, ND }, + { -31, -6.062411484514648 } + }, { + {-26, -10}, {-26, 13}, + {-37, -4}, {-18, -4}, + { ND, ND }, + { ND, ND }, + { -25.99999999999969, -4.05441766735837 }, + { ND, ND }, + { ND, ND }, + { -26, -4.05405101510235 } + }, { + {-26, -10}, {154, 78}, + {-46, 41}, {-13, 36}, + { ND, ND }, + { ND, ND }, + { -26, 39.19492836828103 }, + { ND, ND }, + { ND, ND }, + { -26, 39.19004133747198 } + }, { + {-26, -10}, {154, 68}, + {-85, 79}, {22, 80}, + { ND, ND }, + { ND, ND }, + { -26.00000000000013, 83.7164889918963 }, + { ND, ND }, + { ND, ND }, + { -26.00000000000002, 83.71603758698208 } + }, { + {-25, 55}, {155, 68}, + {178, 79}, {124, 80}, + { ND, ND }, + { ND, ND }, + { 155, 80.55982670305147 }, + { ND, ND }, + { ND, ND }, + { 155, 80.55961176607357 } } }; diff --git a/test/formulas/test_formula.hpp b/test/formulas/test_formula.hpp index 92f690bd3..262980d98 100644 --- a/test/formulas/test_formula.hpp +++ b/test/formulas/test_formula.hpp @@ -24,7 +24,8 @@ void normalize_deg(double & deg) deg += 360.0; } -void check_one(double result, double expected, double reference, double reference_error, bool normalize = false) +void check_one(double result, double expected, double reference, double reference_error, + bool normalize = false, bool check_reference_only = false) { if (normalize) { @@ -33,18 +34,29 @@ void check_one(double result, double expected, double reference, double referenc normalize_deg(reference); } - double res_max = (std::max)(bg::math::abs(result), bg::math::abs(expected)); - if (res_max > 100 * std::numeric_limits::epsilon()) + if (! check_reference_only) { - BOOST_CHECK_CLOSE(result, expected, 0.001); - } - else if (res_max > 10 * std::numeric_limits::epsilon()) - { - BOOST_CHECK_CLOSE(result, expected, 0.1); - } - else if (res_max > std::numeric_limits::epsilon()) - { - BOOST_CHECK_CLOSE(result, expected, 10); + double eps = std::numeric_limits::epsilon(); + double abs_result = bg::math::abs(result); + double abs_expected = bg::math::abs(expected); + double res_max = (std::max)(abs_result, abs_expected); + double res_min = (std::min)(abs_result, abs_expected); + if (res_min <= eps) // including 0 + { + BOOST_CHECK(abs_result <= 10 * eps && abs_expected <= 10 * eps); + } + else if (res_max > 100 * eps) + { + BOOST_CHECK_CLOSE(result, expected, 0.1); + } + else if (res_max > 10 * eps) + { + BOOST_CHECK_CLOSE(result, expected, 10); + } + else if (res_max > eps) + { + BOOST_CHECK_CLOSE(result, expected, 1000); + } } // NOTE: in some cases it probably will be necessary to normalize @@ -52,7 +64,6 @@ void check_one(double result, double expected, double reference, double referenc double ref_diff = bg::math::abs(result - reference); double ref_max = (std::max)(bg::math::abs(result), bg::math::abs(reference)); bool is_ref_close = ref_diff <= reference_error || ref_diff <= reference_error * ref_max; - BOOST_CHECK_MESSAGE((is_ref_close), std::setprecision(16) << "{" << result << "} and {" << reference << "} not close enough."); } From 941590b975e3563765c9252029d992b14f8a6d8a Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 6 Dec 2016 00:21:03 +0100 Subject: [PATCH 24/59] [core] Initialize spheroid by default with more precise WGS84 minor axis. --- include/boost/geometry/core/srs.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/boost/geometry/core/srs.hpp b/include/boost/geometry/core/srs.hpp index bf1b4e28a..cd637c1e9 100644 --- a/include/boost/geometry/core/srs.hpp +++ b/include/boost/geometry/core/srs.hpp @@ -4,8 +4,8 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. -// This file was modified by Oracle on 2014. -// Modifications copyright (c) 2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014, 2016. +// Modifications copyright (c) 2014-2016 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -51,7 +51,7 @@ public: spheroid() : m_a(RadiusType(6378137.0)) - , m_b(RadiusType(6356752.314245)) + , m_b(RadiusType(6356752.3142451793)) {} template From ee63de69931e475f522135c85b00f2cd57641edd Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 6 Dec 2016 00:22:06 +0100 Subject: [PATCH 25/59] [strategies] Add geodesic_intersection geographic strategy. The strategy use sjoberg intersection (crossing segments intersection point) formula and chosen inverse formula (sides and distances). --- .../geographic/geodesic_intersection.hpp | 703 ++++++++++++++++++ 1 file changed, 703 insertions(+) create mode 100644 include/boost/geometry/strategies/geographic/geodesic_intersection.hpp diff --git a/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp b/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp new file mode 100644 index 000000000..7d502978c --- /dev/null +++ b/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp @@ -0,0 +1,703 @@ +// Boost.Geometry + +// Copyright (c) 2016, Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_GEODESIC_INTERSECTION_HPP +#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_GEODESIC_INTERSECTION_HPP + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace intersection +{ + +//TODO: Improve the robustness/accuracy/repeatability by +// moving all segments to 0 longitude +// switching the segments +// switching the endpoints +// BECAUSE: the result calculated by sjoberg intersection strategy +// is not consistent when the above are altered +// worse than that, the error is a lot greater either for lon or lat +// so make it consistently greater for lat + +template +< + typename Policy, + typename Spheroid = srs::spheroid, + template class Inverse = formula::andoyer_inverse, + unsigned int Order = 2, + typename CalculationType = void +> +struct relate_geodesic_segments +{ + typedef typename Policy::return_type return_type; + + enum intersection_point_flag { ipi_inters = 0, ipi_at_a1, ipi_at_a2, ipi_at_b1, ipi_at_b2 }; + + // segment_intersection_info cannot outlive relate_ecef_segments + template + struct segment_intersection_info + { + typedef typename select_most_precise + < + CoordinateType, double + >::type promoted_type; + + promoted_type comparable_length_a() const + { + return robust_ra.denominator(); + } + + promoted_type comparable_length_b() const + { + return robust_rb.denominator(); + } + + template + void assign_a(Point& point, Segment1 const& a, Segment2 const& b) const + { + assign(point, a, b); + } + template + void assign_b(Point& point, Segment1 const& a, Segment2 const& b) const + { + assign(point, a, b); + } + + template + void assign(Point& point, Segment1 const& a, Segment2 const& b) const + { + if (ip_flag == ipi_inters) + { + // TODO: assign the rest of coordinates + set_from_radian<0>(point, lon); + set_from_radian<1>(point, lat); + } + else if (ip_flag == ipi_at_a1) + { + detail::assign_point_from_index<0>(a, point); + } + else if (ip_flag == ipi_at_a2) + { + detail::assign_point_from_index<1>(a, point); + } + else if (ip_flag == ipi_at_b1) + { + detail::assign_point_from_index<0>(b, point); + } + else // ip_flag == ipi_at_b2 + { + detail::assign_point_from_index<1>(b, point); + } + } + + CoordinateType lon; + CoordinateType lat; + SegmentRatio robust_ra; + SegmentRatio robust_rb; + intersection_point_flag ip_flag; + }; + + // Relate segments a and b + template + static inline return_type apply(Segment1 const& a, Segment2 const& b, + RobustPolicy const& robust_policy) + { + typedef typename point_type::type point1_t; + typedef typename point_type::type point2_t; + point1_t a1, a2; + point2_t b1, b2; + + // TODO: use indexed_point_view if possible? + detail::assign_point_from_index<0>(a, a1); + detail::assign_point_from_index<1>(a, a2); + detail::assign_point_from_index<0>(b, b1); + detail::assign_point_from_index<1>(b, b2); + + return apply(a, b, robust_policy, a1, a2, b1, b2); + } + + // Relate segments a and b + template + static inline return_type apply(Segment1 const& a, Segment2 const& b, + RobustPolicy const&, + Point1 const& a1, Point1 const& a2, Point2 const& b1, Point2 const& b2) + { + BOOST_CONCEPT_ASSERT( (concepts::ConstSegment) ); + BOOST_CONCEPT_ASSERT( (concepts::ConstSegment) ); + + typedef typename select_calculation_type + ::type calc_t; + + // For now create it using default constructor. In the future it could + // be stored in strategy. However then apply() wouldn't be static and + // all relops and setops would have to take the strategy or model. + Spheroid spheroid_in; + // normalized spheroid + srs::spheroid spheroid(calc_t(1), + calc_t(get_radius<2>(spheroid_in)) // b/a + / calc_t(get_radius<0>(spheroid_in))); + + // TODO: check only 2 first coordinates here? + using geometry::detail::equals::equals_point_point; + bool a_is_point = equals_point_point(a1, a2); + bool b_is_point = equals_point_point(b1, b2); + + if(a_is_point && b_is_point) + { + return equals_point_point(a1, b2) + ? Policy::degenerate(a, true) + : Policy::disjoint() + ; + } + + calc_t const a1_lon = get_as_radian<0>(a1); + calc_t const a1_lat = get_as_radian<1>(a1); + calc_t const a2_lon = get_as_radian<0>(a2); + calc_t const a2_lat = get_as_radian<1>(a2); + calc_t const b1_lon = get_as_radian<0>(b1); + calc_t const b1_lat = get_as_radian<1>(b1); + calc_t const b2_lon = get_as_radian<0>(b2); + calc_t const b2_lat = get_as_radian<1>(b2); + + side_info sides; + + // NOTE: potential optimization, don't calculate distance at this point + // this would require to reimplement inverse strategy to allow + // calculation of distance if needed, probably also storing intermediate + // results somehow inside an object. + typedef Inverse inverse_dist_azi; + typedef typename inverse_dist_azi::result_type inverse_result; + + // TODO: no need to call inverse formula if we know that the points are equal + // distance can be set to 0 in this case and azimuth may be not calculated + bool const is_equal_a1_b1 = equals_point_point(a1, b1); + bool const is_equal_a2_b1 = equals_point_point(a2, b1); + + inverse_result res_b1_b2 = inverse_dist_azi::apply(b1_lon, b1_lat, b2_lon, b2_lat, spheroid); + inverse_result res_b1_a1 = inverse_dist_azi::apply(b1_lon, b1_lat, a1_lon, a1_lat, spheroid); + inverse_result res_b1_a2 = inverse_dist_azi::apply(b1_lon, b1_lat, a2_lon, a2_lat, spheroid); + sides.set<0>(is_equal_a1_b1 ? 0 : formula::azimuth_side_value(res_b1_a1.azimuth, res_b1_b2.azimuth), + is_equal_a2_b1 ? 0 : formula::azimuth_side_value(res_b1_a2.azimuth, res_b1_b2.azimuth)); + if (sides.same<0>()) + { + // Both points are at the same side of other segment, we can leave + return Policy::disjoint(); + } + + bool const is_equal_a1_b2 = equals_point_point(a1, b2); + + inverse_result res_a1_a2 = inverse_dist_azi::apply(a1_lon, a1_lat, a2_lon, a2_lat, spheroid); + inverse_result res_a1_b1 = inverse_dist_azi::apply(a1_lon, a1_lat, b1_lon, b1_lat, spheroid); + inverse_result res_a1_b2 = inverse_dist_azi::apply(a1_lon, a1_lat, b2_lon, b2_lat, spheroid); + sides.set<1>(is_equal_a1_b1 ? 0 : formula::azimuth_side_value(res_a1_b1.azimuth, res_a1_a2.azimuth), + is_equal_a1_b2 ? 0 : formula::azimuth_side_value(res_a1_b2.azimuth, res_a1_a2.azimuth)); + if (sides.same<1>()) + { + // Both points are at the same side of other segment, we can leave + return Policy::disjoint(); + } + + // NOTE: at this point the segments may still be disjoint + // NOTE: at this point one of the segments may be degenerated + + bool collinear = sides.collinear(); + + if (! collinear) + { + // WARNING: the side strategy doesn't have the info about the other + // segment so it may return results inconsistent with this intersection + // strategy, as it checks both segments for consistency + + if (sides.get<0, 0>() == 0 && sides.get<0, 1>() == 0) + { + collinear = true; + sides.set<1>(0, 0); + } + else if (sides.get<1, 0>() == 0 && sides.get<1, 1>() == 0) + { + collinear = true; + sides.set<0>(0, 0); + } + } + + if (collinear) + { + if (a_is_point) + { + return collinear_one_degenerted(a, true, b1, b2, a1, a2, res_b1_b2, res_b1_a1); + } + else if (b_is_point) + { + return collinear_one_degenerted(b, false, a1, a2, b1, b2, res_a1_a2, res_a1_b1); + } + else + { + calc_t dist_a1_a2, dist_a1_b1, dist_a1_b2; + calc_t dist_b1_b2, dist_b1_a1, dist_b1_a2; + // use shorter segment + if (res_a1_a2.distance <= res_b1_b2.distance) + { + calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_b1, dist_a1_a2, dist_a1_b1); + calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_b2, dist_a1_a2, dist_a1_b2); + dist_b1_b2 = dist_a1_b2 - dist_a1_b1; + dist_b1_a1 = -dist_a1_b1; + dist_b1_a2 = dist_a1_a2 - dist_a1_b1; + } + else + { + calculate_collinear_data(b1, b2, a1, a2, res_b1_b2, res_b1_a1, dist_b1_b2, dist_b1_a1); + calculate_collinear_data(b1, b2, a1, a2, res_b1_b2, res_b1_a2, dist_b1_b2, dist_b1_a2); + dist_a1_a2 = dist_b1_a2 - dist_b1_a1; + dist_a1_b1 = -dist_b1_a1; + dist_a1_b2 = dist_b1_b2 - dist_b1_a1; + } + + segment_ratio ra_from(dist_b1_a1, dist_b1_b2); + segment_ratio ra_to(dist_b1_a2, dist_b1_b2); + segment_ratio rb_from(dist_a1_b1, dist_a1_a2); + segment_ratio rb_to(dist_a1_b2, dist_a1_a2); + + calc_t const c0 = 0; + + // NOTE: this is probably not needed + int const a1_wrt_b = position_value(c0, dist_a1_b1, dist_a1_b2); + int const a2_wrt_b = position_value(dist_a1_a2, dist_a1_b1, dist_a1_b2); + int const b1_wrt_a = position_value(c0, dist_b1_a1, dist_b1_a2); + int const b2_wrt_a = position_value(dist_b1_b2, dist_b1_a1, dist_b1_a2); + + if (a1_wrt_b == 1) + { + ra_from.assign(0, dist_b1_b2); + rb_from.assign(0, dist_a1_a2); + } + else if (a1_wrt_b == 3) + { + ra_from.assign(dist_b1_b2, dist_b1_b2); + rb_to.assign(0, dist_a1_a2); + } + + if (a2_wrt_b == 1) + { + ra_to.assign(0, dist_b1_b2); + rb_from.assign(dist_a1_a2, dist_a1_a2); + } + else if (a2_wrt_b == 3) + { + ra_to.assign(dist_b1_b2, dist_b1_b2); + rb_to.assign(dist_a1_a2, dist_a1_a2); + } + + if ((a1_wrt_b < 1 && a2_wrt_b < 1) || (a1_wrt_b > 3 && a2_wrt_b > 3)) + { + return Policy::disjoint(); + } + + bool const opposite = ! same_direction(res_a1_a2.azimuth, res_b1_b2.azimuth); + + return Policy::segments_collinear(a, b, opposite, + a1_wrt_b, a2_wrt_b, b1_wrt_a, b2_wrt_a, + ra_from, ra_to, rb_from, rb_to); + } + } + else // crossing or touching + { + if (a_is_point || b_is_point) + { + return Policy::disjoint(); + } + + calc_t lon = 0, lat = 0; + intersection_point_flag ip_flag; + calc_t dist_a1_a2, dist_a1_i1, dist_b1_b2, dist_b1_i1; + if (calculate_ip_data(a1, a2, b1, b2, + a1_lon, a1_lat, a2_lon, a2_lat, + b1_lon, b1_lat, b2_lon, b2_lat, + res_a1_a2, res_a1_b1, res_a1_b2, + res_b1_b2, res_b1_a1, res_b1_a2, + sides, spheroid, + lon, lat, + dist_a1_a2, dist_a1_i1, dist_b1_b2, dist_b1_i1, + ip_flag)) + { + // intersects + segment_intersection_info + < + calc_t, + segment_ratio + > sinfo; + + sinfo.lon = lon; + sinfo.lat = lat; + sinfo.robust_ra.assign(dist_a1_i1, dist_a1_a2); + sinfo.robust_rb.assign(dist_b1_i1, dist_b1_b2); + sinfo.ip_flag = ip_flag; + + return Policy::segments_crosses(sides, sinfo, a, b); + } + else + { + return Policy::disjoint(); + } + } + } + +private: + template + static inline return_type collinear_one_degenerted(Segment const& segment, bool degenerated_a, + Point1 const& a1, Point1 const& a2, + Point2 const& b1, Point2 const& b2, + ResultInverse const& res_a1_a2, + ResultInverse const& res_a1_bi) + { + CalcT dist_1_2, dist_1_o; + return ! calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_bi, dist_1_2, dist_1_o) + ? Policy::disjoint() + : Policy::one_degenerate(segment, segment_ratio(dist_1_o, dist_1_2), degenerated_a); + } + + // TODO: instead of the code below test bi against a1 and a2 here? + template + static inline bool calculate_collinear_data(Point1 const& a1, Point1 const& a2, // in + Point2 const& b1, Point2 const& b2, // in + ResultInverse const& res_a1_a2, // in + ResultInverse const& res_a1_bi, // in + CalcT& dist_a1_a2, CalcT& dist_a1_bi) // out + { + dist_a1_a2 = res_a1_a2.distance; + + dist_a1_bi = res_a1_bi.distance; + if (! same_direction(res_a1_bi.azimuth, res_a1_a2.azimuth)) + { + dist_a1_bi = -dist_a1_bi; + } + + // if i1 is close to a1 and b1 or b2 is equal to a1 + if (is_endpoint_equal(dist_a1_bi, a1, b1, b2)) + { + dist_a1_bi = 0; + return true; + } + // or i1 is close to a2 and b1 or b2 is equal to a2 + else if (is_endpoint_equal(dist_a1_a2 - dist_a1_bi, a2, b1, b2)) + { + dist_a1_bi = dist_a1_a2; + return true; + } + + // or i1 is on b + return segment_ratio(dist_a1_bi, dist_a1_a2).on_segment(); + } + + template + static inline bool calculate_ip_data(Point1 const& a1, Point1 const& a2, // in + Point2 const& b1, Point2 const& b2, // in + CalcT const& a1_lon, CalcT const& a1_lat, // in + CalcT const& a2_lon, CalcT const& a2_lat, // in + CalcT const& b1_lon, CalcT const& b1_lat, // in + CalcT const& b2_lon, CalcT const& b2_lat, // in + ResultInverse const& res_a1_a2, // in + ResultInverse const& res_a1_b1, // in + ResultInverse const& res_a1_b2, // in + ResultInverse const& res_b1_b2, // in + ResultInverse const& res_b1_a1, // in + ResultInverse const& res_b1_a2, // in + side_info const& sides, // in + Spheroid_ const& spheroid, // in + CalcT & lon, CalcT & lat, // out + CalcT& dist_a1_a2, CalcT& dist_a1_ip, // out + CalcT& dist_b1_b2, CalcT& dist_b1_ip, // out + intersection_point_flag& ip_flag) // out + { + dist_a1_a2 = res_a1_a2.distance; + dist_b1_b2 = res_b1_b2.distance; + + // assign the IP if some endpoints overlap + using geometry::detail::equals::equals_point_point; + if (equals_point_point(a1, b1)) + { + lon = a1_lon; + lat = a1_lat; + dist_a1_ip = 0; + dist_b1_ip = 0; + ip_flag = ipi_at_a1; + return true; + } + else if (equals_point_point(a1, b2)) + { + lon = a1_lon; + lat = a1_lat; + dist_a1_ip = 0; + dist_b1_ip = dist_b1_b2; + ip_flag = ipi_at_a1; + return true; + } + else if (equals_point_point(a2, b1)) + { + lon = a2_lon; + lat = a2_lat; + dist_a1_ip = dist_a1_a2; + dist_b1_ip = 0; + ip_flag = ipi_at_a2; + return true; + } + else if (equals_point_point(a2, b2)) + { + lon = a2_lon; + lat = a2_lat; + dist_a1_ip = dist_a1_a2; + dist_b1_ip = dist_b1_b2; + ip_flag = ipi_at_a2; + return true; + } + + // at this point we know that the endpoints doesn't overlap + // check cases when an endpoint lies on the other geodesic + if (sides.template get<0, 0>() == 0) // a1 wrt b + { + if (res_b1_a1.distance <= res_b1_b2.distance + && same_direction(res_b1_a1.azimuth, res_b1_b2.azimuth)) + { + lon = a1_lon; + lat = a1_lat; + dist_a1_ip = 0; + dist_b1_ip = res_b1_a1.distance; + ip_flag = ipi_at_a1; + return true; + } + else + { + return false; + } + } + else if (sides.template get<0, 1>() == 0) // a2 wrt b + { + if (res_b1_a2.distance <= res_b1_b2.distance + && same_direction(res_b1_a2.azimuth, res_b1_b2.azimuth)) + { + lon = a2_lon; + lat = a2_lat; + dist_a1_ip = res_a1_a2.distance; + dist_b1_ip = res_b1_a2.distance; + ip_flag = ipi_at_a2; + return true; + } + else + { + return false; + } + } + else if (sides.template get<1, 0>() == 0) // b1 wrt a + { + if (res_a1_b1.distance <= res_a1_a2.distance + && same_direction(res_a1_b1.azimuth, res_a1_a2.azimuth)) + { + lon = b1_lon; + lat = b1_lat; + dist_a1_ip = res_a1_b1.distance; + dist_b1_ip = 0; + ip_flag = ipi_at_b1; + return true; + } + else + { + return false; + } + } + else if (sides.template get<1, 1>() == 0) // b2 wrt a + { + if (res_a1_b2.distance <= res_a1_a2.distance + && same_direction(res_a1_b2.azimuth, res_a1_a2.azimuth)) + { + lon = b2_lon; + lat = b2_lat; + dist_a1_ip = res_a1_b2.distance; + dist_b1_ip = res_b1_b2.distance; + ip_flag = ipi_at_b2; + return true; + } + else + { + return false; + } + } + + // At this point neither the endpoints overlaps + // nor any andpoint lies on the other geodesic + // So the endpoints should lie on the opposite sides of both geodesics + + bool const ok = formula::sjoberg_intersection::apply( + a1_lon, a1_lat, a2_lon, a2_lat, res_a1_a2.azimuth, + b1_lon, b1_lat, b2_lon, b2_lat, res_b1_b2.azimuth, + lon, lat, spheroid); + + if (! ok) + { + return false; + } + + // TODO: it's theoretically possible to generate the IP outside the segment, + // i.e. the azimuth of the IP should be "smaller" than the azimuth of the endpoints + // i.e. the sides has to match, e.g. if one side is -1 and the other is 0 + // then the side of the IP has to be -1 or 0 as well + + // TODO: consider swapping points at the beginning if lon1 > lon2 + + typedef Inverse inverse_dist_azi; + typedef typename inverse_dist_azi::result_type inverse_result; + + inverse_result const res_a1_ip = inverse_dist_azi::apply(a1_lon, a1_lat, lon, lat, spheroid); + dist_a1_ip = res_a1_ip.distance; + if (! same_direction(res_a1_ip.azimuth, res_a1_a2.azimuth)) + { + dist_a1_ip = -dist_a1_ip; + } + + bool is_on_a = segment_ratio(dist_a1_ip, dist_a1_a2).on_segment(); + // NOTE: not fully consistent with equals_point_point() since radians are always used. + bool is_on_a1 = math::equals(lon, a1_lon) && math::equals(lat, a1_lat); + bool is_on_a2 = math::equals(lon, a2_lon) && math::equals(lat, a2_lat); + + if (! (is_on_a || is_on_a1 || is_on_a2)) + { + return false; + } + + inverse_result const res_b1_ip = inverse_dist_azi::apply(b1_lon, b1_lat, lon, lat, spheroid); + dist_b1_ip = res_b1_ip.distance; + if (! same_direction(res_b1_ip.azimuth, res_b1_b2.azimuth)) + { + dist_b1_ip = -dist_b1_ip; + } + + bool is_on_b = segment_ratio(dist_b1_ip, dist_b1_b2).on_segment(); + // NOTE: not fully consistent with equals_point_point() since radians are always used. + bool is_on_b1 = math::equals(lon, b1_lon) && math::equals(lat, b1_lat); + bool is_on_b2 = math::equals(lon, b2_lon) && math::equals(lat, b2_lat); + + if (! (is_on_b || is_on_b1 || is_on_b2)) + { + return false; + } + + ip_flag = ipi_inters; + + if (is_on_b1) + { + lon = b1_lon; + lat = b1_lat; + dist_b1_ip = 0; + ip_flag = ipi_at_b1; + } + else if (is_on_b2) + { + lon = b2_lon; + lat = b2_lat; + dist_b1_ip = res_b1_b2.distance; + ip_flag = ipi_at_b2; + } + + if (is_on_a1) + { + lon = a1_lon; + lat = a1_lat; + dist_a1_ip = 0; + ip_flag = ipi_at_a1; + } + else if (is_on_a2) + { + lon = a2_lon; + lat = a2_lat; + dist_a1_ip = res_a1_a2.distance; + ip_flag = ipi_at_a2; + } + + return true; + } + + template + static inline bool is_endpoint_equal(CalcT const& dist, + P1 const& ai, P2 const& b1, P2 const& b2) + { + using geometry::detail::equals::equals_point_point; + return is_near(dist) && (equals_point_point(ai, b1) || equals_point_point(ai, b2)); + } + + template + static inline bool is_near(CalcT const& dist) + { + // NOTE: This strongly depends on the Inverse method + CalcT const small_number = CalcT(boost::is_same::value ? 0.0001 : 0.00000001); + return math::abs(dist) <= small_number; + } + + template + static inline int position_value(ProjCoord1 const& ca1, + ProjCoord2 const& cb1, + ProjCoord2 const& cb2) + { + // S1x 0 1 2 3 4 + // S2 |----------> + return math::equals(ca1, cb1) ? 1 + : math::equals(ca1, cb2) ? 3 + : cb1 < cb2 ? + ( ca1 < cb1 ? 0 + : ca1 > cb2 ? 4 + : 2 ) + : ( ca1 > cb1 ? 0 + : ca1 < cb2 ? 4 + : 2 ); + } + + template + static inline bool same_direction(CalcT const& azimuth1, CalcT const& azimuth2) + { + // distance between two angles normalized to (-180, 180] + CalcT const angle_diff = math::longitude_distance_signed(azimuth1, azimuth2); + return math::abs(angle_diff) <= math::half_pi(); + } +}; + + +}} // namespace strategy::intersection + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_GEODESIC_INTERSECTION_HPP From ad1df7325cddba378b7c218c91855f18c8c565fe Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 6 Dec 2016 00:25:42 +0100 Subject: [PATCH 26/59] [test][strategies] Update the tests of geographic intersection strategies. --- test/strategies/segment_intersection_geo.cpp | 297 ++++++++---------- test/strategies/segment_intersection_geo.hpp | 298 +++++++++++++++++++ test/strategies/segment_intersection_sph.cpp | 1 + test/strategies/segment_intersection_sph.hpp | 18 +- 4 files changed, 447 insertions(+), 167 deletions(-) create mode 100644 test/strategies/segment_intersection_geo.hpp diff --git a/test/strategies/segment_intersection_geo.cpp b/test/strategies/segment_intersection_geo.cpp index 563b0075d..d2adbafa5 100644 --- a/test/strategies/segment_intersection_geo.cpp +++ b/test/strategies/segment_intersection_geo.cpp @@ -9,343 +9,308 @@ // http://www.boost.org/LICENSE_1_0.txt) -#include "segment_intersection_sph.hpp" +#include "segment_intersection_geo.hpp" -#include - - -template -void test_default_strategy(std::string const& s1_wkt, std::string const& s2_wkt, - char m, std::size_t expected_count, - std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") -{ - typedef typename bg::coordinate_type

::type coord_t; - typedef bg::policies::relate::segments_tupled - < - bg::policies::relate::segments_intersection_points - < - bg::segment_intersection_points > - >, - bg::policies::relate::segments_direction - > policy_t; - - typename bg::strategy::intersection::services::default_strategy - < - bg::geographic_tag, - policy_t, - void - >::type strategy; - - test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); -} - -template -void test_great_elliptic_strategy(std::string const& s1_wkt, std::string const& s2_wkt, - char m, std::size_t expected_count, - std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") -{ - typedef typename bg::coordinate_type

::type coord_t; - typedef bg::policies::relate::segments_tupled - < - bg::policies::relate::segments_intersection_points - < - bg::segment_intersection_points > - >, - bg::policies::relate::segments_direction - > policy_t; - - bg::strategy::intersection::relate_great_elliptic_segments - < - policy_t - > strategy; - - test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); -} - -template -void test_experimental_elliptic_strategy(std::string const& s1_wkt, std::string const& s2_wkt, - char m, std::size_t expected_count, - std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") -{ - typedef typename bg::coordinate_type

::type coord_t; - typedef bg::policies::relate::segments_tupled - < - bg::policies::relate::segments_intersection_points - < - bg::segment_intersection_points > - >, - bg::policies::relate::segments_direction - > policy_t; - - bg::strategy::intersection::relate_experimental_elliptic_segments - < - policy_t - > strategy; - - test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); -} - -template -void test_geographic_strategies(std::string const& s1_wkt, std::string const& s2_wkt, - char m, std::size_t expected_count, - std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") -{ - test_default_strategy(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); - test_great_elliptic_strategy(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); - test_experimental_elliptic_strategy(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); -} - template void test_geographic() { typedef bg::model::point > point_t; typedef bg::model::segment segment_t; + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -2, -1 -1)", 'a', 1, "POINT(-1 -1)"); + test_strategies( + "SEGMENT(-1 -2, -1 2)", "SEGMENT(-2 -2, 2 2)", + great_elliptic('i', 1, "POINT(-1 -1.000457053724121)"), + geodesic_vincenty('i', 1, "POINT(-1 -1.000459099991114)"), + geodesic_andoyer('i', 1, "POINT(-1 -1.000453510849886)")); + test_strategies( + "SEGMENT(-2 -2, 2 2)", "SEGMENT(-1 -2, -1 2)", + great_elliptic('i', 1, "POINT(-1 -1.000457053724121)"), + geodesic_vincenty('i', 1, "POINT(-1 -1.000459099991114)"), + geodesic_andoyer('i', 1, "POINT(-1 -1.000453510849886)")); + // crossing X - test_geographic_strategies( - "SEGMENT(-45 -45, 45 45)", "SEGMENT(-45 45, 45 -45)", 'i', 1, "POINT(0 0)"); - test_geographic_strategies( - "SEGMENT(-45 -45, 45 45)", "SEGMENT(45 -45, -45 45)", 'i', 1, "POINT(0 0)"); - test_geographic_strategies( - "SEGMENT(45 45, -45 -45)", "SEGMENT(-45 45, 45 -45)", 'i', 1, "POINT(0 0)"); - test_geographic_strategies( - "SEGMENT(45 45, -45 -45)", "SEGMENT(45 -45, -45 45)", 'i', 1, "POINT(0 0)"); + test_strategies( + "SEGMENT(-45 -45, 45 45)", "SEGMENT(-45 45, 45 -45)", + great_elliptic('i', 1, "POINT(0 0)"), + geodesic_vincenty('i', 1, "POINT(0.00000000138009992627 0.00000000000000636110)"), + geodesic_andoyer('i', 1, "POINT(-0.0002480157764363963 0.00000000000000636111)")); + test_strategies( + "SEGMENT(-45 -45, 45 45)", "SEGMENT(45 -45, -45 45)", + great_elliptic('i', 1, "POINT(0 0)"), + geodesic_vincenty('i', 1, "POINT(0 -0.0000000019543337739)"), + geodesic_andoyer('i', 1, "POINT(0 0.0003512140328446071)")); + test_strategies( + "SEGMENT(45 45, -45 -45)", "SEGMENT(-45 45, 45 -45)", + great_elliptic('i', 1, "POINT(0 0)"), + geodesic_vincenty('i', 1, "POINT(0 0.0000000019543337739)"), + geodesic_andoyer('i', 1, "POINT(0 -0.0003512140328446071)")); + test_strategies( + "SEGMENT(45 45, -45 -45)", "SEGMENT(45 -45, -45 45)", + great_elliptic('i', 1, "POINT(0 0)"), + geodesic_vincenty('i', 1, "POINT(-0.00000000138009992627 -0.00000000000000636110)"), + geodesic_andoyer('i', 1, "POINT(0.0002480157764363963 -0.00000000000000636111)")); // crossing X - test_geographic_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 1, 1 -1)", 'i', 1, "POINT(0 0)"); - test_geographic_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(1 -1, -1 1)", 'i', 1, "POINT(0 0)"); - test_geographic_strategies( - "SEGMENT(1 1, -1 -1)", "SEGMENT(-1 1, 1 -1)", 'i', 1, "POINT(0 0)"); - test_geographic_strategies( - "SEGMENT(1 1, -1 -1)", "SEGMENT(1 -1, -1 1)", 'i', 1, "POINT(0 0)"); + test_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 1, 1 -1)", + great_elliptic('i', 1, "POINT(0 0)"), + geodesic_vincenty('i', 1, "POINT(0.00000000006265374667 0)"), + geodesic_andoyer('i', 1, "POINT(-0.00000557785856154582 0)")); + test_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(1 -1, -1 1)", + great_elliptic('i', 1, "POINT(0 0)"), + geodesic_vincenty('i', 1, "POINT(0.000000000000000596354 -0.0000000000626626779)"), + geodesic_andoyer('i', 1, "POINT(-0.000000000000000596354 0.0000055787431347552)")); + test_strategies( + "SEGMENT(1 1, -1 -1)", "SEGMENT(-1 1, 1 -1)", + great_elliptic('i', 1, "POINT(0 0)"), + geodesic_vincenty('i', 1, "POINT(-0.00000000000000059635 0.0000000000626626779)"), + geodesic_andoyer('i', 1, "POINT(0.000000000000000596354 -0.000005578743134557)")); + test_strategies( + "SEGMENT(1 1, -1 -1)", "SEGMENT(1 -1, -1 1)", + great_elliptic('i', 1, "POINT(0 0)"), + geodesic_vincenty('i', 1, "POINT(-0.00000000006265374 0)"), + geodesic_andoyer('i', 1, "POINT(0.00000557785856134 0)")); // equal // // - test_geographic_strategies( + test_all_strategies( "SEGMENT(-45 -45, 45 45)", "SEGMENT(-45 -45, 45 45)", 'e', 2, "POINT(-45 -45)", "POINT(45 45)"); // // - test_geographic_strategies( + test_all_strategies( "SEGMENT(-45 -45, 45 45)", "SEGMENT(45 45, -45 -45)", 'e', 2, "POINT(-45 -45)", "POINT(45 45)"); // starting outside s1 // / // | - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, -1 -1)", 'a', 1, "POINT(-1 -1)"); // / // /| - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, 0 0)", 'm', 1, "POINT(0 0)"); // /| // / | - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, 1 1)", 't', 1, "POINT(1 1)"); // |/ // /| - test_geographic_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, 2 2)", 'i', 1, "POINT(0 0)"); + test_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, 2 2)", + great_elliptic('i', 1, "POINT(0 0)"), + geodesic_vincenty('i', 1, "POINT(0.00000013628420059 0.00000013624239008)"), + geodesic_thomas('i', 1, "POINT(-0.00004079969079346 -0.00004078714535240)"), + geodesic_andoyer('i', 1, "POINT(-0.01217344899138908 -0.01216980051876599)")); // ------ // ------ - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, -1 0)", 'a', 1, "POINT(-1 0)"); // ------ // ------ - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, 0 0)", 'c', 2, "POINT(-1 0)", "POINT(0 0)"); // ------ // --------- - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, 1 0)", 'c', 2, "POINT(-1 0)", "POINT(1 0)"); // ------ // ------------ - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, 2 0)", 'c', 2, "POINT(-1 0)", "POINT(1 0)"); // starting at s1 // / // // - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, 0 0)", 'c', 2, "POINT(-1 -1)", "POINT(0 0)"); // // // // - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, 1 1)", 'e', 2, "POINT(-1 -1)", "POINT(1 1)"); // | / // |/ - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, 2 2)", 'f', 1, "POINT(-1 -1)"); // ------ // --- - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, 0 0)", 'c', 2, "POINT(-1 0)", "POINT(0 0)"); // ------ // ------ - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, 1 0)", 'e', 2, "POINT(-1 0)", "POINT(1 0)"); // ------ // --------- - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, 2 0)", 'c', 2, "POINT(-1 0)", "POINT(1 0)"); // starting inside // // // / - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(0 0, 1 1)", 'c', 2, "POINT(0 0)", "POINT(1 1)"); // |/ // / - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(0 0, 2 2)", 's', 1, "POINT(0 0)"); // ------ // --- - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 0, 1 0)", "SEGMENT(0 0, 1 0)", 'c', 2, "POINT(0 0)", "POINT(1 0)"); // ------ // ------ - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 0, 1 0)", "SEGMENT(0 0, 2 0)", 'c', 2, "POINT(0 0)", "POINT(1 0)"); // starting at p2 // | // / - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(1 1, 2 2)", 'a', 1, "POINT(1 1)"); // ------ // --- - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 0, 1 0)", "SEGMENT(1 0, 2 0)", 'a', 1, "POINT(1 0)"); // disjoint, crossing // / // | - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(-3 -3, -2 -2)", 'd', 0); // | // / - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(2 2, 3 3)", 'd', 0); // disjoint, collinear // ------ // ------ - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 0, 1 0)", "SEGMENT(-3 0, -2 0)", 'd', 0); // ------ // ------ - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 0, 1 0)", "SEGMENT(2 0, 3 0)", 'd', 0); // degenerated // / // * - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, -2 -2)", 'd', 0); // / // * - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, -1 -1)", '0', 1, "POINT(-1 -1)"); // / // * // / - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(0 0, 0 0)", '0', 1, "POINT(0 0)"); // * // / - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(1 1, 1 1)", '0', 1, "POINT(1 1)"); // * // / - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(2 2, 2 2)", 'd', 0); // similar to above, collinear // * ------ - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, -2 0)", 'd', 0); // *------ - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, -1 0)", '0', 1, "POINT(-1 0)"); // ---*--- - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 0, 1 0)", "SEGMENT(0 0, 0 0)", '0', 1, "POINT(0 0)"); // ------* - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 0, 1 0)", "SEGMENT(1 0, 1 0)", '0', 1, "POINT(1 0)"); // ------ * - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 0, 1 0)", "SEGMENT(2 0, 2 0)", 'd', 0); // Northern hemisphere // --- ------ - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 50, 1 50)", "SEGMENT(-3 50, -2 50)", 'd', 0); // ------ // --- - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, -1 50)", 'a', 1, "POINT(-1 50)"); // \/ // /\ (avoid multi-line comment) - test_default_strategy( - "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 0 50)", 'i', 1, "POINT(-0.5 50.0032229484023)"); - test_great_elliptic_strategy( - "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 0 50)", 'i', 1, "POINT(-0.5 50.0032229484023)"); - test_experimental_elliptic_strategy( - "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 0 50)", 'i', 1, "POINT(-0.5 50.0032319221120)"); + test_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 0 50)", + great_elliptic('i', 1, "POINT(-0.5 50.0032229484023)"), + geodesic_vincenty('i', 1, "POINT(-0.4999999996073994 50.00323192256208)"), + geodesic_thomas('i', 1, "POINT(-0.4999999963998482 50.00323192258598)"), + geodesic_andoyer('i', 1, "POINT(-0.4999990340391772 50.00323192463806)")); // ________ // / _____\ (avoid multi-line comment) - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 1 50)", 't', 1, "POINT(1 50)"); // _________ // / _____ \ (avoid multi-line comment) - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 2 50)", 'd', 0); // ______ // /___ \ (avoid multi-line comment) - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 50, 1 50)", "SEGMENT(-1 50, 0 50)", 'f', 1, "POINT(-1 50)"); // ------ // ------ - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 50, 1 50)", "SEGMENT(-1 50, 1 50)", 'e', 2, "POINT(-1 50)", "POINT(1 50)"); // ________ // /_____ \ (avoid multi-line comment) - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 50, 1 50)", "SEGMENT(-1 50, 2 50)", 'f', 1, "POINT(-1 50)"); // ______ // / ___\ (avoid multi-line comment) - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 50, 1 50)", "SEGMENT(0 50, 1 50)", 't', 1, "POINT(1 50)"); // \/ // /\ (avoid multi-line comment) - test_default_strategy( - "SEGMENT(-1 50, 1 50)", "SEGMENT(0 50, 2 50)", 'i', 1, "POINT(0.5 50.0032229484023)"); - test_great_elliptic_strategy( - "SEGMENT(-1 50, 1 50)", "SEGMENT(0 50, 2 50)", 'i', 1, "POINT(0.5 50.0032229484023)"); - test_experimental_elliptic_strategy( - "SEGMENT(-1 50, 1 50)", "SEGMENT(0 50, 2 50)", 'i', 1, "POINT(0.5 50.0032319221120)"); + test_strategies( + "SEGMENT(-1 50, 1 50)", "SEGMENT(0 50, 2 50)", + great_elliptic('i', 1, "POINT(0.5 50.0032229484023)"), + geodesic_vincenty('i', 1, "POINT(0.4999999996112415 50.00323192256208)"), + geodesic_thomas('i', 1, "POINT(0.5000000051005989 50.00323192258598)"), + geodesic_andoyer('i', 1, "POINT(0.5000009655139563 50.00323192463806)")); // ------ // --- - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 50, 1 50)", "SEGMENT(1 50, 2 50)", 'a', 1, "POINT(1 50)"); // ------ --- - test_geographic_strategies( + test_all_strategies( "SEGMENT(-1 50, 1 50)", "SEGMENT(2 50, 3 50)", 'd', 0); // ___| - test_geographic_strategies( + test_all_strategies( "SEGMENT(0 0, 1 0)", "SEGMENT(1 0, 1 1)", 'a', 1, "POINT(1 0)"); // ___| - test_geographic_strategies( + test_all_strategies( "SEGMENT(1 0, 1 1)", "SEGMENT(0 0, 1 0)", 'a', 1, "POINT(1 0)"); // |/ // /| - test_geographic_strategies( - "SEGMENT(10 -1, 20 1)", "SEGMENT(12.5 -1, 12.5 1)", 'i', 1, "POINT(12.5 -0.50051443471392)"); + test_strategies( + "SEGMENT(10 -1, 20 1)", "SEGMENT(12.5 -1, 12.5 1)", + great_elliptic('i', 1, "POINT(12.5 -0.50051443471392)"), + geodesic_vincenty('i', 1, "POINT(12.5 -0.5005177749487734)"), + geodesic_thomas('i', 1, "POINT(12.5 -0.5005177654827411)"), + geodesic_andoyer('i', 1, "POINT(12.5 -0.500525380045163)")); // |/ // /| - test_geographic_strategies( - "SEGMENT(10 -1, 20 1)", "SEGMENT(17.5 -1, 17.5 1)", 'i', 1, "POINT(17.5 0.50051443471392)"); + test_strategies( + "SEGMENT(10 -1, 20 1)", "SEGMENT(17.5 -1, 17.5 1)", + great_elliptic('i', 1, "POINT(17.5 0.50051443471392)"), + geodesic_vincenty('i', 1, "POINT(17.5 0.5005177748229335)"), + geodesic_thomas('i', 1, "POINT(17.5 0.5005178030678186)"), + geodesic_andoyer('i', 1, "POINT(17.5 0.5004949944844279)")); } int test_main(int, char* []) diff --git a/test/strategies/segment_intersection_geo.hpp b/test/strategies/segment_intersection_geo.hpp new file mode 100644 index 000000000..23aa17718 --- /dev/null +++ b/test/strategies/segment_intersection_geo.hpp @@ -0,0 +1,298 @@ +// Boost.Geometry +// Unit Test + +// Copyright (c) 2016, Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_TEST_STRATEGIES_SEGMENT_INTERSECTION_GEO_HPP +#define BOOST_GEOMETRY_TEST_STRATEGIES_SEGMENT_INTERSECTION_GEO_HPP + + +#include "segment_intersection_sph.hpp" + + +#include +#include +#include + +#include +#include + + +template +void test_default_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") +{ + typedef typename bg::coordinate_type

::type coord_t; + typedef bg::policies::relate::segments_tupled + < + bg::policies::relate::segments_intersection_points + < + bg::segment_intersection_points > + >, + bg::policies::relate::segments_direction + > policy_t; + + typename bg::strategy::intersection::services::default_strategy + < + bg::geographic_tag, + policy_t, + void + >::type strategy; + + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); +} + +template +void test_great_elliptic(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") +{ + typedef typename bg::coordinate_type

::type coord_t; + typedef bg::policies::relate::segments_tupled + < + bg::policies::relate::segments_intersection_points + < + bg::segment_intersection_points > + >, + bg::policies::relate::segments_direction + > policy_t; + + bg::strategy::intersection::relate_great_elliptic_segments + < + policy_t + > strategy; + + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); +} +/* +template +void test_experimental_elliptic(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") +{ + typedef typename bg::coordinate_type

::type coord_t; + typedef bg::policies::relate::segments_tupled + < + bg::policies::relate::segments_intersection_points + < + bg::segment_intersection_points > + >, + bg::policies::relate::segments_direction + > policy_t; + + bg::strategy::intersection::relate_experimental_elliptic_segments + < + policy_t + > strategy; + + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); +} +*/ +template +void test_geodesic_vincenty(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") +{ + typedef typename bg::coordinate_type

::type coord_t; + typedef bg::policies::relate::segments_tupled + < + bg::policies::relate::segments_intersection_points + < + bg::segment_intersection_points > + >, + bg::policies::relate::segments_direction + > policy_t; + + bg::strategy::intersection::relate_geodesic_segments + < + policy_t, + bg::srs::spheroid, + bg::formula::vincenty_inverse, + 4 + > strategy; + + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); +} + +template +void test_geodesic_thomas(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") +{ + typedef typename bg::coordinate_type

::type coord_t; + typedef bg::policies::relate::segments_tupled + < + bg::policies::relate::segments_intersection_points + < + bg::segment_intersection_points > + >, + bg::policies::relate::segments_direction + > policy_t; + + bg::strategy::intersection::relate_geodesic_segments + < + policy_t, + bg::srs::spheroid, + bg::formula::thomas_inverse, + 2 + > strategy; + + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); +} + +template +void test_geodesic_andoyer(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") +{ + typedef typename bg::coordinate_type

::type coord_t; + typedef bg::policies::relate::segments_tupled + < + bg::policies::relate::segments_intersection_points + < + bg::segment_intersection_points > + >, + bg::policies::relate::segments_direction + > policy_t; + + bg::strategy::intersection::relate_geodesic_segments + < + policy_t, + bg::srs::spheroid, + bg::formula::andoyer_inverse, + 1 + > strategy; + + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); +} + + +struct strategy_base +{ + strategy_base(char m_, std::size_t expected_count_, std::string const& wkt1_ = "", std::string const& wkt2_ = "") + : m(m_), expected_count(expected_count_), wkt1(wkt1_), wkt2(wkt2_) + {} + char m; + std::size_t expected_count; + std::string wkt1, wkt2; +}; +struct strategy_default : strategy_base +{ + strategy_default(char m, std::size_t expected_count, std::string const& wkt1 = "", std::string const& wkt2 = "") + : strategy_base(m, expected_count, wkt1, wkt2) + {} +}; +struct geodesic_vincenty : strategy_base +{ + geodesic_vincenty(char m, std::size_t expected_count, std::string const& wkt1 = "", std::string const& wkt2 = "") + : strategy_base(m, expected_count, wkt1, wkt2) + {} +}; +struct geodesic_thomas : strategy_base +{ + geodesic_thomas(char m, std::size_t expected_count, std::string const& wkt1 = "", std::string const& wkt2 = "") + : strategy_base(m, expected_count, wkt1, wkt2) + {} +}; +struct geodesic_andoyer : strategy_base +{ + geodesic_andoyer(char m, std::size_t expected_count, std::string const& wkt1 = "", std::string const& wkt2 = "") + : strategy_base(m, expected_count, wkt1, wkt2) + {} +}; +struct great_elliptic : strategy_base +{ + great_elliptic(char m, std::size_t expected_count, std::string const& wkt1 = "", std::string const& wkt2 = "") + : strategy_base(m, expected_count, wkt1, wkt2) + {} +}; + + +template +void test_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + strategy_default const& s) +{ + test_default_strategy(s1_wkt, s2_wkt, s.m, s.expected_count, s.wkt1, s.wkt2); +} + +template +void test_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + great_elliptic const& s) +{ + test_great_elliptic(s1_wkt, s2_wkt, s.m, s.expected_count, s.wkt1, s.wkt2); +} + +template +void test_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + geodesic_vincenty const& s) +{ + test_geodesic_vincenty(s1_wkt, s2_wkt, s.m, s.expected_count, s.wkt1, s.wkt2); +} + +template +void test_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + geodesic_thomas const& s) +{ + test_geodesic_thomas(s1_wkt, s2_wkt, s.m, s.expected_count, s.wkt1, s.wkt2); +} + +template +void test_strategy(std::string const& s1_wkt, std::string const& s2_wkt, + geodesic_andoyer const& s) +{ + test_geodesic_andoyer(s1_wkt, s2_wkt, s.m, s.expected_count, s.wkt1, s.wkt2); +} + + +template +void test_strategies(std::string const& s1_wkt, std::string const& s2_wkt, + SR1 const& sr1) +{ + test_strategy(s1_wkt, s2_wkt, sr1); +} +template +void test_strategies(std::string const& s1_wkt, std::string const& s2_wkt, + SR1 const& sr1, SR2 const& sr2) +{ + test_strategy(s1_wkt, s2_wkt, sr1); + test_strategy(s1_wkt, s2_wkt, sr2); +} +template +void test_strategies(std::string const& s1_wkt, std::string const& s2_wkt, + SR1 const& sr1, SR2 const& sr2, SR3 const& sr3) +{ + test_strategy(s1_wkt, s2_wkt, sr1); + test_strategy(s1_wkt, s2_wkt, sr2); + test_strategy(s1_wkt, s2_wkt, sr3); +} +template +void test_strategies(std::string const& s1_wkt, std::string const& s2_wkt, + SR1 const& sr1, SR2 const& sr2, SR3 const& sr3, SR4 const& sr4) +{ + test_strategy(s1_wkt, s2_wkt, sr1); + test_strategy(s1_wkt, s2_wkt, sr2); + test_strategy(s1_wkt, s2_wkt, sr3); + test_strategy(s1_wkt, s2_wkt, sr4); +} + + +template +void test_all_strategies(std::string const& s1_wkt, std::string const& s2_wkt, + char m, std::size_t expected_count, + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") +{ + test_default_strategy(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); + test_great_elliptic(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); + //test_experimental_elliptic(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); + test_geodesic_vincenty(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); + test_geodesic_thomas(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); + test_geodesic_andoyer(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); +} + +#endif // BOOST_GEOMETRY_TEST_STRATEGIES_SEGMENT_INTERSECTION_GEO_HPP diff --git a/test/strategies/segment_intersection_sph.cpp b/test/strategies/segment_intersection_sph.cpp index 9d9e7cdc2..5161d9f15 100644 --- a/test/strategies/segment_intersection_sph.cpp +++ b/test/strategies/segment_intersection_sph.cpp @@ -9,6 +9,7 @@ // http://www.boost.org/LICENSE_1_0.txt) +#define BOOST_GEOMETRY_TEST_GEO_INTERSECTION_TEST_SIMILAR #include "segment_intersection_sph.hpp" diff --git a/test/strategies/segment_intersection_sph.hpp b/test/strategies/segment_intersection_sph.hpp index b3a6dc067..aa3e5e1ee 100644 --- a/test/strategies/segment_intersection_sph.hpp +++ b/test/strategies/segment_intersection_sph.hpp @@ -73,7 +73,19 @@ void test_strategy_one(S1 const& s1, S2 const& s2, // to cartesian 3d the same way which results in a different intersection point // For more information read the info in spherical intersection strategy file. - int eps_scale = res_method != 'i' ? 1 : 1000; + // Plus in geographic CS the result strongly depends on the compiler, + // probably due to differences in FP trigonometric function implementations + + int eps_scale = 1; + bool is_geographic = boost::is_same::type, bg::geographic_tag>::value; + if (is_geographic) + { + eps_scale = 100000; + } + else + { + eps_scale = res_method != 'i' ? 1 : 1000; + } if (res_count > 0 && expected_count > 0) { @@ -111,6 +123,9 @@ void test_strategy(S1 const& s1, S2 const& s2, P ip0t = ip0; P ip1t = ip1; +#ifndef BOOST_GEOMETRY_TEST_GEO_INTERSECTION_TEST_SIMILAR + test_strategy_one(s1t, s2t, strategy, m, expected_count, ip0t, ip1t); +#else double t = 0; for (int i = 0; i < 4; ++i, t += 90) { @@ -125,6 +140,7 @@ void test_strategy(S1 const& s1, S2 const& s2, test_strategy_one(s1t, s2t, strategy, m, expected_count, ip0t, ip1t); } +#endif } template From b9f6bc73324d4f132f24dcca1f65e0ae5ddc6c24 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 7 Dec 2016 13:59:36 +0100 Subject: [PATCH 27/59] [formulas] Fix warnings on Clang. --- include/boost/geometry/formulas/geographic.hpp | 2 +- include/boost/geometry/formulas/sjoberg_intersection.hpp | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/include/boost/geometry/formulas/geographic.hpp b/include/boost/geometry/formulas/geographic.hpp index e98ac3b58..f6feb6663 100644 --- a/include/boost/geometry/formulas/geographic.hpp +++ b/include/boost/geometry/formulas/geographic.hpp @@ -168,7 +168,7 @@ inline Point3d projected_to_surface(Point3d const& direction, Spheroid const& sp { typedef typename coordinate_type::type coord_t; - coord_t const c0 = 0; + //coord_t const c0 = 0; coord_t const c2 = 2; coord_t const c4 = 4; diff --git a/include/boost/geometry/formulas/sjoberg_intersection.hpp b/include/boost/geometry/formulas/sjoberg_intersection.hpp index 745c2968d..cf9e8c97b 100644 --- a/include/boost/geometry/formulas/sjoberg_intersection.hpp +++ b/include/boost/geometry/formulas/sjoberg_intersection.hpp @@ -1036,9 +1036,9 @@ private: bool check_sin_beta = true) { bool ok = converge_07_one_geod(sin_beta, t, geodesics.geod1, geodesics.vertex1, lon_sph, - result.lon1, result.k1_diff, check_sin_beta, 1) + result.lon1, result.k1_diff, check_sin_beta) && converge_07_one_geod(sin_beta, t, geodesics.geod2, geodesics.vertex2, lon_sph, - result.lon2, result.k2_diff, check_sin_beta, 2); + result.lon2, result.k2_diff, check_sin_beta); if (!ok) { @@ -1059,8 +1059,7 @@ private: typename geodesic_type::vertex_data const& vertex, CT const& lon_sph, CT & lon, CT & k_diff, - bool check_sin_beta, - int id) // TEMP + bool check_sin_beta) { using math::detail::bounded; CT const c1 = 1; From 3abef7e1ea49e451a1179622a6819a2b859f79d3 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 7 Dec 2016 14:00:28 +0100 Subject: [PATCH 28/59] [strategies] Fix a small error on Clang. --- .../boost/geometry/strategies/spherical/intersection.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/boost/geometry/strategies/spherical/intersection.hpp b/include/boost/geometry/strategies/spherical/intersection.hpp index b1333c464..9ce6f5405 100644 --- a/include/boost/geometry/strategies/spherical/intersection.hpp +++ b/include/boost/geometry/strategies/spherical/intersection.hpp @@ -170,9 +170,10 @@ struct relate_ecef_segments Point1 const& a1, Point1 const& a2, Point2 const& b1, Point2 const& b2) { // For now create it using default constructor. In the future it could - // be stored in strategy. However then apply() wouldn't be static and - // all relops and setops would have to take the strategy or model. - CalcPolicy const calc_policy; + // be stored in strategy. However then apply() wouldn't be static and + // all relops and setops would have to take the strategy or model. + // Initialize explicitly to prevent compiler errors in case of PoD type + CalcPolicy const calc_policy = CalcPolicy(); BOOST_CONCEPT_ASSERT( (concepts::ConstSegment) ); BOOST_CONCEPT_ASSERT( (concepts::ConstSegment) ); From 88ee4250f0da9925bab3cb1dabe7f8962cc39bff Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 11 Dec 2016 01:37:55 +0100 Subject: [PATCH 29/59] [strategies] Improve robustness of geodesic intersection strategy. Reverse segments before analysing them if needed. Make sure the segments are going from S to N. This way the intersection point calculated by Sjoberg's intersection formula is always the same for equal but reversed segments. The latitude is taken into account because this way the accuracy of result's longitudes is greater. --- .../geographic/geodesic_intersection.hpp | 212 +++++++++++++----- 1 file changed, 151 insertions(+), 61 deletions(-) diff --git a/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp b/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp index 7d502978c..d05855df6 100644 --- a/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp +++ b/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp @@ -47,14 +47,10 @@ namespace boost { namespace geometry namespace strategy { namespace intersection { -//TODO: Improve the robustness/accuracy/repeatability by +// CONSIDER: Improvement of the robustness/accuracy/repeatability by // moving all segments to 0 longitude -// switching the segments -// switching the endpoints -// BECAUSE: the result calculated by sjoberg intersection strategy -// is not consistent when the above are altered -// worse than that, the error is a lot greater either for lon or lat -// so make it consistently greater for lat +// picking latitudes closer to 0 +// etc. template < @@ -70,7 +66,6 @@ struct relate_geodesic_segments enum intersection_point_flag { ipi_inters = 0, ipi_at_a1, ipi_at_a2, ipi_at_b1, ipi_at_b2 }; - // segment_intersection_info cannot outlive relate_ecef_segments template struct segment_intersection_info { @@ -144,7 +139,6 @@ struct relate_geodesic_segments point1_t a1, a2; point2_t b1, b2; - // TODO: use indexed_point_view if possible? detail::assign_point_from_index<0>(a, a1); detail::assign_point_from_index<1>(a, a2); detail::assign_point_from_index<0>(b, b1); @@ -157,7 +151,30 @@ struct relate_geodesic_segments template static inline return_type apply(Segment1 const& a, Segment2 const& b, RobustPolicy const&, - Point1 const& a1, Point1 const& a2, Point2 const& b1, Point2 const& b2) + Point1 a1, Point1 a2, Point2 b1, Point2 b2) + { + bool is_a_reversed = get<1>(a1) > get<1>(a2); + bool is_b_reversed = get<1>(b1) > get<1>(b2); + + if (is_a_reversed) + { + std::swap(a1, a2); + } + + if (is_b_reversed) + { + std::swap(b1, b2); + } + + return apply(a, b, a1, a2, b1, b2, is_a_reversed, is_b_reversed); + } + +private: + // Relate segments a and b + template + static inline return_type apply(Segment1 const& a, Segment2 const& b, + Point1 const& a1, Point1 const& a2, Point2 const& b1, Point2 const& b2, + bool is_a_reversed, bool is_b_reversed) { BOOST_CONCEPT_ASSERT( (concepts::ConstSegment) ); BOOST_CONCEPT_ASSERT( (concepts::ConstSegment) ); @@ -261,11 +278,11 @@ struct relate_geodesic_segments { if (a_is_point) { - return collinear_one_degenerted(a, true, b1, b2, a1, a2, res_b1_b2, res_b1_a1); + return collinear_one_degenerted(a, true, b1, b2, a1, a2, res_b1_b2, res_b1_a1, is_b_reversed); } else if (b_is_point) { - return collinear_one_degenerted(b, false, a1, a2, b1, b2, res_a1_a2, res_a1_b1); + return collinear_one_degenerted(b, false, a1, a2, b1, b2, res_a1_a2, res_a1_b1, is_a_reversed); } else { @@ -289,50 +306,77 @@ struct relate_geodesic_segments dist_a1_b2 = dist_b1_b2 - dist_b1_a1; } - segment_ratio ra_from(dist_b1_a1, dist_b1_b2); - segment_ratio ra_to(dist_b1_a2, dist_b1_b2); - segment_ratio rb_from(dist_a1_b1, dist_a1_a2); - segment_ratio rb_to(dist_a1_b2, dist_a1_a2); - - calc_t const c0 = 0; - // NOTE: this is probably not needed - int const a1_wrt_b = position_value(c0, dist_a1_b1, dist_a1_b2); - int const a2_wrt_b = position_value(dist_a1_a2, dist_a1_b1, dist_a1_b2); - int const b1_wrt_a = position_value(c0, dist_b1_a1, dist_b1_a2); - int const b2_wrt_a = position_value(dist_b1_b2, dist_b1_a1, dist_b1_a2); + calc_t const c0 = 0; + int a1_on_b = position_value(c0, dist_a1_b1, dist_a1_b2); + int a2_on_b = position_value(dist_a1_a2, dist_a1_b1, dist_a1_b2); + int b1_on_a = position_value(c0, dist_b1_a1, dist_b1_a2); + int b2_on_a = position_value(dist_b1_b2, dist_b1_a1, dist_b1_a2); - if (a1_wrt_b == 1) - { - ra_from.assign(0, dist_b1_b2); - rb_from.assign(0, dist_a1_a2); - } - else if (a1_wrt_b == 3) - { - ra_from.assign(dist_b1_b2, dist_b1_b2); - rb_to.assign(0, dist_a1_a2); - } - - if (a2_wrt_b == 1) - { - ra_to.assign(0, dist_b1_b2); - rb_from.assign(dist_a1_a2, dist_a1_a2); - } - else if (a2_wrt_b == 3) - { - ra_to.assign(dist_b1_b2, dist_b1_b2); - rb_to.assign(dist_a1_a2, dist_a1_a2); - } - - if ((a1_wrt_b < 1 && a2_wrt_b < 1) || (a1_wrt_b > 3 && a2_wrt_b > 3)) + if ((a1_on_b < 1 && a2_on_b < 1) || (a1_on_b > 3 && a2_on_b > 3)) { return Policy::disjoint(); } - bool const opposite = ! same_direction(res_a1_a2.azimuth, res_b1_b2.azimuth); + if (a1_on_b == 1) + { + dist_b1_a1 = 0; + dist_a1_b1 = 0; + } + else if (a1_on_b == 3) + { + dist_b1_a1 = dist_b1_b2; + dist_a1_b2 = 0; + } + + if (a2_on_b == 1) + { + dist_b1_a2 = 0; + dist_a1_b1 = dist_a1_a2; + } + else if (a2_on_b == 3) + { + dist_b1_a2 = dist_b1_b2; + dist_a1_b2 = dist_a1_a2; + } + + bool opposite = ! same_direction(res_a1_a2.azimuth, res_b1_b2.azimuth); + + // NOTE: If segment was reversed opposite, positions and segment ratios has to be altered + if (is_a_reversed) + { + // opposite + opposite = ! opposite; + // positions + std::swap(a1_on_b, a2_on_b); + b1_on_a = 4 - b1_on_a; + b2_on_a = 4 - b2_on_a; + // distances for ratios + std::swap(dist_b1_a1, dist_b1_a2); + dist_a1_b1 = dist_a1_a2 - dist_a1_b1; + dist_a1_b2 = dist_a1_a2 - dist_a1_b2; + } + if (is_b_reversed) + { + // opposite + opposite = ! opposite; + // positions + a1_on_b = 4 - a1_on_b; + a2_on_b = 4 - a2_on_b; + std::swap(b1_on_a, b2_on_a); + // distances for ratios + dist_b1_a1 = dist_b1_b2 - dist_b1_a1; + dist_b1_a2 = dist_b1_b2 - dist_b1_a2; + std::swap(dist_a1_b1, dist_a1_b2); + } + + segment_ratio ra_from(dist_b1_a1, dist_b1_b2); + segment_ratio ra_to(dist_b1_a2, dist_b1_b2); + segment_ratio rb_from(dist_a1_b1, dist_a1_a2); + segment_ratio rb_to(dist_a1_b2, dist_a1_a2); return Policy::segments_collinear(a, b, opposite, - a1_wrt_b, a2_wrt_b, b1_wrt_a, b2_wrt_a, + a1_on_b, a2_on_b, b1_on_a, b2_on_a, ra_from, ra_to, rb_from, rb_to); } } @@ -356,6 +400,26 @@ struct relate_geodesic_segments dist_a1_a2, dist_a1_i1, dist_b1_b2, dist_b1_i1, ip_flag)) { + // NOTE: If segment was reversed sides and segment ratios has to be altered + if (is_a_reversed) + { + // sides + sides_reverse_segment<0>(sides); + // distance for ratio + dist_a1_i1 = dist_a1_a2 - dist_a1_i1; + // ip flag + ip_flag_reverse_segment(ip_flag, ipi_at_a1, ipi_at_a2); + } + if (is_b_reversed) + { + // sides + sides_reverse_segment<1>(sides); + // distance for ratio + dist_b1_i1 = dist_b1_b2 - dist_b1_i1; + // ip flag + ip_flag_reverse_segment(ip_flag, ipi_at_b1, ipi_at_b2); + } + // intersects segment_intersection_info < @@ -378,21 +442,32 @@ struct relate_geodesic_segments } } -private: template static inline return_type collinear_one_degenerted(Segment const& segment, bool degenerated_a, Point1 const& a1, Point1 const& a2, Point2 const& b1, Point2 const& b2, ResultInverse const& res_a1_a2, - ResultInverse const& res_a1_bi) + ResultInverse const& res_a1_bi, + bool is_other_reversed) { CalcT dist_1_2, dist_1_o; - return ! calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_bi, dist_1_2, dist_1_o) - ? Policy::disjoint() - : Policy::one_degenerate(segment, segment_ratio(dist_1_o, dist_1_2), degenerated_a); + if (! calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_bi, dist_1_2, dist_1_o)) + { + return Policy::disjoint(); + } + + // NOTE: If segment was reversed segment ratio has to be altered + if (is_other_reversed) + { + // distance for ratio + dist_1_o = dist_1_2 - dist_1_o; + } + + return Policy::one_degenerate(segment, segment_ratio(dist_1_o, dist_1_2), degenerated_a); } - // TODO: instead of the code below test bi against a1 and a2 here? + // TODO: instead of checks below test bi against a1 and a2 here? + // in order to make this independent from is_near() template static inline bool calculate_collinear_data(Point1 const& a1, Point1 const& a2, // in Point2 const& b1, Point2 const& b2, // in @@ -572,13 +647,6 @@ private: return false; } - // TODO: it's theoretically possible to generate the IP outside the segment, - // i.e. the azimuth of the IP should be "smaller" than the azimuth of the endpoints - // i.e. the sides has to match, e.g. if one side is -1 and the other is 0 - // then the side of the IP has to be -1 or 0 as well - - // TODO: consider swapping points at the beginning if lon1 > lon2 - typedef Inverse inverse_dist_azi; typedef typename inverse_dist_azi::result_type inverse_result; @@ -692,6 +760,28 @@ private: CalcT const angle_diff = math::longitude_distance_signed(azimuth1, azimuth2); return math::abs(angle_diff) <= math::half_pi(); } + + template + static inline void sides_reverse_segment(side_info & sides) + { + // names assuming segment A is reversed (Which == 0) + int a1_wrt_b = sides.template get(); + int a2_wrt_b = sides.template get(); + std::swap(a1_wrt_b, a2_wrt_b); + sides.template set(a1_wrt_b, a2_wrt_b); + int b1_wrt_a = sides.template get<1 - Which, 0>(); + int b2_wrt_a = sides.template get<1 - Which, 1>(); + sides.template set<1 - Which>(-b1_wrt_a, -b2_wrt_a); + } + + static inline void ip_flag_reverse_segment(intersection_point_flag & ip_flag, + intersection_point_flag const& ipi_at_p1, + intersection_point_flag const& ipi_at_p2) + { + ip_flag = ip_flag == ipi_at_p1 ? ipi_at_p2 : + ip_flag == ipi_at_p2 ? ipi_at_p1 : + ip_flag; + } }; From 726541fed8f29bc629ada064883ffbc9ab846171 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 11 Dec 2016 01:46:12 +0100 Subject: [PATCH 30/59] [test][strategies] Update the test cases for geodesic intersection strategy after robustness upgrades. --- test/strategies/segment_intersection_geo.cpp | 238 +++++++++++-------- test/strategies/segment_intersection_geo.hpp | 125 +++++++--- test/strategies/segment_intersection_sph.hpp | 35 ++- 3 files changed, 260 insertions(+), 138 deletions(-) diff --git a/test/strategies/segment_intersection_geo.cpp b/test/strategies/segment_intersection_geo.cpp index d2adbafa5..23c1c0d7e 100644 --- a/test/strategies/segment_intersection_geo.cpp +++ b/test/strategies/segment_intersection_geo.cpp @@ -19,298 +19,340 @@ void test_geographic() typedef bg::model::segment segment_t; test_all_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -2, -1 -1)", 'a', 1, "POINT(-1 -1)"); + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -2, -1 -1)", 'a', "POINT(-1 -1)"); + test_all_strategies( + "SEGMENT(-1 -2, 1 1)", "SEGMENT(-1 -2, -1 -1)", 'f', "POINT(-1 -2)"); + test_all_strategies( + "SEGMENT(1 1, -1 -1)", "SEGMENT(-1 -2, -1 -1)", 't', "POINT(-1 -1)"); + test_all_strategies( + "SEGMENT(1 1, -1 -2)", "SEGMENT(-1 -2, -1 -1)", 'a', "POINT(-1 -2)"); + test_all_strategies( + "SEGMENT(-1 -2, -1 -1)", "SEGMENT(-1 -1, 1 1)", 'a', "POINT(-1 -1)"); + test_all_strategies( + "SEGMENT(-1 -2, -1 -1)", "SEGMENT(-1 -2, 1 1)", 'f', "POINT(-1 -2)"); + test_all_strategies( + "SEGMENT(-1 -2, -1 -1)", "SEGMENT(1 1, -1 -1)", 't', "POINT(-1 -1)"); + test_all_strategies( + "SEGMENT(-1 -2, -1 -1)", "SEGMENT(1 1, -1 -2)", 'a', "POINT(-1 -2)"); + test_strategies( "SEGMENT(-1 -2, -1 2)", "SEGMENT(-2 -2, 2 2)", - great_elliptic('i', 1, "POINT(-1 -1.000457053724121)"), - geodesic_vincenty('i', 1, "POINT(-1 -1.000459099991114)"), - geodesic_andoyer('i', 1, "POINT(-1 -1.000453510849886)")); + great_elliptic('i', "POINT(-1 -1.000457053724121)"), + geodesic_vincenty('i', "POINT(-1 -1.000459099991114)"), + geodesic_andoyer('i', "POINT(-1 -1.000453510849886)")); test_strategies( "SEGMENT(-2 -2, 2 2)", "SEGMENT(-1 -2, -1 2)", - great_elliptic('i', 1, "POINT(-1 -1.000457053724121)"), - geodesic_vincenty('i', 1, "POINT(-1 -1.000459099991114)"), - geodesic_andoyer('i', 1, "POINT(-1 -1.000453510849886)")); + great_elliptic('i', "POINT(-1 -1.000457053724121)"), + geodesic_vincenty('i', "POINT(-1 -1.000459099991114)"), + geodesic_andoyer('i', "POINT(-1 -1.000453510849886)")); // crossing X test_strategies( "SEGMENT(-45 -45, 45 45)", "SEGMENT(-45 45, 45 -45)", - great_elliptic('i', 1, "POINT(0 0)"), - geodesic_vincenty('i', 1, "POINT(0.00000000138009992627 0.00000000000000636110)"), - geodesic_andoyer('i', 1, "POINT(-0.0002480157764363963 0.00000000000000636111)")); + great_elliptic('i', "POINT(0 0)"), + geodesic_vincenty('i', "POINT(0 -0.0000000019543337739)"), + geodesic_andoyer('i', "POINT(0 0.0003512140328446071)")); test_strategies( "SEGMENT(-45 -45, 45 45)", "SEGMENT(45 -45, -45 45)", - great_elliptic('i', 1, "POINT(0 0)"), - geodesic_vincenty('i', 1, "POINT(0 -0.0000000019543337739)"), - geodesic_andoyer('i', 1, "POINT(0 0.0003512140328446071)")); + great_elliptic('i', "POINT(0 0)"), + geodesic_vincenty('i', "POINT(0 -0.0000000019543337739)"), + geodesic_andoyer('i', "POINT(0 0.0003512140328446071)")); test_strategies( "SEGMENT(45 45, -45 -45)", "SEGMENT(-45 45, 45 -45)", - great_elliptic('i', 1, "POINT(0 0)"), - geodesic_vincenty('i', 1, "POINT(0 0.0000000019543337739)"), - geodesic_andoyer('i', 1, "POINT(0 -0.0003512140328446071)")); + great_elliptic('i', "POINT(0 0)"), + geodesic_vincenty('i', "POINT(0 -0.0000000019543337739)"), + geodesic_andoyer('i', "POINT(0 0.0003512140328446071)")); test_strategies( "SEGMENT(45 45, -45 -45)", "SEGMENT(45 -45, -45 45)", - great_elliptic('i', 1, "POINT(0 0)"), - geodesic_vincenty('i', 1, "POINT(-0.00000000138009992627 -0.00000000000000636110)"), - geodesic_andoyer('i', 1, "POINT(0.0002480157764363963 -0.00000000000000636111)")); + great_elliptic('i', "POINT(0 0)"), + geodesic_vincenty('i', "POINT(0 -0.0000000019543337739)"), + geodesic_andoyer('i', "POINT(0 0.0003512140328446071)")); + // crossing X test_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 1, 1 -1)", - great_elliptic('i', 1, "POINT(0 0)"), - geodesic_vincenty('i', 1, "POINT(0.00000000006265374667 0)"), - geodesic_andoyer('i', 1, "POINT(-0.00000557785856154582 0)")); + great_elliptic('i', "POINT(0 0)"), + geodesic_vincenty('i', "POINT(0.000000000000000596354 -0.0000000000626626779)"), + geodesic_andoyer('i', "POINT(-0.000000000000000596354 0.0000055787431347552)")); test_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(1 -1, -1 1)", - great_elliptic('i', 1, "POINT(0 0)"), - geodesic_vincenty('i', 1, "POINT(0.000000000000000596354 -0.0000000000626626779)"), - geodesic_andoyer('i', 1, "POINT(-0.000000000000000596354 0.0000055787431347552)")); + great_elliptic('i', "POINT(0 0)"), + geodesic_vincenty('i', "POINT(0.000000000000000596354 -0.0000000000626626779)"), + geodesic_andoyer('i', "POINT(-0.000000000000000596354 0.0000055787431347552)")); test_strategies( "SEGMENT(1 1, -1 -1)", "SEGMENT(-1 1, 1 -1)", - great_elliptic('i', 1, "POINT(0 0)"), - geodesic_vincenty('i', 1, "POINT(-0.00000000000000059635 0.0000000000626626779)"), - geodesic_andoyer('i', 1, "POINT(0.000000000000000596354 -0.000005578743134557)")); + great_elliptic('i', "POINT(0 0)"), + geodesic_vincenty('i', "POINT(0.000000000000000596354 -0.0000000000626626779)"), + geodesic_andoyer('i', "POINT(-0.000000000000000596354 0.0000055787431347552)")); test_strategies( "SEGMENT(1 1, -1 -1)", "SEGMENT(1 -1, -1 1)", - great_elliptic('i', 1, "POINT(0 0)"), - geodesic_vincenty('i', 1, "POINT(-0.00000000006265374 0)"), - geodesic_andoyer('i', 1, "POINT(0.00000557785856134 0)")); + great_elliptic('i', "POINT(0 0)"), + geodesic_vincenty('i', "POINT(0.000000000000000596354 -0.0000000000626626779)"), + geodesic_andoyer('i', "POINT(-0.000000000000000596354 0.0000055787431347552)")); // equal // // test_all_strategies( - "SEGMENT(-45 -45, 45 45)", "SEGMENT(-45 -45, 45 45)", 'e', 2, "POINT(-45 -45)", "POINT(45 45)"); + "SEGMENT(-45 -45, 45 45)", "SEGMENT(-45 -45, 45 45)", 'e', "POINT(-45 -45)", "POINT(45 45)", false); // // test_all_strategies( - "SEGMENT(-45 -45, 45 45)", "SEGMENT(45 45, -45 -45)", 'e', 2, "POINT(-45 -45)", "POINT(45 45)"); + "SEGMENT(-45 -45, 45 45)", "SEGMENT(45 45, -45 -45)", 'e', "POINT(-45 -45)", "POINT(45 45)", true); // starting outside s1 // / // | test_all_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, -1 -1)", 'a', 1, "POINT(-1 -1)"); + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, -1 -1)", 'a', "POINT(-1 -1)"); // / // /| test_all_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, 0 0)", 'm', 1, "POINT(0 0)"); + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, 0 0)", 'm', "POINT(0 0)"); // /| // / | test_all_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, 1 1)", 't', 1, "POINT(1 1)"); + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, 1 1)", 't', "POINT(1 1)"); // |/ // /| test_strategies( "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, 2 2)", - great_elliptic('i', 1, "POINT(0 0)"), - geodesic_vincenty('i', 1, "POINT(0.00000013628420059 0.00000013624239008)"), - geodesic_thomas('i', 1, "POINT(-0.00004079969079346 -0.00004078714535240)"), - geodesic_andoyer('i', 1, "POINT(-0.01217344899138908 -0.01216980051876599)")); + great_elliptic('i', "POINT(0 0)"), + geodesic_vincenty('i', "POINT(0.00000013628420059 0.00000013624239008)"), + geodesic_thomas('i', "POINT(-0.00004079969079346 -0.00004078714535240)"), + geodesic_andoyer('i', "POINT(-0.01217344899138908 -0.01216980051876599)")); // ------ // ------ test_all_strategies( - "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, -1 0)", 'a', 1, "POINT(-1 0)"); + "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, -1 0)", 'a', "POINT(-1 0)"); // ------ // ------ test_all_strategies( - "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, 0 0)", 'c', 2, "POINT(-1 0)", "POINT(0 0)"); + "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, 0 0)", 'c', "POINT(-1 0)", "POINT(0 0)", false); + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(0 0, -2 0)", 'c', "POINT(-1 0)", "POINT(0 0)", true); + test_all_strategies( + "SEGMENT(1 0, -1 0)", "SEGMENT(-2 0, 0 0)", 'c', "POINT(0 0)", "POINT(-1 0)", true); + test_all_strategies( + "SEGMENT(1 0, -1 0)", "SEGMENT(0 0, -2 0)", 'c', "POINT(0 0)", "POINT(-1 0)", false); // ------ // --------- test_all_strategies( - "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, 1 0)", 'c', 2, "POINT(-1 0)", "POINT(1 0)"); + "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, 1 0)", 'c', "POINT(-1 0)", "POINT(1 0)", false); + test_all_strategies( + "SEGMENT(-1 0, 1 0)", "SEGMENT(1 0, -2 0)", 'c', "POINT(-1 0)", "POINT(1 0)", true); + test_all_strategies( + "SEGMENT(1 0, -1 0)", "SEGMENT(-2 0, 1 0)", 'c', "POINT(1 0)", "POINT(-1 0)", true); + test_all_strategies( + "SEGMENT(1 0, -1 0)", "SEGMENT(1 0, -2 0)", 'c', "POINT(1 0)", "POINT(-1 0)", false); // ------ // ------------ test_all_strategies( - "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, 2 0)", 'c', 2, "POINT(-1 0)", "POINT(1 0)"); + "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, 2 0)", 'c', "POINT(-1 0)", "POINT(1 0)", false); // starting at s1 // / // // test_all_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, 0 0)", 'c', 2, "POINT(-1 -1)", "POINT(0 0)"); + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, 0 0)", 'c', "POINT(-1 -1)", "POINT(0 0)", false); // // // // test_all_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, 1 1)", 'e', 2, "POINT(-1 -1)", "POINT(1 1)"); + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, 1 1)", 'e', "POINT(-1 -1)", "POINT(1 1)", false); // | / // |/ test_all_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, 2 2)", 'f', 1, "POINT(-1 -1)"); + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, 2 2)", 'f', "POINT(-1 -1)"); // ------ // --- test_all_strategies( - "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, 0 0)", 'c', 2, "POINT(-1 0)", "POINT(0 0)"); + "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, 0 0)", 'c', "POINT(-1 0)", "POINT(0 0)", false); // ------ // ------ test_all_strategies( - "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, 1 0)", 'e', 2, "POINT(-1 0)", "POINT(1 0)"); + "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, 1 0)", 'e', "POINT(-1 0)", "POINT(1 0)", false); // ------ // --------- test_all_strategies( - "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, 2 0)", 'c', 2, "POINT(-1 0)", "POINT(1 0)"); + "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, 2 0)", 'c', "POINT(-1 0)", "POINT(1 0)", false); // starting inside // // // / test_all_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(0 0, 1 1)", 'c', 2, "POINT(0 0)", "POINT(1 1)"); + "SEGMENT(-1 -1, 1 1)", "SEGMENT(0 0, 1 1)", 'c', "POINT(0 0)", "POINT(1 1)", false); + test_all_strategies( + "SEGMENT(-1 -1, 1 1)", "SEGMENT(1 1, 0 0)", 'c', "POINT(0 0)", "POINT(1 1)", true); + test_all_strategies( + "SEGMENT(1 1, -1 -1)", "SEGMENT(0 0, 1 1)", 'c', "POINT(1 1)", "POINT(0 0)", true); + test_all_strategies( + "SEGMENT(1 1, -1 -1)", "SEGMENT(1 1, 0 0)", 'c', "POINT(1 1)", "POINT(0 0)", false); + test_all_strategies( + "SEGMENT(0 0, 1 1)", "SEGMENT(-1 -1, 1 1)", 'c', "POINT(0 0)", "POINT(1 1)", false); + test_all_strategies( + "SEGMENT(1 1, 0 0)", "SEGMENT(-1 -1, 1 1)", 'c', "POINT(1 1)", "POINT(0 0)", true); + test_all_strategies( + "SEGMENT(0 0, 1 1)", "SEGMENT(1 1, -1 -1)", 'c', "POINT(0 0)", "POINT(1 1)", true); + test_all_strategies( + "SEGMENT(1 1, 0 0)", "SEGMENT(1 1, -1 -1)", 'c', "POINT(1 1)", "POINT(0 0)", false); // |/ // / test_all_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(0 0, 2 2)", 's', 1, "POINT(0 0)"); + "SEGMENT(-1 -1, 1 1)", "SEGMENT(0 0, 2 2)", 's', "POINT(0 0)"); // ------ // --- test_all_strategies( - "SEGMENT(-1 0, 1 0)", "SEGMENT(0 0, 1 0)", 'c', 2, "POINT(0 0)", "POINT(1 0)"); + "SEGMENT(-1 0, 1 0)", "SEGMENT(0 0, 1 0)", 'c', "POINT(0 0)", "POINT(1 0)", false); // ------ // ------ test_all_strategies( - "SEGMENT(-1 0, 1 0)", "SEGMENT(0 0, 2 0)", 'c', 2, "POINT(0 0)", "POINT(1 0)"); + "SEGMENT(-1 0, 1 0)", "SEGMENT(0 0, 2 0)", 'c', "POINT(0 0)", "POINT(1 0)", false); // starting at p2 // | // / test_all_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(1 1, 2 2)", 'a', 1, "POINT(1 1)"); + "SEGMENT(-1 -1, 1 1)", "SEGMENT(1 1, 2 2)", 'a', "POINT(1 1)"); // ------ // --- test_all_strategies( - "SEGMENT(-1 0, 1 0)", "SEGMENT(1 0, 2 0)", 'a', 1, "POINT(1 0)"); + "SEGMENT(-1 0, 1 0)", "SEGMENT(1 0, 2 0)", 'a', "POINT(1 0)"); // disjoint, crossing // / // | test_all_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(-3 -3, -2 -2)", 'd', 0); + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-3 -3, -2 -2)", 'd'); // | // / test_all_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(2 2, 3 3)", 'd', 0); + "SEGMENT(-1 -1, 1 1)", "SEGMENT(2 2, 3 3)", 'd'); // disjoint, collinear // ------ // ------ test_all_strategies( - "SEGMENT(-1 0, 1 0)", "SEGMENT(-3 0, -2 0)", 'd', 0); + "SEGMENT(-1 0, 1 0)", "SEGMENT(-3 0, -2 0)", 'd'); // ------ // ------ test_all_strategies( - "SEGMENT(-1 0, 1 0)", "SEGMENT(2 0, 3 0)", 'd', 0); + "SEGMENT(-1 0, 1 0)", "SEGMENT(2 0, 3 0)", 'd'); // degenerated // / // * test_all_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, -2 -2)", 'd', 0); + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-2 -2, -2 -2)", 'd'); // / // * test_all_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, -1 -1)", '0', 1, "POINT(-1 -1)"); + "SEGMENT(-1 -1, 1 1)", "SEGMENT(-1 -1, -1 -1)", '0', "POINT(-1 -1)"); // / // * // / test_all_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(0 0, 0 0)", '0', 1, "POINT(0 0)"); + "SEGMENT(-1 -1, 1 1)", "SEGMENT(0 0, 0 0)", '0', "POINT(0 0)"); // * // / test_all_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(1 1, 1 1)", '0', 1, "POINT(1 1)"); + "SEGMENT(-1 -1, 1 1)", "SEGMENT(1 1, 1 1)", '0', "POINT(1 1)"); // * // / test_all_strategies( - "SEGMENT(-1 -1, 1 1)", "SEGMENT(2 2, 2 2)", 'd', 0); + "SEGMENT(-1 -1, 1 1)", "SEGMENT(2 2, 2 2)", 'd'); // similar to above, collinear // * ------ test_all_strategies( - "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, -2 0)", 'd', 0); + "SEGMENT(-1 0, 1 0)", "SEGMENT(-2 0, -2 0)", 'd'); // *------ test_all_strategies( - "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, -1 0)", '0', 1, "POINT(-1 0)"); + "SEGMENT(-1 0, 1 0)", "SEGMENT(-1 0, -1 0)", '0', "POINT(-1 0)"); // ---*--- test_all_strategies( - "SEGMENT(-1 0, 1 0)", "SEGMENT(0 0, 0 0)", '0', 1, "POINT(0 0)"); + "SEGMENT(-1 0, 1 0)", "SEGMENT(0 0, 0 0)", '0', "POINT(0 0)"); // ------* test_all_strategies( - "SEGMENT(-1 0, 1 0)", "SEGMENT(1 0, 1 0)", '0', 1, "POINT(1 0)"); + "SEGMENT(-1 0, 1 0)", "SEGMENT(1 0, 1 0)", '0', "POINT(1 0)"); // ------ * test_all_strategies( - "SEGMENT(-1 0, 1 0)", "SEGMENT(2 0, 2 0)", 'd', 0); + "SEGMENT(-1 0, 1 0)", "SEGMENT(2 0, 2 0)", 'd'); // Northern hemisphere // --- ------ test_all_strategies( - "SEGMENT(-1 50, 1 50)", "SEGMENT(-3 50, -2 50)", 'd', 0); + "SEGMENT(-1 50, 1 50)", "SEGMENT(-3 50, -2 50)", 'd'); // ------ // --- test_all_strategies( - "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, -1 50)", 'a', 1, "POINT(-1 50)"); + "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, -1 50)", 'a', "POINT(-1 50)"); // \/ // /\ (avoid multi-line comment) test_strategies( "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 0 50)", - great_elliptic('i', 1, "POINT(-0.5 50.0032229484023)"), - geodesic_vincenty('i', 1, "POINT(-0.4999999996073994 50.00323192256208)"), - geodesic_thomas('i', 1, "POINT(-0.4999999963998482 50.00323192258598)"), - geodesic_andoyer('i', 1, "POINT(-0.4999990340391772 50.00323192463806)")); + great_elliptic('i', "POINT(-0.5 50.0032229484023)"), + geodesic_vincenty('i', "POINT(-0.4999999996073994 50.00323192256208)"), + geodesic_thomas('i', "POINT(-0.4999999963998482 50.00323192258598)"), + geodesic_andoyer('i', "POINT(-0.4999990340391772 50.00323192463806)")); // ________ // / _____\ (avoid multi-line comment) test_all_strategies( - "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 1 50)", 't', 1, "POINT(1 50)"); + "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 1 50)", 't', "POINT(1 50)"); // _________ // / _____ \ (avoid multi-line comment) test_all_strategies( - "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 2 50)", 'd', 0); + "SEGMENT(-1 50, 1 50)", "SEGMENT(-2 50, 2 50)", 'd'); // ______ // /___ \ (avoid multi-line comment) test_all_strategies( - "SEGMENT(-1 50, 1 50)", "SEGMENT(-1 50, 0 50)", 'f', 1, "POINT(-1 50)"); + "SEGMENT(-1 50, 1 50)", "SEGMENT(-1 50, 0 50)", 'f', "POINT(-1 50)"); // ------ // ------ test_all_strategies( - "SEGMENT(-1 50, 1 50)", "SEGMENT(-1 50, 1 50)", 'e', 2, "POINT(-1 50)", "POINT(1 50)"); + "SEGMENT(-1 50, 1 50)", "SEGMENT(-1 50, 1 50)", 'e', "POINT(-1 50)", "POINT(1 50)", false); // ________ // /_____ \ (avoid multi-line comment) test_all_strategies( - "SEGMENT(-1 50, 1 50)", "SEGMENT(-1 50, 2 50)", 'f', 1, "POINT(-1 50)"); + "SEGMENT(-1 50, 1 50)", "SEGMENT(-1 50, 2 50)", 'f', "POINT(-1 50)"); // ______ // / ___\ (avoid multi-line comment) test_all_strategies( - "SEGMENT(-1 50, 1 50)", "SEGMENT(0 50, 1 50)", 't', 1, "POINT(1 50)"); + "SEGMENT(-1 50, 1 50)", "SEGMENT(0 50, 1 50)", 't', "POINT(1 50)"); // \/ // /\ (avoid multi-line comment) test_strategies( "SEGMENT(-1 50, 1 50)", "SEGMENT(0 50, 2 50)", - great_elliptic('i', 1, "POINT(0.5 50.0032229484023)"), - geodesic_vincenty('i', 1, "POINT(0.4999999996112415 50.00323192256208)"), - geodesic_thomas('i', 1, "POINT(0.5000000051005989 50.00323192258598)"), - geodesic_andoyer('i', 1, "POINT(0.5000009655139563 50.00323192463806)")); + great_elliptic('i', "POINT(0.5 50.0032229484023)"), + geodesic_vincenty('i', "POINT(0.4999999996112415 50.00323192256208)"), + geodesic_thomas('i', "POINT(0.5000000051005989 50.00323192258598)"), + geodesic_andoyer('i', "POINT(0.5000009655139563 50.00323192463806)")); // ------ // --- test_all_strategies( - "SEGMENT(-1 50, 1 50)", "SEGMENT(1 50, 2 50)", 'a', 1, "POINT(1 50)"); + "SEGMENT(-1 50, 1 50)", "SEGMENT(1 50, 2 50)", 'a', "POINT(1 50)"); // ------ --- test_all_strategies( - "SEGMENT(-1 50, 1 50)", "SEGMENT(2 50, 3 50)", 'd', 0); + "SEGMENT(-1 50, 1 50)", "SEGMENT(2 50, 3 50)", 'd'); // ___| test_all_strategies( - "SEGMENT(0 0, 1 0)", "SEGMENT(1 0, 1 1)", 'a', 1, "POINT(1 0)"); + "SEGMENT(0 0, 1 0)", "SEGMENT(1 0, 1 1)", 'a', "POINT(1 0)"); // ___| test_all_strategies( - "SEGMENT(1 0, 1 1)", "SEGMENT(0 0, 1 0)", 'a', 1, "POINT(1 0)"); + "SEGMENT(1 0, 1 1)", "SEGMENT(0 0, 1 0)", 'a', "POINT(1 0)"); // |/ // /| test_strategies( "SEGMENT(10 -1, 20 1)", "SEGMENT(12.5 -1, 12.5 1)", - great_elliptic('i', 1, "POINT(12.5 -0.50051443471392)"), - geodesic_vincenty('i', 1, "POINT(12.5 -0.5005177749487734)"), - geodesic_thomas('i', 1, "POINT(12.5 -0.5005177654827411)"), - geodesic_andoyer('i', 1, "POINT(12.5 -0.500525380045163)")); + great_elliptic('i', "POINT(12.5 -0.50051443471392)"), + geodesic_vincenty('i', "POINT(12.5 -0.5005177749487734)"), + geodesic_thomas('i', "POINT(12.5 -0.5005177654827411)"), + geodesic_andoyer('i', "POINT(12.5 -0.500525380045163)")); // |/ // /| test_strategies( "SEGMENT(10 -1, 20 1)", "SEGMENT(17.5 -1, 17.5 1)", - great_elliptic('i', 1, "POINT(17.5 0.50051443471392)"), - geodesic_vincenty('i', 1, "POINT(17.5 0.5005177748229335)"), - geodesic_thomas('i', 1, "POINT(17.5 0.5005178030678186)"), - geodesic_andoyer('i', 1, "POINT(17.5 0.5004949944844279)")); + great_elliptic('i', "POINT(17.5 0.50051443471392)"), + geodesic_vincenty('i', "POINT(17.5 0.5005177748229335)"), + geodesic_thomas('i', "POINT(17.5 0.5005178030678186)"), + geodesic_andoyer('i', "POINT(17.5 0.5004949944844279)")); } int test_main(int, char* []) diff --git a/test/strategies/segment_intersection_geo.hpp b/test/strategies/segment_intersection_geo.hpp index 23aa17718..d3c3af49c 100644 --- a/test/strategies/segment_intersection_geo.hpp +++ b/test/strategies/segment_intersection_geo.hpp @@ -26,7 +26,8 @@ template void test_default_strategy(std::string const& s1_wkt, std::string const& s2_wkt, char m, std::size_t expected_count, - std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "", + int opposite_id = -1) { typedef typename bg::coordinate_type

::type coord_t; typedef bg::policies::relate::segments_tupled @@ -45,13 +46,14 @@ void test_default_strategy(std::string const& s1_wkt, std::string const& s2_wkt, void >::type strategy; - test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt, opposite_id); } template void test_great_elliptic(std::string const& s1_wkt, std::string const& s2_wkt, char m, std::size_t expected_count, - std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "", + int opposite_id = -1) { typedef typename bg::coordinate_type

::type coord_t; typedef bg::policies::relate::segments_tupled @@ -68,13 +70,14 @@ void test_great_elliptic(std::string const& s1_wkt, std::string const& s2_wkt, policy_t > strategy; - test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt, opposite_id); } /* template void test_experimental_elliptic(std::string const& s1_wkt, std::string const& s2_wkt, char m, std::size_t expected_count, - std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "", + int opposite_id = -1) { typedef typename bg::coordinate_type

::type coord_t; typedef bg::policies::relate::segments_tupled @@ -91,13 +94,14 @@ void test_experimental_elliptic(std::string const& s1_wkt, std::string const& s2 policy_t > strategy; - test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt, opposite_id); } */ template void test_geodesic_vincenty(std::string const& s1_wkt, std::string const& s2_wkt, char m, std::size_t expected_count, - std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "", + int opposite_id = -1) { typedef typename bg::coordinate_type

::type coord_t; typedef bg::policies::relate::segments_tupled @@ -117,13 +121,14 @@ void test_geodesic_vincenty(std::string const& s1_wkt, std::string const& s2_wkt 4 > strategy; - test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt, opposite_id); } template void test_geodesic_thomas(std::string const& s1_wkt, std::string const& s2_wkt, char m, std::size_t expected_count, - std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "", + int opposite_id = -1) { typedef typename bg::coordinate_type

::type coord_t; typedef bg::policies::relate::segments_tupled @@ -143,13 +148,14 @@ void test_geodesic_thomas(std::string const& s1_wkt, std::string const& s2_wkt, 2 > strategy; - test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt, opposite_id); } template void test_geodesic_andoyer(std::string const& s1_wkt, std::string const& s2_wkt, char m, std::size_t expected_count, - std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "", + int opposite_id = -1) { typedef typename bg::coordinate_type

::type coord_t; typedef bg::policies::relate::segments_tupled @@ -169,47 +175,85 @@ void test_geodesic_andoyer(std::string const& s1_wkt, std::string const& s2_wkt, 1 > strategy; - test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt, opposite_id); } struct strategy_base { - strategy_base(char m_, std::size_t expected_count_, std::string const& wkt1_ = "", std::string const& wkt2_ = "") - : m(m_), expected_count(expected_count_), wkt1(wkt1_), wkt2(wkt2_) + strategy_base(char m_) + : m(m_), expected_count(0), opposite(-1) {} + strategy_base(char m_, std::string const& wkt1_) + : m(m_), expected_count(1), wkt1(wkt1_), opposite(-1) + {} + strategy_base(char m_, std::string const& wkt1_, std::string const& wkt2_, bool opposite_) + : m(m_), expected_count(1), wkt1(wkt1_), opposite(opposite_ ? 1 : 0) + {} + char m; std::size_t expected_count; std::string wkt1, wkt2; + int opposite; }; struct strategy_default : strategy_base { - strategy_default(char m, std::size_t expected_count, std::string const& wkt1 = "", std::string const& wkt2 = "") - : strategy_base(m, expected_count, wkt1, wkt2) + strategy_default(char m) + : strategy_base(m) + {} + strategy_default(char m, std::string const& wkt1) + : strategy_base(m, wkt1) + {} + strategy_default(char m, std::string const& wkt1, std::string const& wkt2, bool opposite) + : strategy_base(m, wkt1, wkt2, opposite) {} }; struct geodesic_vincenty : strategy_base { - geodesic_vincenty(char m, std::size_t expected_count, std::string const& wkt1 = "", std::string const& wkt2 = "") - : strategy_base(m, expected_count, wkt1, wkt2) + geodesic_vincenty(char m) + : strategy_base(m) + {} + geodesic_vincenty(char m, std::string const& wkt1) + : strategy_base(m, wkt1) + {} + geodesic_vincenty(char m, std::string const& wkt1, std::string const& wkt2, bool opposite) + : strategy_base(m, wkt1, wkt2, opposite) {} }; struct geodesic_thomas : strategy_base { - geodesic_thomas(char m, std::size_t expected_count, std::string const& wkt1 = "", std::string const& wkt2 = "") - : strategy_base(m, expected_count, wkt1, wkt2) + geodesic_thomas(char m) + : strategy_base(m) + {} + geodesic_thomas(char m, std::string const& wkt1) + : strategy_base(m, wkt1) + {} + geodesic_thomas(char m, std::string const& wkt1, std::string const& wkt2, bool opposite) + : strategy_base(m, wkt1, wkt2, opposite) {} }; struct geodesic_andoyer : strategy_base { - geodesic_andoyer(char m, std::size_t expected_count, std::string const& wkt1 = "", std::string const& wkt2 = "") - : strategy_base(m, expected_count, wkt1, wkt2) + geodesic_andoyer(char m) + : strategy_base(m) + {} + geodesic_andoyer(char m, std::string const& wkt1) + : strategy_base(m, wkt1) + {} + geodesic_andoyer(char m, std::string const& wkt1, std::string const& wkt2, bool opposite) + : strategy_base(m, wkt1, wkt2, opposite) {} }; struct great_elliptic : strategy_base { - great_elliptic(char m, std::size_t expected_count, std::string const& wkt1 = "", std::string const& wkt2 = "") - : strategy_base(m, expected_count, wkt1, wkt2) + great_elliptic(char m) + : strategy_base(m) + {} + great_elliptic(char m, std::string const& wkt1) + : strategy_base(m, wkt1) + {} + great_elliptic(char m, std::string const& wkt1, std::string const& wkt2, bool opposite) + : strategy_base(m, wkt1, wkt2, opposite) {} }; @@ -284,15 +328,32 @@ void test_strategies(std::string const& s1_wkt, std::string const& s2_wkt, template void test_all_strategies(std::string const& s1_wkt, std::string const& s2_wkt, - char m, std::size_t expected_count, - std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") + char m, std::string const& ip0_wkt = "") { - test_default_strategy(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); - test_great_elliptic(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); - //test_experimental_elliptic(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); - test_geodesic_vincenty(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); - test_geodesic_thomas(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); - test_geodesic_andoyer(s1_wkt, s2_wkt, m, expected_count, ip0_wkt, ip1_wkt); + std::size_t expected_count = ip0_wkt.empty() ? 0 : 1; + + test_default_strategy(s1_wkt, s2_wkt, m, expected_count, ip0_wkt); + test_great_elliptic(s1_wkt, s2_wkt, m, expected_count, ip0_wkt); + //test_experimental_elliptic(s1_wkt, s2_wkt, m, expected_count, ip0_wkt); + test_geodesic_vincenty(s1_wkt, s2_wkt, m, expected_count, ip0_wkt); + test_geodesic_thomas(s1_wkt, s2_wkt, m, expected_count, ip0_wkt); + test_geodesic_andoyer(s1_wkt, s2_wkt, m, expected_count, ip0_wkt); +} + +template +void test_all_strategies(std::string const& s1_wkt, std::string const& s2_wkt, + char m, + std::string const& ip0_wkt, std::string const& ip1_wkt, + bool opposite) +{ + int opposite_id = opposite ? 1 : 0; + + test_default_strategy(s1_wkt, s2_wkt, m, 2, ip0_wkt, ip1_wkt, opposite_id); + test_great_elliptic(s1_wkt, s2_wkt, m, 2, ip0_wkt, ip1_wkt, opposite_id); + //test_experimental_elliptic(s1_wkt, s2_wkt, m, 2, ip0_wkt, ip1_wkt, opposite_id); + test_geodesic_vincenty(s1_wkt, s2_wkt, m, 2, ip0_wkt, ip1_wkt, opposite_id); + test_geodesic_thomas(s1_wkt, s2_wkt, m, 2, ip0_wkt, ip1_wkt, opposite_id); + test_geodesic_andoyer(s1_wkt, s2_wkt, m, 2, ip0_wkt, ip1_wkt, opposite_id); } #endif // BOOST_GEOMETRY_TEST_STRATEGIES_SEGMENT_INTERSECTION_GEO_HPP diff --git a/test/strategies/segment_intersection_sph.hpp b/test/strategies/segment_intersection_sph.hpp index aa3e5e1ee..8a8436ba6 100644 --- a/test/strategies/segment_intersection_sph.hpp +++ b/test/strategies/segment_intersection_sph.hpp @@ -26,6 +26,14 @@ #include #include +template +bool equals_relaxed_val(T const& v1, T const& v2, T const& eps_scale) +{ + T relaxed_eps = std::numeric_limits::epsilon() + * bg::math::detail::greatest(c1, bg::math::abs(v1), bg::math::abs(v2)) + * eps_scale; + return bg::math::abs(v1 - v2) <= relaxed_eps; +} template bool equals_relaxed(P1 const& p1, P2 const& p2, T const& eps_scale) @@ -51,7 +59,8 @@ template void test_strategy_one(S1 const& s1, S2 const& s2, Strategy const& strategy, char m, std::size_t expected_count, - P const& ip0 = P(), P const& ip1 = P()) + P const& ip0 = P(), P const& ip1 = P(), + int opposite_id = -1) { typedef typename Strategy::return_type return_type; @@ -101,6 +110,13 @@ void test_strategy_one(S1 const& s1, S2 const& s2, "IP1: " << std::setprecision(16) << bg::wkt(res_i1) << " different than expected: " << bg::wkt(ip1) << " for " << bg::wkt(s1) << " and " << bg::wkt(s2)); } + + if (opposite_id >= 0) + { + bool opposite = opposite_id != 0; + BOOST_CHECK_MESSAGE(opposite == boost::get<1>(res).opposite, + bg::wkt(s1) << " and " << bg::wkt(s2) << (opposite_id == 0 ? " are not " : " are ") << "opposite" ); + } } template @@ -116,7 +132,8 @@ template void test_strategy(S1 const& s1, S2 const& s2, Strategy const& strategy, char m, std::size_t expected_count, - P const& ip0 = P(), P const& ip1 = P()) + P const& ip0 = P(), P const& ip1 = P(), + int opposite_id = -1) { S1 s1t = s1; S2 s2t = s2; @@ -124,7 +141,7 @@ void test_strategy(S1 const& s1, S2 const& s2, P ip1t = ip1; #ifndef BOOST_GEOMETRY_TEST_GEO_INTERSECTION_TEST_SIMILAR - test_strategy_one(s1t, s2t, strategy, m, expected_count, ip0t, ip1t); + test_strategy_one(s1t, s2t, strategy, m, expected_count, ip0t, ip1t, opposite_id); #else double t = 0; for (int i = 0; i < 4; ++i, t += 90) @@ -138,7 +155,7 @@ void test_strategy(S1 const& s1, S2 const& s2, if (expected_count > 1) bg::set<0>(ip1t, translated(bg::get<0>(ip1), t)); - test_strategy_one(s1t, s2t, strategy, m, expected_count, ip0t, ip1t); + test_strategy_one(s1t, s2t, strategy, m, expected_count, ip0t, ip1t, opposite_id); } #endif } @@ -147,7 +164,8 @@ template void test_strategy(std::string const& s1_wkt, std::string const& s2_wkt, Strategy const& strategy, char m, std::size_t expected_count, - std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "", + int opposite_id = -1) { S1 s1; S2 s2; @@ -160,16 +178,17 @@ void test_strategy(std::string const& s1_wkt, std::string const& s2_wkt, if (!ip1_wkt.empty()) bg::read_wkt(ip1_wkt, ip1); - test_strategy(s1, s2, strategy, m, expected_count, ip0, ip1); + test_strategy(s1, s2, strategy, m, expected_count, ip0, ip1, opposite_id); } template void test_strategy(std::string const& s1_wkt, std::string const& s2_wkt, Strategy const& strategy, char m, std::size_t expected_count, - std::string const& ip0_wkt = "", std::string const& ip1_wkt = "") + std::string const& ip0_wkt = "", std::string const& ip1_wkt = "", + int opposite_id = -1) { - test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt); + test_strategy(s1_wkt, s2_wkt, strategy, m, expected_count, ip0_wkt, ip1_wkt, opposite_id); } #endif // BOOST_GEOMETRY_TEST_STRATEGIES_SEGMENT_INTERSECTION_SPH_HPP From 7a764007544c9d0d98c91b4e233a7b93ffe370e8 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 7 Feb 2017 23:59:50 +0100 Subject: [PATCH 31/59] [strategies] Store and use Spheroid model object in relate_geodesic_segments strategy. --- .../geographic/geodesic_intersection.hpp | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp b/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp index b91b92b31..1353e2fa1 100644 --- a/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp +++ b/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp @@ -68,8 +68,7 @@ struct relate_geodesic_segments static inline side_strategy_type get_side_strategy() { - // TODO: pass m_strategy - return side_strategy_type(Spheroid()); + return side_strategy_type(m_spheroid); } template @@ -92,7 +91,7 @@ struct relate_geodesic_segments < Geometry1, Geometry2 >::type strategy_type; - return strategy_type(); + return strategy_type(get_side_strategy()); } enum intersection_point_flag { ipi_inters = 0, ipi_at_a1, ipi_at_a2, ipi_at_b1, ipi_at_b2 }; @@ -160,6 +159,10 @@ struct relate_geodesic_segments intersection_point_flag ip_flag; }; + relate_geodesic_segments(Spheroid const& spheroid = Spheroid()) + : m_spheroid(spheroid) + {} + // Relate segments a and b template < @@ -168,9 +171,9 @@ struct relate_geodesic_segments typename Policy, typename RobustPolicy > - static inline typename Policy::return_type - apply(Segment1 const& a, Segment2 const& b, - Policy const& policy, RobustPolicy const& robust_policy) + inline typename Policy::return_type apply(Segment1 const& a, Segment2 const& b, + Policy const& policy, + RobustPolicy const& robust_policy) const { typedef typename point_type::type point1_t; typedef typename point_type::type point2_t; @@ -195,10 +198,9 @@ struct relate_geodesic_segments typename Point1, typename Point2 > - static inline typename Policy::return_type - apply(Segment1 const& a, Segment2 const& b, - Policy const&, RobustPolicy const&, - Point1 a1, Point1 a2, Point2 b1, Point2 b2) + inline typename Policy::return_type apply(Segment1 const& a, Segment2 const& b, + Policy const&, RobustPolicy const&, + Point1 a1, Point1 a2, Point2 b1, Point2 b2) const { bool is_a_reversed = get<1>(a1) > get<1>(a2); bool is_b_reversed = get<1>(b1) > get<1>(b2); @@ -226,10 +228,10 @@ private: typename Point1, typename Point2 > - static inline typename Policy::return_type - apply(Segment1 const& a, Segment2 const& b, - Point1 const& a1, Point1 const& a2, Point2 const& b1, Point2 const& b2, - bool is_a_reversed, bool is_b_reversed) + inline typename Policy::return_type apply(Segment1 const& a, Segment2 const& b, + Point1 const& a1, Point1 const& a2, + Point2 const& b1, Point2 const& b2, + bool is_a_reversed, bool is_b_reversed) const { BOOST_CONCEPT_ASSERT( (concepts::ConstSegment) ); BOOST_CONCEPT_ASSERT( (concepts::ConstSegment) ); @@ -237,14 +239,10 @@ private: typedef typename select_calculation_type ::type calc_t; - // For now create it using default constructor. In the future it could - // be stored in strategy. However then apply() wouldn't be static and - // all relops and setops would have to take the strategy or model. - Spheroid spheroid_in; // normalized spheroid srs::spheroid spheroid(calc_t(1), - calc_t(get_radius<2>(spheroid_in)) // b/a - / calc_t(get_radius<0>(spheroid_in))); + calc_t(get_radius<2>(m_spheroid)) // b/a + / calc_t(get_radius<0>(m_spheroid))); // TODO: check only 2 first coordinates here? using geometry::detail::equals::equals_point_point; @@ -838,6 +836,9 @@ private: ip_flag == ipi_at_p2 ? ipi_at_p1 : ip_flag; } + +private: + Spheroid m_spheroid; }; From 376f777f586179e8091f891b911f3f3651a43a52 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 9 Feb 2017 16:42:08 +0100 Subject: [PATCH 32/59] [strategies] Fix compilation error in spherical area strategy. --- .../boost/geometry/strategies/spherical/area_spherical.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/strategies/spherical/area_spherical.hpp b/include/boost/geometry/strategies/spherical/area_spherical.hpp index 920e7a460..faff0d9b4 100644 --- a/include/boost/geometry/strategies/spherical/area_spherical.hpp +++ b/include/boost/geometry/strategies/spherical/area_spherical.hpp @@ -1,7 +1,8 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2016 Oracle and/or its affiliates. +// Copyright (c) 2016-2017 Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fisikopoulos, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -102,7 +103,7 @@ public : inline spherical(CT radius) //backward compatibility : m_sphere() { - m_sphere.set_radius<0>(radius); + geometry::set_radius<0>(m_sphere, radius); } inline void apply(PointOfSegment const& p1, From ea1b6c0ce0b81da14595b369c133f12e8853b86a Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 10 Feb 2017 23:57:00 +0100 Subject: [PATCH 33/59] [test][envelope][cross_product] Fix/suppress warnings. --- test/algorithms/envelope_expand/envelope_on_spheroid.cpp | 6 +++--- test/arithmetic/cross_product.cpp | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/test/algorithms/envelope_expand/envelope_on_spheroid.cpp b/test/algorithms/envelope_expand/envelope_on_spheroid.cpp index 2b736af00..a6336132a 100644 --- a/test/algorithms/envelope_expand/envelope_on_spheroid.cpp +++ b/test/algorithms/envelope_expand/envelope_on_spheroid.cpp @@ -940,7 +940,7 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_thomas ) typedef test_envelope_on_sphere_or_spheroid < G, B, - typename bg::tag::type, + bg::tag::type, test_reverse_geometry::value, bg::formula::thomas_inverse > tester; @@ -1098,7 +1098,7 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_andoyer ) typedef test_envelope_on_sphere_or_spheroid < G, B, - typename bg::tag::type, + bg::tag::type, test_reverse_geometry::value, bg::formula::andoyer_inverse > tester; @@ -1256,7 +1256,7 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_vincenty ) typedef test_envelope_on_sphere_or_spheroid < G, B, - typename bg::tag::type, + bg::tag::type, test_reverse_geometry::value, bg::formula::vincenty_inverse > tester; diff --git a/test/arithmetic/cross_product.cpp b/test/arithmetic/cross_product.cpp index e53662861..cb1ddf617 100644 --- a/test/arithmetic/cross_product.cpp +++ b/test/arithmetic/cross_product.cpp @@ -11,6 +11,8 @@ #include +#include + #include #include @@ -60,6 +62,7 @@ void test_4d() bg::assign_values(p2, 45, 70, 20); bg::set<3>(p2, 35); P c = bg::cross_product(p1, p2); + boost::ignore_unused(c); } #endif From e95d85c5523a6e739b18bad247f8780b9bc6afee Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 17 Feb 2017 04:58:45 +0100 Subject: [PATCH 34/59] [strategies] Add area and pt-pt distance strategies getters to intersection strategies. --- .../strategies/cartesian/area_surveyor.hpp | 5 ++- .../strategies/cartesian/cart_intersect.hpp | 35 ++++++++++++++++++ .../strategies/spherical/area_spherical.hpp | 3 ++ .../strategies/spherical/intersection.hpp | 37 +++++++++++++++++++ 4 files changed, 78 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/strategies/cartesian/area_surveyor.hpp b/include/boost/geometry/strategies/cartesian/area_surveyor.hpp index ba76f6794..b3f19b1b1 100644 --- a/include/boost/geometry/strategies/cartesian/area_surveyor.hpp +++ b/include/boost/geometry/strategies/cartesian/area_surveyor.hpp @@ -4,8 +4,8 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. -// This file was modified by Oracle on 2016. -// Modifications copyright (c) 2016, Oracle and/or its affiliates. +// This file was modified by Oracle on 2016, 2017. +// Modifications copyright (c) 2016-2017, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -25,6 +25,7 @@ //#include #include #include +#include #include diff --git a/include/boost/geometry/strategies/cartesian/cart_intersect.hpp b/include/boost/geometry/strategies/cartesian/cart_intersect.hpp index 1f6f56d66..e4c2baa99 100644 --- a/include/boost/geometry/strategies/cartesian/cart_intersect.hpp +++ b/include/boost/geometry/strategies/cartesian/cart_intersect.hpp @@ -34,6 +34,8 @@ #include #include +#include +#include #include #include #include @@ -95,6 +97,39 @@ struct relate_cartesian_segments return strategy_type(); } + template + struct area_strategy + { + typedef area::surveyor + < + typename point_type::type, + CalculationType + > type; + }; + + template + static inline typename area_strategy::type get_area_strategy() + { + typedef typename area_strategy::type strategy_type; + return strategy_type(); + } + + template + struct distance_strategy + { + typedef distance::pythagoras + < + CalculationType + > type; + }; + + template + static inline typename distance_strategy::type get_distance_strategy() + { + typedef typename distance_strategy::type strategy_type; + return strategy_type(); + } + template struct segment_intersection_info { diff --git a/include/boost/geometry/strategies/spherical/area_spherical.hpp b/include/boost/geometry/strategies/spherical/area_spherical.hpp index faff0d9b4..4aa038253 100644 --- a/include/boost/geometry/strategies/spherical/area_spherical.hpp +++ b/include/boost/geometry/strategies/spherical/area_spherical.hpp @@ -11,9 +11,12 @@ #ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_AREA_SPHERICAL_HPP #define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_AREA_SPHERICAL_HPP + #include #include #include +#include + namespace boost { namespace geometry { diff --git a/include/boost/geometry/strategies/spherical/intersection.hpp b/include/boost/geometry/strategies/spherical/intersection.hpp index b8b8bf93e..73cc85059 100644 --- a/include/boost/geometry/strategies/spherical/intersection.hpp +++ b/include/boost/geometry/strategies/spherical/intersection.hpp @@ -38,6 +38,8 @@ #include #include #include +#include +#include #include #include @@ -106,6 +108,41 @@ struct relate_spherical_segments return strategy_type(); } + template + struct area_strategy + { + typedef area::spherical + < + typename point_type::type, + false, + CalculationType + > type; + }; + + template + static inline typename area_strategy::type get_area_strategy() + { + typedef typename area_strategy::type strategy_type; + return strategy_type(); + } + + template + struct distance_strategy + { + typedef distance::haversine + < + typename coordinate_type::type, + CalculationType + > type; + }; + + template + static inline typename distance_strategy::type get_distance_strategy() + { + typedef typename distance_strategy::type strategy_type; + return strategy_type(); + } + enum intersection_point_flag { ipi_inters = 0, ipi_at_a1, ipi_at_a2, ipi_at_b1, ipi_at_b2 }; template From 6f7beaf01ad4a9abaee59eed39f8a8942e6c3205 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 17 Feb 2017 05:00:57 +0100 Subject: [PATCH 35/59] [setops] Support strategies in intersection, difference, sym_difference and union_. --- .../detail/intersection/interface.hpp | 249 +++++++++++----- .../boost/geometry/algorithms/difference.hpp | 281 ++++++++++++++++-- .../geometry/algorithms/sym_difference.hpp | 274 ++++++++++++++++- include/boost/geometry/algorithms/union.hpp | 278 ++++++++++++++++- 4 files changed, 953 insertions(+), 129 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/intersection/interface.hpp b/include/boost/geometry/algorithms/detail/intersection/interface.hpp index ddf49ad03..cd806d9c7 100644 --- a/include/boost/geometry/algorithms/detail/intersection/interface.hpp +++ b/include/boost/geometry/algorithms/detail/intersection/interface.hpp @@ -15,12 +15,14 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_INTERSECTION_INTERFACE_HPP -// TODO: those headers probably may be removed -#include -#include +#include +#include +#include #include #include +#include +#include namespace boost { namespace geometry @@ -98,33 +100,46 @@ struct intersection } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH - -namespace resolve_variant -{ - -template + +namespace resolve_strategy { + struct intersection { - template - static inline bool - apply( - const Geometry1& geometry1, - const Geometry2& geometry2, - GeometryOut& geometry_out) + template + < + typename Geometry1, + typename Geometry2, + typename RobustPolicy, + typename GeometryOut, + typename Strategy + > + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + GeometryOut & geometry_out, + Strategy const& strategy) { - concepts::check(); - concepts::check(); - - typedef typename geometry::rescale_overlay_policy_type + return dispatch::intersection < Geometry1, Geometry2 - >::type rescale_policy_type; - - rescale_policy_type robust_policy - = geometry::get_rescale_policy(geometry1, - geometry2); - + >::apply(geometry1, geometry2, robust_policy, geometry_out, + strategy); + } + + template + < + typename Geometry1, + typename Geometry2, + typename RobustPolicy, + typename GeometryOut + > + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + GeometryOut & geometry_out, + default_strategy) + { typedef typename strategy::relate::services::default_strategy < Geometry1, Geometry2 @@ -139,44 +154,83 @@ struct intersection } }; +} // resolve_strategy + + +namespace resolve_variant +{ + +template +struct intersection +{ + template + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + GeometryOut& geometry_out, + Strategy const& strategy) + { + concepts::check(); + concepts::check(); + + typedef typename geometry::rescale_overlay_policy_type + < + Geometry1, + Geometry2 + >::type rescale_policy_type; + + rescale_policy_type robust_policy + = geometry::get_rescale_policy(geometry1, + geometry2); + + return resolve_strategy::intersection::apply(geometry1, + geometry2, + robust_policy, + geometry_out, + strategy); + } +}; + template struct intersection, Geometry2> { - template + template struct visitor: static_visitor { Geometry2 const& m_geometry2; GeometryOut& m_geometry_out; + Strategy const& m_strategy; visitor(Geometry2 const& geometry2, - GeometryOut& geometry_out) + GeometryOut& geometry_out, + Strategy const& strategy) : m_geometry2(geometry2) , m_geometry_out(geometry_out) + , m_strategy(strategy) {} template - result_type operator()(Geometry1 const& geometry1) const + bool operator()(Geometry1 const& geometry1) const { return intersection - < - Geometry1, - Geometry2 - >::template apply - < - GeometryOut - > - (geometry1, m_geometry2, m_geometry_out); + < + Geometry1, + Geometry2 + >::apply(geometry1, m_geometry2, m_geometry_out, m_strategy); } }; - template + template static inline bool apply(variant const& geometry1, Geometry2 const& geometry2, - GeometryOut& geometry_out) + GeometryOut& geometry_out, + Strategy const& strategy) { - return boost::apply_visitor(visitor(geometry2, geometry_out), geometry1); + return boost::apply_visitor(visitor(geometry2, + geometry_out, + strategy), + geometry1); } }; @@ -184,40 +238,43 @@ struct intersection, Geometry2> template struct intersection > { - template + template struct visitor: static_visitor { Geometry1 const& m_geometry1; GeometryOut& m_geometry_out; + Strategy const& m_strategy; visitor(Geometry1 const& geometry1, - GeometryOut& geometry_out) + GeometryOut& geometry_out, + Strategy const& strategy) : m_geometry1(geometry1) , m_geometry_out(geometry_out) + , m_strategy(strategy) {} template - result_type operator()(Geometry2 const& geometry2) const + bool operator()(Geometry2 const& geometry2) const { return intersection - < - Geometry1, - Geometry2 - >::template apply - < - GeometryOut - > - (m_geometry1, geometry2, m_geometry_out); + < + Geometry1, + Geometry2 + >::apply(m_geometry1, geometry2, m_geometry_out, m_strategy); } }; - template + template static inline bool apply(Geometry1 const& geometry1, - const variant& geometry2, - GeometryOut& geometry_out) + variant const& geometry2, + GeometryOut& geometry_out, + Strategy const& strategy) { - return boost::apply_visitor(visitor(geometry1, geometry_out), geometry2); + return boost::apply_visitor(visitor(geometry1, + geometry_out, + strategy), + geometry2); } }; @@ -225,44 +282,82 @@ struct intersection > template struct intersection, variant > { - template + template struct visitor: static_visitor { GeometryOut& m_geometry_out; + Strategy const& m_strategy; - visitor(GeometryOut& geometry_out) + visitor(GeometryOut& geometry_out, Strategy const& strategy) : m_geometry_out(geometry_out) + , m_strategy(strategy) {} template - result_type operator()(Geometry1 const& geometry1, - Geometry2 const& geometry2) const + bool operator()(Geometry1 const& geometry1, + Geometry2 const& geometry2) const { return intersection - < - Geometry1, - Geometry2 - >::template apply - < - GeometryOut - > - (geometry1, geometry2, m_geometry_out); + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, m_geometry_out, m_strategy); } }; - template + template static inline bool - apply(const variant& geometry1, - const variant& geometry2, - GeometryOut& geometry_out) + apply(variant const& geometry1, + variant const& geometry2, + GeometryOut& geometry_out, + Strategy const& strategy) { - return boost::apply_visitor(visitor(geometry_out), geometry1, geometry2); + return boost::apply_visitor(visitor(geometry_out, + strategy), + geometry1, geometry2); } }; } // namespace resolve_variant +/*! +\brief \brief_calc2{intersection} +\ingroup intersection +\details \details_calc2{intersection, spatial set theoretic intersection}. +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam GeometryOut Collection of geometries (e.g. std::vector, std::deque, boost::geometry::multi*) of which + the value_type fulfills a \p_l_or_c concept, or it is the output geometry (e.g. for a box) +\tparam Strategy \tparam_strategy{Intersection} +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param geometry_out The output geometry, either a multi_point, multi_polygon, + multi_linestring, or a box (for intersection of two boxes) +\param strategy \param_strategy{intersection} + +\qbk{[include reference/algorithms/intersection.qbk]} +*/ +template +< + typename Geometry1, + typename Geometry2, + typename GeometryOut, + typename Strategy +> +inline bool intersection(Geometry1 const& geometry1, + Geometry2 const& geometry2, + GeometryOut& geometry_out, + Strategy const& strategy) +{ + return resolve_variant::intersection + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, geometry_out, strategy); +} + + /*! \brief \brief_calc2{intersection} \ingroup intersection @@ -285,18 +380,14 @@ template typename GeometryOut > inline bool intersection(Geometry1 const& geometry1, - Geometry2 const& geometry2, - GeometryOut& geometry_out) + Geometry2 const& geometry2, + GeometryOut& geometry_out) { return resolve_variant::intersection < - Geometry1, - Geometry2 - >::template apply - < - GeometryOut - > - (geometry1, geometry2, geometry_out); + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, geometry_out, default_strategy()); } diff --git a/include/boost/geometry/algorithms/difference.hpp b/include/boost/geometry/algorithms/difference.hpp index 2d0afffe8..06c79dc15 100644 --- a/include/boost/geometry/algorithms/difference.hpp +++ b/include/boost/geometry/algorithms/difference.hpp @@ -4,6 +4,7 @@ // This file was modified by Oracle on 2017. // Modifications copyright (c) 2017, Oracle and/or its affiliates. + // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, @@ -13,10 +14,16 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DIFFERENCE_HPP #define BOOST_GEOMETRY_ALGORITHMS_DIFFERENCE_HPP -#include + +#include +#include +#include #include #include +#include +#include + namespace boost { namespace geometry { @@ -101,18 +108,14 @@ inline OutputIterator difference_insert(Geometry1 const& geometry1, RobustPolicy const& robust_policy, OutputIterator out) { - concepts::check(); - concepts::check(); - concepts::check(); - - typename strategy::relate::services::default_strategy + typedef typename strategy::relate::services::default_strategy < Geometry1, Geometry2 - >::type strategy; + >::type strategy_type; return difference_insert(geometry1, geometry2, - robust_policy, out, strategy); + robust_policy, out, strategy_type()); } @@ -120,6 +123,250 @@ inline OutputIterator difference_insert(Geometry1 const& geometry1, #endif // DOXYGEN_NO_DETAIL +namespace resolve_strategy { + +struct difference +{ + template + < + typename Geometry1, + typename Geometry2, + typename RobustPolicy, + typename Collection, + typename Strategy + > + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + Collection & output_collection, + Strategy const& strategy) + { + typedef typename boost::range_value::type geometry_out; + + detail::difference::difference_insert( + geometry1, geometry2, robust_policy, + range::back_inserter(output_collection), + strategy); + } + + template + < + typename Geometry1, + typename Geometry2, + typename RobustPolicy, + typename Collection + > + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + Collection & output_collection, + default_strategy) + { + typedef typename boost::range_value::type geometry_out; + + detail::difference::difference_insert( + geometry1, geometry2, robust_policy, + range::back_inserter(output_collection)); + } +}; + +} // resolve_strategy + + +namespace resolve_variant +{ + +template +struct difference +{ + template + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + typedef typename geometry::rescale_overlay_policy_type + < + Geometry1, + Geometry2 + >::type rescale_policy_type; + + rescale_policy_type robust_policy + = geometry::get_rescale_policy(geometry1, + geometry2); + + resolve_strategy::difference::apply(geometry1, geometry2, + robust_policy, + output_collection, + strategy); + } +}; + + +template +struct difference, Geometry2> +{ + template + struct visitor: static_visitor<> + { + Geometry2 const& m_geometry2; + Collection& m_output_collection; + Strategy const& m_strategy; + + visitor(Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) + : m_geometry2(geometry2) + , m_output_collection(output_collection) + , m_strategy(strategy) + {} + + template + void operator()(Geometry1 const& geometry1) const + { + difference + < + Geometry1, + Geometry2 + >::apply(geometry1, m_geometry2, m_output_collection, m_strategy); + } + }; + + template + static inline void + apply(variant const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + boost::apply_visitor(visitor(geometry2, + output_collection, + strategy), + geometry1); + } +}; + + +template +struct difference > +{ + template + struct visitor: static_visitor<> + { + Geometry1 const& m_geometry1; + Collection& m_output_collection; + Strategy const& m_strategy; + + visitor(Geometry1 const& geometry1, + Collection& output_collection, + Strategy const& strategy) + : m_geometry1(geometry1) + , m_output_collection(output_collection) + , m_strategy(strategy) + {} + + template + void operator()(Geometry2 const& geometry2) const + { + difference + < + Geometry1, + Geometry2 + >::apply(m_geometry1, geometry2, m_output_collection, m_strategy); + } + }; + + template + static inline void + apply(Geometry1 const& geometry1, + variant const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + boost::apply_visitor(visitor(geometry1, + output_collection, + strategy), + geometry2); + } +}; + + +template +struct difference, variant > +{ + template + struct visitor: static_visitor<> + { + Collection& m_output_collection; + Strategy const& m_strategy; + + visitor(Collection& output_collection, Strategy const& strategy) + : m_output_collection(output_collection) + , m_strategy(strategy) + {} + + template + void operator()(Geometry1 const& geometry1, + Geometry2 const& geometry2) const + { + difference + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, m_output_collection, m_strategy); + } + }; + + template + static inline void + apply(variant const& geometry1, + variant const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + boost::apply_visitor(visitor(output_collection, + strategy), + geometry1, geometry2); + } +}; + +} // namespace resolve_variant + + +/*! +\brief_calc2{difference} +\ingroup difference +\details \details_calc2{difference, spatial set theoretic difference}. +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Collection \tparam_output_collection +\tparam Strategy \tparam_strategy{Difference} +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param output_collection the output collection +\param strategy \param_strategy{difference} + +\qbk{[include reference/algorithms/difference.qbk]} +*/ +template +< + typename Geometry1, + typename Geometry2, + typename Collection, + typename Strategy +> +inline void difference(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) +{ + resolve_variant::difference + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, output_collection, strategy); +} + /*! \brief_calc2{difference} @@ -144,25 +391,11 @@ inline void difference(Geometry1 const& geometry1, Geometry2 const& geometry2, Collection& output_collection) { - concepts::check(); - concepts::check(); - - typedef typename boost::range_value::type geometry_out; - concepts::check(); - - typedef typename geometry::rescale_overlay_policy_type + resolve_variant::difference < Geometry1, Geometry2 - >::type rescale_policy_type; - - rescale_policy_type robust_policy - = geometry::get_rescale_policy(geometry1, - geometry2); - - detail::difference::difference_insert( - geometry1, geometry2, robust_policy, - range::back_inserter(output_collection)); + >::apply(geometry1, geometry2, output_collection, default_strategy()); } diff --git a/include/boost/geometry/algorithms/sym_difference.hpp b/include/boost/geometry/algorithms/sym_difference.hpp index 5d5bd904b..ad5878185 100644 --- a/include/boost/geometry/algorithms/sym_difference.hpp +++ b/include/boost/geometry/algorithms/sym_difference.hpp @@ -15,13 +15,21 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_SYM_DIFFERENCE_HPP #define BOOST_GEOMETRY_ALGORITHMS_SYM_DIFFERENCE_HPP + #include #include #include +#include +#include +#include + #include #include #include +#include +#include +#include namespace boost { namespace geometry @@ -289,6 +297,252 @@ inline OutputIterator sym_difference_insert(Geometry1 const& geometry1, #endif // DOXYGEN_NO_DETAIL +namespace resolve_strategy { + +struct sym_difference +{ + template + < + typename Geometry1, + typename Geometry2, + typename RobustPolicy, + typename Collection, + typename Strategy + > + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + Collection & output_collection, + Strategy const& strategy) + { + typedef typename boost::range_value::type geometry_out; + + detail::sym_difference::sym_difference_insert( + geometry1, geometry2, robust_policy, + range::back_inserter(output_collection), + strategy); + } + + template + < + typename Geometry1, + typename Geometry2, + typename RobustPolicy, + typename Collection + > + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + Collection & output_collection, + default_strategy) + { + typedef typename boost::range_value::type geometry_out; + + detail::sym_difference::sym_difference_insert( + geometry1, geometry2, robust_policy, + range::back_inserter(output_collection)); + } +}; + +} // resolve_strategy + + +namespace resolve_variant +{ + +template +struct sym_difference +{ + template + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + typedef typename geometry::rescale_overlay_policy_type + < + Geometry1, + Geometry2 + >::type rescale_policy_type; + + rescale_policy_type robust_policy + = geometry::get_rescale_policy(geometry1, + geometry2); + + resolve_strategy::sym_difference::apply(geometry1, geometry2, + robust_policy, + output_collection, + strategy); + } +}; + + +template +struct sym_difference, Geometry2> +{ + template + struct visitor: static_visitor<> + { + Geometry2 const& m_geometry2; + Collection& m_output_collection; + Strategy const& m_strategy; + + visitor(Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) + : m_geometry2(geometry2) + , m_output_collection(output_collection) + , m_strategy(strategy) + {} + + template + void operator()(Geometry1 const& geometry1) const + { + sym_difference + < + Geometry1, + Geometry2 + >::apply(geometry1, m_geometry2, m_output_collection, m_strategy); + } + }; + + template + static inline void + apply(variant const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + boost::apply_visitor(visitor(geometry2, + output_collection, + strategy), + geometry1); + } +}; + + +template +struct sym_difference > +{ + template + struct visitor: static_visitor<> + { + Geometry1 const& m_geometry1; + Collection& m_output_collection; + Strategy const& m_strategy; + + visitor(Geometry1 const& geometry1, + Collection& output_collection, + Strategy const& strategy) + : m_geometry1(geometry1) + , m_output_collection(output_collection) + , m_strategy(strategy) + {} + + template + void operator()(Geometry2 const& geometry2) const + { + sym_difference + < + Geometry1, + Geometry2 + >::apply(m_geometry1, geometry2, m_output_collection, m_strategy); + } + }; + + template + static inline void + apply(Geometry1 const& geometry1, + variant const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + boost::apply_visitor(visitor(geometry1, + output_collection, + strategy), + geometry2); + } +}; + + +template +struct sym_difference, variant > +{ + template + struct visitor: static_visitor<> + { + Collection& m_output_collection; + Strategy const& m_strategy; + + visitor(Collection& output_collection, Strategy const& strategy) + : m_output_collection(output_collection) + , m_strategy(strategy) + {} + + template + void operator()(Geometry1 const& geometry1, + Geometry2 const& geometry2) const + { + sym_difference + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, m_output_collection, m_strategy); + } + }; + + template + static inline void + apply(variant const& geometry1, + variant const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + boost::apply_visitor(visitor(output_collection, + strategy), + geometry1, geometry2); + } +}; + +} // namespace resolve_variant + + +/*! +\brief \brief_calc2{symmetric difference} +\ingroup sym_difference +\details \details_calc2{symmetric difference, spatial set theoretic symmetric difference (XOR)}. +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Collection output collection, either a multi-geometry, + or a std::vector / std::deque etc +\tparam Strategy \tparam_strategy{Sym_difference} +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param output_collection the output collection +\param strategy \param_strategy{sym_difference} + +\qbk{[include reference/algorithms/sym_difference.qbk]} +*/ +template +< + typename Geometry1, + typename Geometry2, + typename Collection, + typename Strategy +> +inline void sym_difference(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) +{ + resolve_variant::sym_difference + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, output_collection, strategy); +} + + /*! \brief \brief_calc2{symmetric difference} \ingroup sym_difference @@ -310,26 +564,14 @@ template typename Collection > inline void sym_difference(Geometry1 const& geometry1, - Geometry2 const& geometry2, Collection& output_collection) + Geometry2 const& geometry2, + Collection& output_collection) { - concepts::check(); - concepts::check(); - - typedef typename boost::range_value::type geometry_out; - concepts::check(); - - typedef typename geometry::rescale_overlay_policy_type + resolve_variant::sym_difference < Geometry1, Geometry2 - >::type rescale_policy_type; - - rescale_policy_type robust_policy - = geometry::get_rescale_policy(geometry1, geometry2); - - detail::sym_difference::sym_difference_insert( - geometry1, geometry2, robust_policy, - range::back_inserter(output_collection)); + >::apply(geometry1, geometry2, output_collection, default_strategy()); } diff --git a/include/boost/geometry/algorithms/union.hpp b/include/boost/geometry/algorithms/union.hpp index e2a7d2b96..42682fd96 100644 --- a/include/boost/geometry/algorithms/union.hpp +++ b/include/boost/geometry/algorithms/union.hpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include @@ -234,6 +236,265 @@ inline OutputIterator union_insert(Geometry1 const& geometry1, #endif // DOXYGEN_NO_DETAIL +namespace resolve_strategy { + +struct union_ +{ + template + < + typename Geometry1, + typename Geometry2, + typename RobustPolicy, + typename Collection, + typename Strategy + > + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + Collection & output_collection, + Strategy const& strategy) + { + typedef typename boost::range_value::type geometry_out; + + dispatch::union_insert + < + Geometry1, Geometry2, geometry_out + >::apply(geometry1, geometry2, robust_policy, + range::back_inserter(output_collection), + strategy); + } + + template + < + typename Geometry1, + typename Geometry2, + typename RobustPolicy, + typename Collection + > + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + Collection & output_collection, + default_strategy) + { + typedef typename boost::range_value::type geometry_out; + + typedef typename strategy::intersection::services::default_strategy + < + typename cs_tag::type + >::type strategy_type; + + dispatch::union_insert + < + Geometry1, Geometry2, geometry_out + >::apply(geometry1, geometry2, robust_policy, + range::back_inserter(output_collection), + strategy_type()); + } +}; + +} // resolve_strategy + + +namespace resolve_variant +{ + +template +struct union_ +{ + template + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + concepts::check(); + concepts::check(); + concepts::check::type>(); + + typedef typename geometry::rescale_overlay_policy_type + < + Geometry1, + Geometry2 + >::type rescale_policy_type; + + rescale_policy_type robust_policy + = geometry::get_rescale_policy(geometry1, + geometry2); + + resolve_strategy::union_::apply(geometry1, geometry2, + robust_policy, + output_collection, + strategy); + } +}; + + +template +struct union_, Geometry2> +{ + template + struct visitor: static_visitor<> + { + Geometry2 const& m_geometry2; + Collection& m_output_collection; + Strategy const& m_strategy; + + visitor(Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) + : m_geometry2(geometry2) + , m_output_collection(output_collection) + , m_strategy(strategy) + {} + + template + void operator()(Geometry1 const& geometry1) const + { + union_ + < + Geometry1, + Geometry2 + >::apply(geometry1, m_geometry2, m_output_collection, m_strategy); + } + }; + + template + static inline void + apply(variant const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + boost::apply_visitor(visitor(geometry2, + output_collection, + strategy), + geometry1); + } +}; + + +template +struct union_ > +{ + template + struct visitor: static_visitor<> + { + Geometry1 const& m_geometry1; + Collection& m_output_collection; + Strategy const& m_strategy; + + visitor(Geometry1 const& geometry1, + Collection& output_collection, + Strategy const& strategy) + : m_geometry1(geometry1) + , m_output_collection(output_collection) + , m_strategy(strategy) + {} + + template + void operator()(Geometry2 const& geometry2) const + { + union_ + < + Geometry1, + Geometry2 + >::apply(m_geometry1, geometry2, m_output_collection, m_strategy); + } + }; + + template + static inline void + apply(Geometry1 const& geometry1, + variant const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + boost::apply_visitor(visitor(geometry1, + output_collection, + strategy), + geometry2); + } +}; + + +template +struct union_, variant > +{ + template + struct visitor: static_visitor<> + { + Collection& m_output_collection; + Strategy const& m_strategy; + + visitor(Collection& output_collection, Strategy const& strategy) + : m_output_collection(output_collection) + , m_strategy(strategy) + {} + + template + void operator()(Geometry1 const& geometry1, + Geometry2 const& geometry2) const + { + union_ + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, m_output_collection, m_strategy); + } + }; + + template + static inline void + apply(variant const& geometry1, + variant const& geometry2, + Collection& output_collection, + Strategy const& strategy) + { + boost::apply_visitor(visitor(output_collection, + strategy), + geometry1, geometry2); + } +}; + +} // namespace resolve_variant + + +/*! +\brief Combines two geometries which each other +\ingroup union +\details \details_calc2{union, spatial set theoretic union}. +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Collection output collection, either a multi-geometry, + or a std::vector / std::deque etc +\tparam Strategy \tparam_strategy{Union_} +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param output_collection the output collection +\param strategy \param_strategy{union_} +\note Called union_ because union is a reserved word. + +\qbk{[include reference/algorithms/union.qbk]} +*/ +template +< + typename Geometry1, + typename Geometry2, + typename Collection, + typename Strategy +> +inline void union_(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection, + Strategy const& strategy) +{ + resolve_variant::union_ + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, output_collection, strategy); +} /*! @@ -258,17 +519,14 @@ template typename Collection > inline void union_(Geometry1 const& geometry1, - Geometry2 const& geometry2, - Collection& output_collection) + Geometry2 const& geometry2, + Collection& output_collection) { - concepts::check(); - concepts::check(); - - typedef typename boost::range_value::type geometry_out; - concepts::check(); - - detail::union_::union_insert(geometry1, geometry2, - range::back_inserter(output_collection)); + resolve_variant::union_ + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, output_collection, default_strategy()); } From 6bd28d96e6c1c00ea20eb76bf2cb51c360fe295e Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 17 Feb 2017 05:02:57 +0100 Subject: [PATCH 36/59] [is_simple] Add support for strategies. --- .../detail/is_simple/always_simple.hpp | 6 +- .../algorithms/detail/is_simple/areal.hpp | 18 ++++- .../algorithms/detail/is_simple/interface.hpp | 76 +++++++++++++++++-- .../algorithms/detail/is_simple/linear.hpp | 32 +++++--- .../detail/is_simple/multipoint.hpp | 6 +- 5 files changed, 112 insertions(+), 26 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/is_simple/always_simple.hpp b/include/boost/geometry/algorithms/detail/is_simple/always_simple.hpp index 91e2ef76b..5cec5e192 100644 --- a/include/boost/geometry/algorithms/detail/is_simple/always_simple.hpp +++ b/include/boost/geometry/algorithms/detail/is_simple/always_simple.hpp @@ -1,8 +1,9 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -27,7 +28,8 @@ namespace detail { namespace is_simple template struct always_simple { - static inline bool apply(Geometry const&) + template + static inline bool apply(Geometry const&, Strategy const&) { return true; } diff --git a/include/boost/geometry/algorithms/detail/is_simple/areal.hpp b/include/boost/geometry/algorithms/detail/is_simple/areal.hpp index a2322e483..d4d6db9bc 100644 --- a/include/boost/geometry/algorithms/detail/is_simple/areal.hpp +++ b/include/boost/geometry/algorithms/detail/is_simple/areal.hpp @@ -1,8 +1,9 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -37,6 +38,12 @@ namespace detail { namespace is_simple template struct is_simple_ring { + template + static inline bool apply(Ring const& ring, Strategy const&) + { + return apply(ring); + } + static inline bool apply(Ring const& ring) { simplicity_failure_policy policy; @@ -69,6 +76,12 @@ private: } public: + template + static inline bool apply(Polygon const& polygon, Strategy const&) + { + return apply(polygon); + } + static inline bool apply(Polygon const& polygon) { return @@ -119,7 +132,8 @@ struct is_simple template struct is_simple { - static inline bool apply(MultiPolygon const& multipolygon) + template + static inline bool apply(MultiPolygon const& multipolygon, Strategy const&) { return detail::check_iterator_range diff --git a/include/boost/geometry/algorithms/detail/is_simple/interface.hpp b/include/boost/geometry/algorithms/detail/is_simple/interface.hpp index 6d425232b..01c686ed8 100644 --- a/include/boost/geometry/algorithms/detail/is_simple/interface.hpp +++ b/include/boost/geometry/algorithms/detail/is_simple/interface.hpp @@ -1,8 +1,9 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -17,46 +18,105 @@ #include #include +#include +#include +#include namespace boost { namespace geometry { +namespace resolve_strategy +{ -namespace resolve_variant { +struct is_simple +{ + template + static inline bool apply(Geometry const& geometry, + Strategy const& strategy) + { + return dispatch::is_simple::apply(geometry, strategy); + } + + template + static inline bool apply(Geometry const& geometry, + default_strategy) + { + // NOTE: Currently the strategy is only used for Linear geometries + typedef typename strategy::intersection::services::default_strategy + < + typename cs_tag::type + >::type strategy_type; + + return dispatch::is_simple::apply(geometry, strategy_type()); + } +}; + +} // namespace resolve_strategy + +namespace resolve_variant +{ template struct is_simple { - static inline bool apply(Geometry const& geometry) + template + static inline bool apply(Geometry const& geometry, Strategy const& strategy) { concepts::check(); - return dispatch::is_simple::apply(geometry); + + return resolve_strategy::is_simple::apply(geometry, strategy); } }; template struct is_simple > { + template struct visitor : boost::static_visitor { + Strategy const& m_strategy; + + visitor(Strategy const& strategy) + : m_strategy(strategy) + {} + template bool operator()(Geometry const& geometry) const { - return is_simple::apply(geometry); + return is_simple::apply(geometry, m_strategy); } }; + template static inline bool - apply(boost::variant const& geometry) + apply(boost::variant const& geometry, + Strategy const& strategy) { - return boost::apply_visitor(visitor(), geometry); + return boost::apply_visitor(visitor(strategy), geometry); } }; } // namespace resolve_variant +/*! +\brief \brief_check{is simple} +\ingroup is_simple +\tparam Geometry \tparam_geometry +\tparam Strategy \tparam_strategy{Is_simple} +\param geometry \param_geometry +\param strategy \param_strategy{is_simple} +\return \return_check{is simple} + +\qbk{[include reference/algorithms/is_simple.qbk]} +*/ +template +inline bool is_simple(Geometry const& geometry, Strategy const& strategy) +{ + return resolve_variant::is_simple::apply(geometry, strategy); +} + /*! \brief \brief_check{is simple} @@ -70,7 +130,7 @@ struct is_simple > template inline bool is_simple(Geometry const& geometry) { - return resolve_variant::is_simple::apply(geometry); + return resolve_variant::is_simple::apply(geometry, default_strategy()); } diff --git a/include/boost/geometry/algorithms/detail/is_simple/linear.hpp b/include/boost/geometry/algorithms/detail/is_simple/linear.hpp index 6c469f07f..52b9d9d1c 100644 --- a/include/boost/geometry/algorithms/detail/is_simple/linear.hpp +++ b/include/boost/geometry/algorithms/detail/is_simple/linear.hpp @@ -189,8 +189,8 @@ private: }; -template -inline bool has_self_intersections(Linear const& linear) +template +inline bool has_self_intersections(Linear const& linear, Strategy const& strategy) { typedef typename point_type::type point_type; @@ -217,16 +217,11 @@ inline bool has_self_intersections(Linear const& linear) is_acceptable_turn > interrupt_policy(predicate); - typedef typename strategy::intersection::services::default_strategy - < - typename cs_tag::type - >::type strategy_type; - detail::self_get_turn_points::get_turns < turn_policy >::apply(linear, - strategy_type(), + strategy, detail::no_rescale_policy(), turns, interrupt_policy); @@ -252,8 +247,19 @@ struct is_simple_linestring && ! detail::is_valid::has_spikes < Linestring, closed - >::apply(linestring, policy) - && ! (CheckSelfIntersections && has_self_intersections(linestring)); + >::apply(linestring, policy); + } +}; + +template +struct is_simple_linestring +{ + template + static inline bool apply(Linestring const& linestring, + Strategy const& strategy) + { + return is_simple_linestring::apply(linestring) + && ! has_self_intersections(linestring, strategy); } }; @@ -261,7 +267,9 @@ struct is_simple_linestring template struct is_simple_multilinestring { - static inline bool apply(MultiLinestring const& multilinestring) + template + static inline bool apply(MultiLinestring const& multilinestring, + Strategy const& strategy) { // check each of the linestrings for simplicity // but do not compute self-intersections yet; these will be @@ -281,7 +289,7 @@ struct is_simple_multilinestring return false; } - return ! has_self_intersections(multilinestring); + return ! has_self_intersections(multilinestring, strategy); } }; diff --git a/include/boost/geometry/algorithms/detail/is_simple/multipoint.hpp b/include/boost/geometry/algorithms/detail/is_simple/multipoint.hpp index f9f43d1cd..61f0bc913 100644 --- a/include/boost/geometry/algorithms/detail/is_simple/multipoint.hpp +++ b/include/boost/geometry/algorithms/detail/is_simple/multipoint.hpp @@ -1,8 +1,9 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -38,7 +39,8 @@ namespace detail { namespace is_simple template struct is_simple_multipoint { - static inline bool apply(MultiPoint const& multipoint) + template + static inline bool apply(MultiPoint const& multipoint, Strategy const&) { if (boost::empty(multipoint)) { From b3da05740a037a31f0e3f9a8a5e899b6384c9181 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 17 Feb 2017 05:03:11 +0100 Subject: [PATCH 37/59] [is_valid] Add support for strategies. --- .../algorithms/detail/is_valid/box.hpp | 6 +- .../detail/is_valid/has_valid_self_turns.hpp | 20 +-- .../algorithms/detail/is_valid/interface.hpp | 162 +++++++++++++++--- .../algorithms/detail/is_valid/linear.hpp | 15 +- .../detail/is_valid/multipolygon.hpp | 21 ++- .../algorithms/detail/is_valid/pointlike.hpp | 11 +- .../algorithms/detail/is_valid/polygon.hpp | 43 +++-- .../algorithms/detail/is_valid/ring.hpp | 49 +++--- .../algorithms/detail/is_valid/segment.hpp | 6 +- 9 files changed, 232 insertions(+), 101 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/is_valid/box.hpp b/include/boost/geometry/algorithms/detail/is_valid/box.hpp index 863ce625f..69a4d4e78 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/box.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/box.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -71,8 +71,8 @@ struct has_valid_corners template struct is_valid_box { - template - static inline bool apply(Box const& box, VisitPolicy& visitor) + template + static inline bool apply(Box const& box, VisitPolicy& visitor, Strategy const&) { return ! has_invalid_coordinate::apply(box, visitor) diff --git a/include/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp b/include/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp index 9f278ac04..b91dc6a69 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp @@ -48,11 +48,6 @@ class has_valid_self_turns private: typedef typename point_type::type point_type; - typedef typename strategy::intersection::services::default_strategy - < - typename cs_tag::type - >::type intersection_strategy_type; - typedef typename geometry::rescale_policy_type < point_type @@ -75,15 +70,14 @@ public: > turn_type; // returns true if all turns are valid - template + template static inline bool apply(Geometry const& geometry, Turns& turns, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const& strategy) { boost::ignore_unused(visitor); - intersection_strategy_type intersection_strategy; - rescale_policy_type robust_policy = geometry::get_rescale_policy(geometry); @@ -93,7 +87,7 @@ public: > interrupt_policy; geometry::self_turns(geometry, - intersection_strategy, + strategy, robust_policy, turns, interrupt_policy); @@ -110,11 +104,11 @@ public: } // returns true if all turns are valid - template - static inline bool apply(Geometry const& geometry, VisitPolicy& visitor) + template + static inline bool apply(Geometry const& geometry, VisitPolicy& visitor, Strategy const& strategy) { std::vector turns; - return apply(geometry, turns, visitor); + return apply(geometry, turns, visitor, strategy); } }; diff --git a/include/boost/geometry/algorithms/detail/is_valid/interface.hpp b/include/boost/geometry/algorithms/detail/is_valid/interface.hpp index 5a04a9282..7561a0cb8 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/interface.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/interface.hpp @@ -1,8 +1,9 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -23,48 +24,88 @@ #include #include #include +#include +#include namespace boost { namespace geometry { + +namespace resolve_strategy +{ +struct is_valid +{ + template + static inline bool apply(Geometry const& geometry, + VisitPolicy& visitor, + Strategy const& strategy) + { + return dispatch::is_valid::apply(geometry, visitor, strategy); + } -namespace resolve_variant { + template + static inline bool apply(Geometry const& geometry, + VisitPolicy& visitor, + default_strategy) + { + // NOTE: Currently the strategy is only used for Areal geometries + typedef typename strategy::intersection::services::default_strategy + < + typename cs_tag::type + >::type strategy_type; + + return dispatch::is_valid::apply(geometry, visitor, strategy_type()); + } +}; + +} // namespace resolve_strategy + +namespace resolve_variant +{ template struct is_valid { - template - static inline bool apply(Geometry const& geometry, VisitPolicy& visitor) + template + static inline bool apply(Geometry const& geometry, + VisitPolicy& visitor, + Strategy const& strategy) { concepts::check(); - return dispatch::is_valid::apply(geometry, visitor); + + return resolve_strategy::is_valid::apply(geometry, visitor, strategy); } }; template struct is_valid > { - template + template struct visitor : boost::static_visitor { - visitor(VisitPolicy& policy) : m_policy(policy) {} + visitor(VisitPolicy& policy, Strategy const& strategy) + : m_policy(policy) + , m_strategy(strategy) + {} template bool operator()(Geometry const& geometry) const { - return is_valid::apply(geometry, m_policy); + return is_valid::apply(geometry, m_policy, m_strategy); } VisitPolicy& m_policy; + Strategy const& m_strategy; }; - template + template static inline bool apply(boost::variant const& geometry, - VisitPolicy& policy_visitor) + VisitPolicy& policy_visitor, + Strategy const& strategy) { - return boost::apply_visitor(visitor(policy_visitor), + return boost::apply_visitor(visitor(policy_visitor, strategy), geometry); } }; @@ -73,13 +114,37 @@ struct is_valid > // Undocumented for now -template -inline bool is_valid(Geometry const& geometry, VisitPolicy& visitor) +template +inline bool is_valid(Geometry const& geometry, + VisitPolicy& visitor, + Strategy const& strategy) { - return resolve_variant::is_valid::apply(geometry, visitor); + return resolve_variant::is_valid::apply(geometry, visitor, strategy); } +/*! +\brief \brief_check{is valid (in the OGC sense)} +\ingroup is_valid +\tparam Geometry \tparam_geometry +\tparam Strategy \tparam_strategy{Is_valid} +\param geometry \param_geometry +\param strategy \param_strategy{is_valid} +\return \return_check{is valid (in the OGC sense); +furthermore, the following geometries are considered valid: +multi-geometries with no elements, +linear geometries containing spikes, +areal geometries with duplicate (consecutive) points} + +\qbk{[include reference/algorithms/is_valid.qbk]} +*/ +template +inline bool is_valid(Geometry const& geometry, Strategy const& strategy) +{ + is_valid_default_policy<> visitor; + return resolve_variant::is_valid::apply(geometry, visitor, strategy); +} + /*! \brief \brief_check{is valid (in the OGC sense)} \ingroup is_valid @@ -96,11 +161,37 @@ inline bool is_valid(Geometry const& geometry, VisitPolicy& visitor) template inline bool is_valid(Geometry const& geometry) { - is_valid_default_policy<> policy_visitor; - return geometry::is_valid(geometry, policy_visitor); + return is_valid(geometry, default_strategy()); } +/*! +\brief \brief_check{is valid (in the OGC sense)} +\ingroup is_valid +\tparam Geometry \tparam_geometry +\tparam Strategy \tparam_strategy{Is_valid} +\param geometry \param_geometry +\param failure An enumeration value indicating that the geometry is + valid or not, and if not valid indicating the reason why +\param strategy \param_strategy{is_valid} +\return \return_check{is valid (in the OGC sense); + furthermore, the following geometries are considered valid: + multi-geometries with no elements, + linear geometries containing spikes, + areal geometries with duplicate (consecutive) points} + +\qbk{distinguish,with failure value} +\qbk{[include reference/algorithms/is_valid_with_failure.qbk]} +*/ +template +inline bool is_valid(Geometry const& geometry, validity_failure_type& failure, Strategy const& strategy) +{ + failure_type_policy<> visitor; + bool result = resolve_variant::is_valid::apply(geometry, visitor, strategy); + failure = visitor.failure(); + return result; +} + /*! \brief \brief_check{is valid (in the OGC sense)} \ingroup is_valid @@ -120,13 +211,38 @@ inline bool is_valid(Geometry const& geometry) template inline bool is_valid(Geometry const& geometry, validity_failure_type& failure) { - failure_type_policy<> policy_visitor; - bool result = geometry::is_valid(geometry, policy_visitor); - failure = policy_visitor.failure(); - return result; + return is_valid(geometry, failure, default_strategy()); } +/*! +\brief \brief_check{is valid (in the OGC sense)} +\ingroup is_valid +\tparam Geometry \tparam_geometry +\tparam Strategy \tparam_strategy{Is_valid} +\param geometry \param_geometry +\param message A string containing a message stating if the geometry + is valid or not, and if not valid a reason why +\param strategy \param_strategy{is_valid} +\return \return_check{is valid (in the OGC sense); + furthermore, the following geometries are considered valid: + multi-geometries with no elements, + linear geometries containing spikes, + areal geometries with duplicate (consecutive) points} + +\qbk{distinguish,with message} +\qbk{[include reference/algorithms/is_valid_with_message.qbk]} +*/ +template +inline bool is_valid(Geometry const& geometry, std::string& message, Strategy const& strategy) +{ + std::ostringstream stream; + failing_reason_policy<> visitor(stream); + bool result = resolve_variant::is_valid::apply(geometry, visitor, strategy); + message = stream.str(); + return result; +} + /*! \brief \brief_check{is valid (in the OGC sense)} \ingroup is_valid @@ -146,11 +262,7 @@ inline bool is_valid(Geometry const& geometry, validity_failure_type& failure) template inline bool is_valid(Geometry const& geometry, std::string& message) { - std::ostringstream stream; - failing_reason_policy<> policy_visitor(stream); - bool result = geometry::is_valid(geometry, policy_visitor); - message = stream.str(); - return result; + return is_valid(geometry, message, default_strategy()); } diff --git a/include/boost/geometry/algorithms/detail/is_valid/linear.hpp b/include/boost/geometry/algorithms/detail/is_valid/linear.hpp index a49e07723..6bc6b86cf 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/linear.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/linear.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -77,6 +77,14 @@ struct is_valid_linestring } return ! has_spikes::apply(linestring, visitor); } + + template + static inline bool apply(Linestring const& linestring, + VisitPolicy& visitor, + Strategy const&) + { + return apply(linestring, visitor); + } }; @@ -142,9 +150,10 @@ private: }; public: - template + template static inline bool apply(MultiLinestring const& multilinestring, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const&) { if (BOOST_GEOMETRY_CONDITION( AllowEmptyMultiGeometries && boost::empty(multilinestring))) diff --git a/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp b/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp index eafe07841..84dacc57f 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp @@ -237,23 +237,28 @@ private: } - template + template struct per_polygon { - per_polygon(VisitPolicy& policy) : m_policy(policy) {} + per_polygon(VisitPolicy& policy, Strategy const& strategy) + : m_policy(policy) + , m_strategy(strategy) + {} template inline bool apply(Polygon const& polygon) const { - return base::apply(polygon, m_policy); + return base::apply(polygon, m_policy, m_strategy); } VisitPolicy& m_policy; + Strategy const& m_strategy; }; public: - template + template static inline bool apply(MultiPolygon const& multipolygon, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const& strategy) { typedef debug_validity_phase debug_phase; @@ -268,11 +273,11 @@ public: if (! detail::check_iterator_range < - per_polygon, + per_polygon, false // do not check for empty multipolygon (done above) >::apply(boost::begin(multipolygon), boost::end(multipolygon), - per_polygon(visitor))) + per_polygon(visitor, strategy))) { return false; } @@ -285,7 +290,7 @@ public: std::deque turns; bool has_invalid_turns = - ! has_valid_turns::apply(multipolygon, turns, visitor); + ! has_valid_turns::apply(multipolygon, turns, visitor, strategy); debug_print_turns(turns.begin(), turns.end()); if (has_invalid_turns) diff --git a/include/boost/geometry/algorithms/detail/is_valid/pointlike.hpp b/include/boost/geometry/algorithms/detail/is_valid/pointlike.hpp index 51035f7a7..f77f7a35e 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/pointlike.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/pointlike.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -36,8 +36,8 @@ namespace dispatch template struct is_valid { - template - static inline bool apply(Point const& point, VisitPolicy& visitor) + template + static inline bool apply(Point const& point, VisitPolicy& visitor, Strategy const&) { boost::ignore_unused(visitor); return ! detail::is_valid::has_invalid_coordinate @@ -56,9 +56,10 @@ struct is_valid template struct is_valid { - template + template static inline bool apply(MultiPoint const& multipoint, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const&) { boost::ignore_unused(multipoint, visitor); diff --git a/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp b/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp index 182571c56..f7e22fb8d 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp @@ -1,8 +1,9 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -74,10 +75,13 @@ class is_valid_polygon protected: typedef debug_validity_phase debug_phase; - template + template struct per_ring { - per_ring(VisitPolicy& policy) : m_policy(policy) {} + per_ring(VisitPolicy& policy, Strategy const& strategy) + : m_policy(policy) + , m_strategy(strategy) + {} template inline bool apply(Ring const& ring) const @@ -85,30 +89,34 @@ protected: return detail::is_valid::is_valid_ring < Ring, false, true - >::apply(ring, m_policy); + >::apply(ring, m_policy, m_strategy); } VisitPolicy& m_policy; + Strategy const& m_strategy; }; - template + template static bool has_valid_interior_rings(InteriorRings const& interior_rings, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const& strategy) { return detail::check_iterator_range < - per_ring, + per_ring, true // allow for empty interior ring range >::apply(boost::begin(interior_rings), boost::end(interior_rings), - per_ring(visitor)); + per_ring(visitor, strategy)); } struct has_valid_rings { - template - static inline bool apply(Polygon const& polygon, VisitPolicy& visitor) + template + static inline bool apply(Polygon const& polygon, + VisitPolicy& visitor, + Strategy const& strategy) { typedef typename ring_type::type ring_type; @@ -119,7 +127,7 @@ protected: < ring_type, false // do not check self intersections - >::apply(exterior_ring(polygon), visitor)) + >::apply(exterior_ring(polygon), visitor, strategy)) { return false; } @@ -128,7 +136,8 @@ protected: debug_phase::apply(2); return has_valid_interior_rings(geometry::interior_rings(polygon), - visitor); + visitor, + strategy); } }; @@ -344,10 +353,12 @@ protected: }; public: - template - static inline bool apply(Polygon const& polygon, VisitPolicy& visitor) + template + static inline bool apply(Polygon const& polygon, + VisitPolicy& visitor, + Strategy const& strategy) { - if (! has_valid_rings::apply(polygon, visitor)) + if (! has_valid_rings::apply(polygon, visitor, strategy)) { return false; } @@ -364,7 +375,7 @@ public: std::deque turns; bool has_invalid_turns - = ! has_valid_turns::apply(polygon, turns, visitor); + = ! has_valid_turns::apply(polygon, turns, visitor, strategy); debug_print_turns(turns.begin(), turns.end()); if (has_invalid_turns) diff --git a/include/boost/geometry/algorithms/detail/is_valid/ring.hpp b/include/boost/geometry/algorithms/detail/is_valid/ring.hpp index 925c03a47..9ab68fdc4 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/ring.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/ring.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -101,35 +101,33 @@ struct ring_area_predicate template struct is_properly_oriented { - typedef typename point_type::type point_type; - - typedef typename strategy::area::services::default_strategy - < - typename cs_tag::type, - point_type - >::type strategy_type; - - typedef detail::area::ring_area - < - order_as_direction::value>::value, - geometry::closure::value - > ring_area_type; - - typedef typename default_area_result::type area_result_type; - - template - static inline bool apply(Ring const& ring, VisitPolicy& visitor) + template + static inline bool apply(Ring const& ring, VisitPolicy& visitor, + Strategy const& strategy) { boost::ignore_unused(visitor); + typedef typename point_type::type point_type; + + typedef detail::area::ring_area + < + order_as_direction::value>::value, + geometry::closure::value + > ring_area_type; + + typedef typename default_area_result::type area_result_type; + typename ring_area_predicate < area_result_type, IsInteriorRing >::type predicate; // Check area - area_result_type const zero = area_result_type(); - if (predicate(ring_area_type::apply(ring, strategy_type()), zero)) + area_result_type const zero = 0; + area_result_type const area + = ring_area_type::apply(ring, + strategy.template get_area_strategy()); + if (predicate(area, zero)) { return visitor.template apply(); } @@ -150,8 +148,9 @@ template > struct is_valid_ring { - template - static inline bool apply(Ring const& ring, VisitPolicy& visitor) + template + static inline bool apply(Ring const& ring, VisitPolicy& visitor, + Strategy const& strategy) { // return invalid if any of the following condition holds: // (a) the ring's point coordinates are not invalid (e.g., NaN) @@ -198,8 +197,8 @@ struct is_valid_ring && ! has_duplicates::apply(ring, visitor) && ! has_spikes::apply(ring, visitor) && (! CheckSelfIntersections - || has_valid_self_turns::apply(ring, visitor)) - && is_properly_oriented::apply(ring, visitor); + || has_valid_self_turns::apply(ring, visitor, strategy)) + && is_properly_oriented::apply(ring, visitor, strategy); } }; diff --git a/include/boost/geometry/algorithms/detail/is_valid/segment.hpp b/include/boost/geometry/algorithms/detail/is_valid/segment.hpp index f92f73381..30cbf7afd 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/segment.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/segment.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -44,8 +44,8 @@ namespace dispatch template struct is_valid { - template - static inline bool apply(Segment const& segment, VisitPolicy& visitor) + template + static inline bool apply(Segment const& segment, VisitPolicy& visitor, Strategy const&) { boost::ignore_unused(visitor); From 682cb65f62359928017a87ee5e3c801e6c8ed420 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 17 Feb 2017 05:04:03 +0100 Subject: [PATCH 38/59] [equals] Use area and distance strategies in equals() TrivialCheck. --- include/boost/geometry/algorithms/equals.hpp | 30 +++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/include/boost/geometry/algorithms/equals.hpp b/include/boost/geometry/algorithms/equals.hpp index 47eb7134c..a306f738e 100644 --- a/include/boost/geometry/algorithms/equals.hpp +++ b/include/boost/geometry/algorithms/equals.hpp @@ -138,24 +138,32 @@ struct segment_segment struct area_check { - template - static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) + template + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) { return geometry::math::equals( - geometry::area(geometry1), - geometry::area(geometry2)); + geometry::area(geometry1, + strategy.template get_area_strategy()), + geometry::area(geometry2, + strategy.template get_area_strategy())); } }; struct length_check { - template - static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) + template + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) { return geometry::math::equals( - geometry::length(geometry1), - geometry::length(geometry2)); + geometry::length(geometry1, + strategy.template get_distance_strategy()), + geometry::length(geometry2, + strategy.template get_distance_strategy())); } }; @@ -184,9 +192,11 @@ template struct equals_by_collection { template - static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Strategy const&) + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) { - if (! TrivialCheck::apply(geometry1, geometry2)) + if (! TrivialCheck::apply(geometry1, geometry2, strategy)) { return false; } From b1c78f662267d8a6b5c8964b0e46f734fb2c4427 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 17 Feb 2017 05:45:34 +0100 Subject: [PATCH 39/59] [test] Test setops, is_simple and is_valid with strategies. --- test/algorithms/is_simple.cpp | 35 +++++++++++- .../difference/test_difference.hpp | 16 ++++-- .../test_difference_linear_linear.hpp | 48 +++++++++++++---- .../intersection/test_intersection.hpp | 16 +++++- .../test_intersection_linear_linear.hpp | 53 ++++++++++++------- .../test_sym_difference_linear_linear.hpp | 40 ++++++++++---- .../set_operations/union/test_union.hpp | 28 ++++++++-- .../union/test_union_linear_linear.hpp | 48 +++++++++++------ test/algorithms/test_is_valid.hpp | 11 ++-- 9 files changed, 228 insertions(+), 67 deletions(-) diff --git a/test/algorithms/is_simple.cpp b/test/algorithms/is_simple.cpp index 17cbb4b7c..a61ba6216 100644 --- a/test/algorithms/is_simple.cpp +++ b/test/algorithms/is_simple.cpp @@ -1,9 +1,10 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -63,7 +64,7 @@ typedef bg::model::box box_type; //---------------------------------------------------------------------------- -template +template void test_simple(Geometry const& geometry, bool expected_result, bool check_validity = true) { @@ -72,12 +73,22 @@ void test_simple(Geometry const& geometry, bool expected_result, #endif bool simple = bg::is_simple(geometry); + BOOST_ASSERT( ! check_validity || bg::is_valid(geometry) ); BOOST_CHECK_MESSAGE( simple == expected_result, "Expected: " << expected_result << " detected: " << simple << " wkt: " << bg::wkt(geometry) ); + typedef typename bg::strategy::intersection::services::default_strategy + < + CSTag + >::type strategy_type; + + bool simple_s = bg::is_simple(geometry, strategy_type()); + + BOOST_CHECK_EQUAL(simple, simple_s); + #ifdef BOOST_GEOMETRY_TEST_DEBUG std::cout << "Geometry: "; pretty_print_geometry::apply(std::cout, geometry); @@ -92,6 +103,26 @@ void test_simple(Geometry const& geometry, bool expected_result, } +template +void test_simple(Geometry const& geometry, + bool expected_result, + bool check_validity = true) +{ + typedef typename bg::cs_tag::type cs_tag; + test_simple(geometry, expected_result, check_validity); +} + +template +void test_simple(boost::variant const& variant_geometry, + bool expected_result, + bool check_validity = true) +{ + typedef typename bg::cs_tag::type cs_tag; + test_simple(variant_geometry, expected_result, check_validity); +} + + + //---------------------------------------------------------------------------- diff --git a/test/algorithms/set_operations/difference/test_difference.hpp b/test/algorithms/set_operations/difference/test_difference.hpp index 947d09131..69c296b5a 100644 --- a/test/algorithms/set_operations/difference/test_difference.hpp +++ b/test/algorithms/set_operations/difference/test_difference.hpp @@ -3,8 +3,8 @@ // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2016. -// Modifications copyright (c) 2016, Oracle and/or its affiliates. +// This file was modified by Oracle on 2016, 2017. +// Modifications copyright (c) 2016-2017, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, @@ -131,20 +131,28 @@ std::string test_difference(std::string const& caseid, G1 const& g1, G2 const& g typedef typename bg::coordinate_type::type coordinate_type; boost::ignore_unused(); - bg::model::multi_polygon result; + bg::model::multi_polygon result, result_s; + + typedef typename bg::strategy::relate::services::default_strategy + < + G1, G2 + >::type strategy_type; if (sym) { bg::sym_difference(g1, g2, result); + bg::sym_difference(g1, g2, result_s, strategy_type()); } else { bg::difference(g1, g2, result); + bg::difference(g1, g2, result_s, strategy_type()); } if (settings.remove_spikes) { bg::remove_spikes(result); + bg::remove_spikes(result_s); } std::ostringstream return_string; @@ -233,6 +241,8 @@ std::string test_difference(std::string const& caseid, G1 const& g1, G2 const& g } BOOST_CHECK_CLOSE(area, expected_area, settings.percentage); + + BOOST_CHECK_EQUAL(bg::num_points(result), bg::num_points(result_s)); #endif diff --git a/test/algorithms/set_operations/difference/test_difference_linear_linear.hpp b/test/algorithms/set_operations/difference/test_difference_linear_linear.hpp index 258d5cfb4..2fd934768 100644 --- a/test/algorithms/set_operations/difference/test_difference_linear_linear.hpp +++ b/test/algorithms/set_operations/difference/test_difference_linear_linear.hpp @@ -1,11 +1,12 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html -// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle #ifndef BOOST_GEOMETRY_TEST_DIFFERENCE_LINEAR_LINEAR_HPP #define BOOST_GEOMETRY_TEST_DIFFERENCE_LINEAR_LINEAR_HPP @@ -25,6 +26,23 @@ //================================================================== //================================================================== +template +inline void check_result(Geometry1 const& geometry1, + Geometry2 const& geometry2, + MultiLineString const& mls_output, + MultiLineString const& mls_diff, + std::string const& case_id, + double tolerance) +{ + BOOST_CHECK_MESSAGE( equals::apply(mls_diff, mls_output, tolerance), + "case id: " << case_id + << ", difference L/L: " << bg::wkt(geometry1) + << " " << bg::wkt(geometry2) + << " -> Expected: " << bg::wkt(mls_diff) + << std::setprecision(20) + << " computed: " << bg::wkt(mls_output) ); +} + template < typename Geometry1, typename Geometry2, @@ -52,6 +70,22 @@ private: linestring_vector ls_vector_output; linestring_deque ls_deque_output; + // Check strategy passed explicitly + typedef typename bg::strategy::relate::services::default_strategy + < + Geometry1, Geometry2 + >::type strategy_type; + bg::difference(geometry1, geometry2, mls_output, strategy_type()); + + if (reverse_output_for_checking) + { + bg::reverse(mls_output); + } + + check_result(geometry1, geometry2, mls_output, mls_diff, case_id, tolerance); + + // Check normal behaviour + bg::clear(mls_output); bg::difference(geometry1, geometry2, mls_output); if ( reverse_output_for_checking ) @@ -59,14 +93,8 @@ private: bg::reverse(mls_output); } - BOOST_CHECK_MESSAGE( equals::apply(mls_diff, mls_output, tolerance), - "case id: " << case_id - << ", difference L/L: " << bg::wkt(geometry1) - << " " << bg::wkt(geometry2) - << " -> Expected: " << bg::wkt(mls_diff) - << std::setprecision(20) - << " computed: " << bg::wkt(mls_output) ); - + check_result(geometry1, geometry2, mls_output, mls_diff, case_id, tolerance); + set_operation_output("difference", case_id, geometry1, geometry2, mls_output); diff --git a/test/algorithms/set_operations/intersection/test_intersection.hpp b/test/algorithms/set_operations/intersection/test_intersection.hpp index 58b0d6f28..bc3f16728 100644 --- a/test/algorithms/set_operations/intersection/test_intersection.hpp +++ b/test/algorithms/set_operations/intersection/test_intersection.hpp @@ -3,8 +3,8 @@ // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2016. -// Modifications copyright (c) 2016, Oracle and/or its affiliates. +// This file was modified by Oracle on 2016, 2017. +// Modifications copyright (c) 2016-2017, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, @@ -189,6 +189,18 @@ typename bg::default_area_result::type test_intersection(std::string const& result_type intersection_output; bg::intersection(g1, g2, intersection_output); + check_result(intersection_output, caseid, expected_count, + expected_holes_count, expected_point_count, expected_length_or_area, + settings); + + // Check strategy passed explicitly + typedef typename bg::strategy::relate::services::default_strategy + < + G1, G2 + >::type strategy_type; + intersection_output.clear(); + bg::intersection(g1, g2, intersection_output, strategy_type()); + check_result(intersection_output, caseid, expected_count, expected_holes_count, expected_point_count, expected_length_or_area, settings); diff --git a/test/algorithms/set_operations/intersection/test_intersection_linear_linear.hpp b/test/algorithms/set_operations/intersection/test_intersection_linear_linear.hpp index b4ade8310..f916b023c 100644 --- a/test/algorithms/set_operations/intersection/test_intersection_linear_linear.hpp +++ b/test/algorithms/set_operations/intersection/test_intersection_linear_linear.hpp @@ -1,11 +1,12 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html -// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle #ifndef BOOST_GEOMETRY_TEST_INTERSECTION_LINEAR_LINEAR_HPP #define BOOST_GEOMETRY_TEST_INTERSECTION_LINEAR_LINEAR_HPP @@ -26,6 +27,25 @@ //================================================================== //================================================================== +template +inline void check_result(Geometry1 const& geometry1, + Geometry2 const& geometry2, + MultiLineString const& mls_output, + MultiLineString const& mls_int1, + MultiLineString const& mls_int2, + std::string const& case_id, + double tolerance) +{ + BOOST_CHECK_MESSAGE( equals::apply(mls_int1, mls_output, tolerance) + || equals::apply(mls_int2, mls_output, tolerance), + "case id: " << case_id + << ", intersection L/L: " << bg::wkt(geometry1) + << " " << bg::wkt(geometry2) + << " -> Expected: " << bg::wkt(mls_int1) + << " or: " << bg::wkt(mls_int2) + << " computed: " << bg::wkt(mls_output) ); +} + template < typename Geometry1, typename Geometry2, @@ -53,16 +73,20 @@ private: linestring_vector ls_vector_output; linestring_deque ls_deque_output; + // Check normal behaviour bg::intersection(geometry1, geometry2, mls_output); - BOOST_CHECK_MESSAGE( equals::apply(mls_int1, mls_output, tolerance) - || equals::apply(mls_int2, mls_output, tolerance), - "case id: " << case_id - << ", intersection L/L: " << bg::wkt(geometry1) - << " " << bg::wkt(geometry2) - << " -> Expected: " << bg::wkt(mls_int1) - << " or: " << bg::wkt(mls_int2) - << " computed: " << bg::wkt(mls_output) ); + check_result(geometry1, geometry2, mls_output, mls_int1, mls_int2, case_id, tolerance); + + // Check strategy passed explicitly + typedef typename bg::strategy::relate::services::default_strategy + < + Geometry1, Geometry2 + >::type strategy_type; + bg::clear(mls_output); + bg::intersection(geometry1, geometry2, mls_output, strategy_type()); + + check_result(geometry1, geometry2, mls_output, mls_int1, mls_int2, case_id, tolerance); set_operation_output("intersection", case_id, geometry1, geometry2, mls_output); @@ -109,14 +133,7 @@ private: bg::clear(mls_output); bg::intersection(geometry2, geometry1, mls_output); - BOOST_CHECK_MESSAGE( equals::apply(mls_int1, mls_output, tolerance) - || equals::apply(mls_int2, mls_output, tolerance), - "case id: " << case_id - << ", intersection L/L: " << bg::wkt(geometry1) - << " " << bg::wkt(geometry2) - << " -> Expected: " << bg::wkt(mls_int1) - << " or: " << bg::wkt(mls_int2) - << " computed: " << bg::wkt(mls_output) ); + check_result(geometry1, geometry2, mls_output, mls_int1, mls_int2, case_id, tolerance); #ifdef BOOST_GEOMETRY_TEST_DEBUG std::cout << "Geometry #1: " << bg::wkt(geometry2) << std::endl; diff --git a/test/algorithms/set_operations/sym_difference/test_sym_difference_linear_linear.hpp b/test/algorithms/set_operations/sym_difference/test_sym_difference_linear_linear.hpp index 3676fd9a2..843345247 100644 --- a/test/algorithms/set_operations/sym_difference/test_sym_difference_linear_linear.hpp +++ b/test/algorithms/set_operations/sym_difference/test_sym_difference_linear_linear.hpp @@ -1,11 +1,12 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html -// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle #ifndef BOOST_GEOMETRY_TEST_SYM_DIFFERENCE_LINEAR_LINEAR_HPP #define BOOST_GEOMETRY_TEST_SYM_DIFFERENCE_LINEAR_LINEAR_HPP @@ -24,6 +25,22 @@ //================================================================== //================================================================== +template +inline void check_result(Geometry1 const& geometry1, + Geometry2 const& geometry2, + MultiLineString const& mls_output, + MultiLineString const& mls_sym_diff, + std::string const& case_id, + double tolerance) +{ + BOOST_CHECK_MESSAGE( equals::apply(mls_sym_diff, mls_output, tolerance), + "case id: " << case_id + << ", sym diff L/L: " << bg::wkt(geometry1) + << " " << bg::wkt(geometry2) + << " -> Expected: " << bg::wkt(mls_sym_diff) + << " computed: " << bg::wkt(mls_output) ); +} + template < typename Geometry1, typename Geometry2, @@ -50,15 +67,20 @@ private: linestring_vector ls_vector_output; linestring_deque ls_deque_output; + // Check strategy passed explicitly + typedef typename bg::strategy::relate::services::default_strategy + < + Geometry1, Geometry2 + >::type strategy_type; + bg::sym_difference(geometry1, geometry2, mls_output, strategy_type()); + + check_result(geometry1, geometry2, mls_output, mls_sym_diff, case_id, tolerance); + + // Check normal behaviour + bg::clear(mls_output); bg::sym_difference(geometry1, geometry2, mls_output); - BOOST_CHECK_MESSAGE( equals::apply(mls_sym_diff, mls_output, tolerance), - "case id: " << case_id - << ", sym diff L/L: " << bg::wkt(geometry1) - << " " << bg::wkt(geometry2) - << " -> Expected: " << bg::wkt(mls_sym_diff) - << " computed: " << bg::wkt(mls_output) ); - + check_result(geometry1, geometry2, mls_output, mls_sym_diff, case_id, tolerance); set_operation_output("sym_difference", case_id, geometry1, geometry2, mls_output); diff --git a/test/algorithms/set_operations/union/test_union.hpp b/test/algorithms/set_operations/union/test_union.hpp index 810c7c353..32bc72aba 100644 --- a/test/algorithms/set_operations/union/test_union.hpp +++ b/test/algorithms/set_operations/union/test_union.hpp @@ -3,8 +3,8 @@ // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2015, 2016. -// Modifications copyright (c) 2015-2016 Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2016, 2017. +// Modifications copyright (c) 2015-2017 Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -70,6 +70,18 @@ inline void check_input_validity(std::string const& caseid, int case_index, } #endif +template +inline std::size_t num_points(Range const& rng, bool add_for_open = false) +{ + std::size_t result = 0; + for (typename boost::range_iterator::type it = boost::begin(rng); + it != boost::end(rng); ++it) + { + result += bg::num_points(*it, add_for_open); + } + return result; +} + template void test_union(std::string const& caseid, G1 const& g1, G2 const& g2, int expected_count, int expected_hole_count, @@ -82,7 +94,7 @@ void test_union(std::string const& caseid, G1 const& g1, G2 const& g2, // Declare output (vector of rings or multi_polygon) typedef typename setop_output_type::type result_type; - result_type clip; + result_type clip, clip_s; #if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS) std::cout << "*** UNION " << caseid << std::endl; @@ -93,8 +105,16 @@ void test_union(std::string const& caseid, G1 const& g1, G2 const& g2, check_input_validity(caseid, 1, g2); #endif + // Check normal behaviour bg::union_(g1, g2, clip); + // Check strategy passed explicitly + typedef typename bg::strategy::intersection::services::default_strategy + < + typename bg::cs_tag::type + >::type strategy_type; + bg::union_(g1, g2, clip_s, strategy_type()); + typename bg::default_area_result::type area = 0; std::size_t n = 0; std::size_t holes = 0; @@ -178,6 +198,8 @@ void test_union(std::string const& caseid, G1 const& g1, G2 const& g2, BOOST_CHECK_CLOSE(area, expected_area, settings.percentage); + BOOST_CHECK_EQUAL(num_points(clip), num_points(clip_s)); + #if defined(TEST_WITH_SVG) { bool const ccw = diff --git a/test/algorithms/set_operations/union/test_union_linear_linear.hpp b/test/algorithms/set_operations/union/test_union_linear_linear.hpp index 1b6dda37c..e4357b603 100644 --- a/test/algorithms/set_operations/union/test_union_linear_linear.hpp +++ b/test/algorithms/set_operations/union/test_union_linear_linear.hpp @@ -1,11 +1,12 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html -// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle #ifndef BOOST_GEOMETRY_TEST_UNION_LINEAR_LINEAR_HPP #define BOOST_GEOMETRY_TEST_UNION_LINEAR_LINEAR_HPP @@ -24,6 +25,22 @@ //================================================================== //================================================================== +template +inline void check_result(Geometry1 const& geometry1, + Geometry2 const& geometry2, + MultiLineString const& mls_output, + MultiLineString const& mls_union, + std::string const& case_id, + double tolerance) +{ + BOOST_CHECK_MESSAGE( equals::apply(mls_union, mls_output, tolerance), + "case id: " << case_id + << ", union L/L: " << bg::wkt(geometry1) + << " " << bg::wkt(geometry2) + << " -> Expected: " << bg::wkt(mls_union) + << " computed: " << bg::wkt(mls_output) ); +} + template < typename Geometry1, typename Geometry2, @@ -51,14 +68,20 @@ private: linestring_vector ls_vector_output; linestring_deque ls_deque_output; + // Check strategy passed explicitly + typedef typename bg::strategy::relate::services::default_strategy + < + Geometry1, Geometry2 + >::type strategy_type; + bg::union_(geometry1, geometry2, mls_output, strategy_type()); + + check_result(geometry1, geometry2, mls_output, mls_union1, case_id, tolerance); + + // Check normal behaviour + bg::clear(mls_output); bg::union_(geometry1, geometry2, mls_output); - BOOST_CHECK_MESSAGE( equals::apply(mls_union1, mls_output, tolerance), - "case id: " << case_id - << ", union L/L: " << bg::wkt(geometry1) - << " " << bg::wkt(geometry2) - << " -> Expected: " << bg::wkt(mls_union1) - << " computed: " << bg::wkt(mls_output) ); + check_result(geometry1, geometry2, mls_output, mls_union1, case_id, tolerance); set_operation_output("union", case_id, geometry1, geometry2, mls_output); @@ -101,17 +124,12 @@ private: #endif } - // check the symmetric difference where the order of the two + // check the union where the order of the two // geometries is reversed bg::clear(mls_output); bg::union_(geometry2, geometry1, mls_output); - BOOST_CHECK_MESSAGE( equals::apply(mls_union2, mls_output, tolerance), - "case id: " << case_id - << ", union L/L: " << bg::wkt(geometry2) - << " " << bg::wkt(geometry1) - << " -> Expected: " << bg::wkt(mls_union2) - << " computed: " << bg::wkt(mls_output) ); + check_result(geometry1, geometry2, mls_output, mls_union2, case_id, tolerance); #ifdef BOOST_GEOMETRY_TEST_DEBUG std::cout << "Geometry #1: " << bg::wkt(geometry2) << std::endl; diff --git a/test/algorithms/test_is_valid.hpp b/test/algorithms/test_is_valid.hpp index 02ebe164e..2776327d9 100644 --- a/test/algorithms/test_is_valid.hpp +++ b/test/algorithms/test_is_valid.hpp @@ -1,9 +1,10 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -287,7 +288,7 @@ struct validity_tester_linear { bool const irrelevant = true; bg::is_valid_default_policy visitor; - return bg::is_valid(geometry, visitor); + return bg::is_valid(geometry, visitor, bg::default_strategy()); } template @@ -296,7 +297,7 @@ struct validity_tester_linear bool const irrelevant = true; std::ostringstream oss; bg::failing_reason_policy visitor(oss); - bg::is_valid(geometry, visitor); + bg::is_valid(geometry, visitor, bg::default_strategy()); return oss.str(); } }; @@ -309,7 +310,7 @@ struct validity_tester_areal static inline bool apply(Geometry const& geometry) { bg::is_valid_default_policy visitor; - return bg::is_valid(geometry, visitor); + return bg::is_valid(geometry, visitor, bg::default_strategy()); } template @@ -317,7 +318,7 @@ struct validity_tester_areal { std::ostringstream oss; bg::failing_reason_policy visitor(oss); - bg::is_valid(geometry, visitor); + bg::is_valid(geometry, visitor, bg::default_strategy()); return oss.str(); } From b03da047a8a95cb7ab93769b3206772887a0eb7d Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 25 Feb 2017 18:50:13 +0100 Subject: [PATCH 40/59] [index] Add workaround for libstdc++ bug (gcc 4.8.2) - segfault in nth_element. --- .../index/detail/algorithms/nth_element.hpp | 62 +++++++++++++++++++ .../index/detail/rtree/pack_create.hpp | 5 +- .../detail/rtree/rstar/choose_next_node.hpp | 5 +- .../rtree/rstar/redistribute_elements.hpp | 12 ++-- 4 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 include/boost/geometry/index/detail/algorithms/nth_element.hpp diff --git a/include/boost/geometry/index/detail/algorithms/nth_element.hpp b/include/boost/geometry/index/detail/algorithms/nth_element.hpp new file mode 100644 index 000000000..201180ae3 --- /dev/null +++ b/include/boost/geometry/index/detail/algorithms/nth_element.hpp @@ -0,0 +1,62 @@ +// Boost.Geometry Index +// +// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland. +// +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_INDEX_DETAIL_ALGORITHMS_NTH_ELEMENT_HPP +#define BOOST_GEOMETRY_INDEX_DETAIL_ALGORITHMS_NTH_ELEMENT_HPP + +#include + +namespace boost { namespace geometry { namespace index { namespace detail { + +// See https://svn.boost.org/trac/boost/ticket/12861 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58800 +// https://gcc.gnu.org/develop.html#timeline +// 20120920 4.7.2 - no bug +// 20130322 4.8.0 - no bug +// 20130411 4.7.3 - no bug +// 20130531 4.8.1 - no bug +// 20131016 4.8.2 - bug +// 20140422 4.9.0 - fixed +// 20140522 4.8.3 - fixed +// 20140612 4.7.4 - fixed +// 20140716 4.9.1 - fixed +#if defined(__GLIBCXX__) && (__GLIBCXX__ == 20131016) + +#warning "std::nth_element replaced with std::sort, libstdc++ bug workaround."; + +template +void nth_element(RandomIt first, RandomIt , RandomIt last) +{ + std::sort(first, last); +} + +template +void nth_element(RandomIt first, RandomIt , RandomIt last, Compare comp) +{ + std::sort(first, last, comp); +} + +#else + +template +void nth_element(RandomIt first, RandomIt nth, RandomIt last) +{ + std::nth_element(first, nth, last); +} + +template +void nth_element(RandomIt first, RandomIt nth, RandomIt last, Compare comp) +{ + std::nth_element(first, nth, last, comp); +} + +#endif + +}}}} // namespace boost::geometry::index::detail + +#endif // BOOST_GEOMETRY_INDEX_DETAIL_ALGORITHMS_NTH_ELEMENT_HPP diff --git a/include/boost/geometry/index/detail/rtree/pack_create.hpp b/include/boost/geometry/index/detail/rtree/pack_create.hpp index d1491b8d4..2d3903a7b 100644 --- a/include/boost/geometry/index/detail/rtree/pack_create.hpp +++ b/include/boost/geometry/index/detail/rtree/pack_create.hpp @@ -2,7 +2,7 @@ // // R-tree initial packing // -// Copyright (c) 2011-2015 Adam Wulkiewicz, Lodz, Poland. +// Copyright (c) 2011-2017 Adam Wulkiewicz, Lodz, Poland. // // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -13,6 +13,7 @@ #include #include +#include #include @@ -67,7 +68,7 @@ struct nth_element_and_half_boxes { if ( I == dim_index ) { - std::nth_element(first, median, last, point_entries_comparer()); + index::detail::nth_element(first, median, last, point_entries_comparer()); geometry::convert(box, left); geometry::convert(box, right); diff --git a/include/boost/geometry/index/detail/rtree/rstar/choose_next_node.hpp b/include/boost/geometry/index/detail/rtree/rstar/choose_next_node.hpp index 7a96986a2..89697b594 100644 --- a/include/boost/geometry/index/detail/rtree/rstar/choose_next_node.hpp +++ b/include/boost/geometry/index/detail/rtree/rstar/choose_next_node.hpp @@ -2,7 +2,7 @@ // // R-tree R*-tree next node choosing algorithm implementation // -// Copyright (c) 2011-2014 Adam Wulkiewicz, Lodz, Poland. +// Copyright (c) 2011-2017 Adam Wulkiewicz, Lodz, Poland. // // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -112,7 +113,7 @@ private: first_n_children_count = overlap_cost_threshold; // rearrange by content_diff // in order to calculate nearly minimum overlap cost - std::nth_element(children_contents.begin(), children_contents.begin() + first_n_children_count, children_contents.end(), content_diff_less); + index::detail::nth_element(children_contents.begin(), children_contents.begin() + first_n_children_count, children_contents.end(), content_diff_less); } // calculate minimum or nearly minimum overlap cost diff --git a/include/boost/geometry/index/detail/rtree/rstar/redistribute_elements.hpp b/include/boost/geometry/index/detail/rtree/rstar/redistribute_elements.hpp index 8f270537f..187d37fac 100644 --- a/include/boost/geometry/index/detail/rtree/rstar/redistribute_elements.hpp +++ b/include/boost/geometry/index/detail/rtree/rstar/redistribute_elements.hpp @@ -2,7 +2,7 @@ // // R-tree R*-tree split algorithm implementation // -// Copyright (c) 2011-2014 Adam Wulkiewicz, Lodz, Poland. +// Copyright (c) 2011-2017 Adam Wulkiewicz, Lodz, Poland. // // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -12,8 +12,9 @@ #define BOOST_GEOMETRY_INDEX_DETAIL_RTREE_RSTAR_REDISTRIBUTE_ELEMENTS_HPP #include -#include #include +#include +#include #include @@ -122,8 +123,9 @@ struct choose_split_axis_and_index_for_corner // { // typename Elements::iterator f = elements_copy.begin() + index_first; // typename Elements::iterator l = elements_copy.begin() + index_last; -// std::nth_element(elements_copy.begin(), f, elements_copy.end(), elements_less); // MAY THROW, BASIC (copy) -// std::nth_element(f, l, elements_copy.end(), elements_less); // MAY THROW, BASIC (copy) +// // NOTE: for stdlibc++ shipped with gcc 4.8.2 std::nth_element is replaced with std::sort anyway +// index::detail::nth_element(elements_copy.begin(), f, elements_copy.end(), elements_less); // MAY THROW, BASIC (copy) +// index::detail::nth_element(f, l, elements_copy.end(), elements_less); // MAY THROW, BASIC (copy) // std::sort(f, l, elements_less); // MAY THROW, BASIC (copy) // } @@ -349,7 +351,7 @@ struct nth_element typedef typename tag::type indexable_tag; element_axis_corner_less less(tr); - std::nth_element(elements.begin(), elements.begin() + index, elements.end(), less); // MAY THROW, BASIC (copy) + index::detail::nth_element(elements.begin(), elements.begin() + index, elements.end(), less); // MAY THROW, BASIC (copy) } } }; From ca00075d59d269880ce4417404ab30c4b9a4f839 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 25 Feb 2017 18:50:55 +0100 Subject: [PATCH 41/59] [doc] Update 1.64.0 release notes (ticket). --- doc/release_notes.qbk | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk index 56fcb8ecc..83909033e 100644 --- a/doc/release_notes.qbk +++ b/doc/release_notes.qbk @@ -6,8 +6,8 @@ Copyright (c) 2009-2016 Mateusz Loskot , London, UK. Copyright (c) 2011-2016 Adam Wulkiewicz, Lodz, Poland. - This file was modified by Oracle on 2015, 2016. - Modifications copyright (c) 2015-2016, Oracle and/or its affiliates. + This file was modified by Oracle on 2015, 2017. + Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -38,6 +38,7 @@ [*Solved tickets] * [@https://svn.boost.org/trac/boost/ticket/12566 12566] Ambiguous template instantiation in equal_to<> when pair value contains raw pointer +* [@https://svn.boost.org/trac/boost/ticket/12861 12566] Segmentation fault in stdlibc++ (gcc 4.8.2) affecting rtree [/=================] [heading Boost 1.63] From f9d9cc2178bc07e78eb72a0dcddc78fa1d6f650b Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 26 Feb 2017 01:37:54 +0100 Subject: [PATCH 42/59] [relops] Alter doc comments (versions taking strategies). --- include/boost/geometry/algorithms/crosses.hpp | 1 + .../algorithms/detail/disjoint/interface.hpp | 1 + .../algorithms/detail/envelope/interface.hpp | 29 ++++++++++++++++++- .../algorithms/detail/expand/interface.hpp | 3 ++ .../detail/overlay/self_turn_points.hpp | 1 + .../algorithms/detail/relate/interface.hpp | 1 + .../algorithms/detail/relation/interface.hpp | 1 + include/boost/geometry/algorithms/equals.hpp | 3 +- .../boost/geometry/algorithms/intersects.hpp | 2 +- .../boost/geometry/algorithms/overlaps.hpp | 1 + include/boost/geometry/algorithms/touches.hpp | 2 +- 11 files changed, 40 insertions(+), 5 deletions(-) diff --git a/include/boost/geometry/algorithms/crosses.hpp b/include/boost/geometry/algorithms/crosses.hpp index 8a906b2e1..c9e3651ab 100644 --- a/include/boost/geometry/algorithms/crosses.hpp +++ b/include/boost/geometry/algorithms/crosses.hpp @@ -228,6 +228,7 @@ namespace resolve_variant \param strategy \param_strategy{crosses} \return \return_check2{crosses} +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/crosses.qbk]} */ template diff --git a/include/boost/geometry/algorithms/detail/disjoint/interface.hpp b/include/boost/geometry/algorithms/detail/disjoint/interface.hpp index 9b505e560..64898e35f 100644 --- a/include/boost/geometry/algorithms/detail/disjoint/interface.hpp +++ b/include/boost/geometry/algorithms/detail/disjoint/interface.hpp @@ -203,6 +203,7 @@ struct disjoint \param strategy \param_strategy{disjoint} \return \return_check2{are disjoint} +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/disjoint.qbk]} */ template diff --git a/include/boost/geometry/algorithms/detail/envelope/interface.hpp b/include/boost/geometry/algorithms/detail/envelope/interface.hpp index d768c2bbd..8e9c35b39 100644 --- a/include/boost/geometry/algorithms/detail/envelope/interface.hpp +++ b/include/boost/geometry/algorithms/detail/envelope/interface.hpp @@ -135,6 +135,7 @@ struct envelope > \param mbr \param_box \param_set{envelope} \param strategy \param_strategy{envelope} +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/envelope.qbk]} \qbk{ [heading Example] @@ -142,7 +143,7 @@ struct envelope > } */ template -inline void envelope(Geometry const& geometry, Box& mbr, Strategy& strategy) +inline void envelope(Geometry const& geometry, Box& mbr, Strategy const& strategy) { resolve_variant::envelope::apply(geometry, mbr, strategy); } @@ -169,6 +170,32 @@ inline void envelope(Geometry const& geometry, Box& mbr) } +/*! +\brief \brief_calc{envelope} +\ingroup envelope +\details \details_calc{return_envelope,\det_envelope}. \details_return{envelope} +\tparam Box \tparam_box +\tparam Geometry \tparam_geometry +\tparam Strategy \tparam_strategy{Envelope} +\param geometry \param_geometry +\param strategy \param_strategy{envelope} +\return \return_calc{envelope} + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/envelope.qbk]} +\qbk{ +[heading Example] +[return_envelope] [return_envelope_output] +} +*/ +template +inline Box return_envelope(Geometry const& geometry, Strategy const& strategy) +{ + Box mbr; + resolve_variant::envelope::apply(geometry, mbr, strategy); + return mbr; +} + /*! \brief \brief_calc{envelope} \ingroup envelope diff --git a/include/boost/geometry/algorithms/detail/expand/interface.hpp b/include/boost/geometry/algorithms/detail/expand/interface.hpp index dc383d4b9..5aacd8e72 100644 --- a/include/boost/geometry/algorithms/detail/expand/interface.hpp +++ b/include/boost/geometry/algorithms/detail/expand/interface.hpp @@ -160,10 +160,13 @@ inline void expand(Box& box, Geometry const& geometry, \ingroup expand \tparam Box type of the box \tparam Geometry \tparam_geometry +\tparam Strategy \tparam_strategy{expand} \param box box to be expanded using another geometry, mutable \param geometry \param_geometry geometry which envelope (bounding box) +\param strategy \param_strategy{expand} will be added to the box +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/expand.qbk]} */ template diff --git a/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp b/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp index 61f2c737b..8540ef98a 100644 --- a/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp @@ -272,6 +272,7 @@ struct self_get_turn_points \tparam Turns type of intersection container (e.g. vector of "intersection/turn point"'s) \param geometry geometry + \param strategy strategy to be used \param robust_policy policy to handle robustness issues \param turns container which will contain intersection points \param interrupt_policy policy determining if process is stopped diff --git a/include/boost/geometry/algorithms/detail/relate/interface.hpp b/include/boost/geometry/algorithms/detail/relate/interface.hpp index 29d346681..3575fe2bc 100644 --- a/include/boost/geometry/algorithms/detail/relate/interface.hpp +++ b/include/boost/geometry/algorithms/detail/relate/interface.hpp @@ -375,6 +375,7 @@ struct relate< \param strategy \param_strategy{relate} \return true if the relation is compatible with the mask, false otherwise. +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/relate.qbk]} */ template diff --git a/include/boost/geometry/algorithms/detail/relation/interface.hpp b/include/boost/geometry/algorithms/detail/relation/interface.hpp index 00a784b3d..83d27ed72 100644 --- a/include/boost/geometry/algorithms/detail/relation/interface.hpp +++ b/include/boost/geometry/algorithms/detail/relation/interface.hpp @@ -175,6 +175,7 @@ struct relation \param strategy \param_strategy{relation} \return The DE-9IM matrix expressing the relation between geometries. +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/relation.qbk]} */ template diff --git a/include/boost/geometry/algorithms/equals.hpp b/include/boost/geometry/algorithms/equals.hpp index 47eb7134c..cd7b72500 100644 --- a/include/boost/geometry/algorithms/equals.hpp +++ b/include/boost/geometry/algorithms/equals.hpp @@ -601,8 +601,8 @@ struct equals< \param strategy \param_strategy{equals} \return \return_check2{are spatially equal} +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/equals.qbk]} - */ template inline bool equals(Geometry1 const& geometry1, @@ -631,7 +631,6 @@ inline bool equals(Geometry1 const& geometry1, \return \return_check2{are spatially equal} \qbk{[include reference/algorithms/equals.qbk]} - */ template inline bool equals(Geometry1 const& geometry1, Geometry2 const& geometry2) diff --git a/include/boost/geometry/algorithms/intersects.hpp b/include/boost/geometry/algorithms/intersects.hpp index e54b00503..12ae169f1 100644 --- a/include/boost/geometry/algorithms/intersects.hpp +++ b/include/boost/geometry/algorithms/intersects.hpp @@ -97,7 +97,7 @@ inline bool intersects(Geometry const& geometry) \param strategy \param_strategy{intersects} \return \return_check2{intersect each other} -\qbk{distinguish,two geometries} +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/intersects.qbk]} */ template diff --git a/include/boost/geometry/algorithms/overlaps.hpp b/include/boost/geometry/algorithms/overlaps.hpp index 800d6dac1..bedf17599 100644 --- a/include/boost/geometry/algorithms/overlaps.hpp +++ b/include/boost/geometry/algorithms/overlaps.hpp @@ -180,6 +180,7 @@ struct overlaps \param strategy \param_strategy{overlaps} \return \return_check2{overlap} +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/overlaps.qbk]} */ template diff --git a/include/boost/geometry/algorithms/touches.hpp b/include/boost/geometry/algorithms/touches.hpp index 83f2b1dfa..49e104d25 100644 --- a/include/boost/geometry/algorithms/touches.hpp +++ b/include/boost/geometry/algorithms/touches.hpp @@ -687,7 +687,7 @@ inline bool touches(Geometry1 const& geometry1, Geometry2 const& geometry2) \param strategy \param_strategy{touches} \return \return_check2{touch each other} -\qbk{distinguish,two geometries} +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/touches.qbk]} */ template From cc472a8c3c9f12f4cea9dcf41a68d84b6234243a Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 26 Feb 2017 01:41:05 +0100 Subject: [PATCH 43/59] [doc] Generate pages for relops versions supporting strategies. --- doc/geometry.qbk | 11 ++++++----- doc/html/index.html | 22 ++++++++++++++-------- doc/quickref.xml | 2 +- doc/reference.qbk | 32 ++++++++++++++++++++++++-------- doc/release_notes.qbk | 8 ++++---- 5 files changed, 49 insertions(+), 26 deletions(-) diff --git a/doc/geometry.qbk b/doc/geometry.qbk index 442604534..6c30c833a 100644 --- a/doc/geometry.qbk +++ b/doc/geometry.qbk @@ -1,9 +1,10 @@ [/============================================================================ Boost.Geometry (aka GGL, Generic Geometry Library) - Copyright (c) 2009-2015 Barend Gehrels, Amsterdam, the Netherlands. - Copyright (c) 2009-2015 Mateusz Loskot, London, UK. - Copyright (c) 2009-2015 Bruno Lalande, Paris, France. + Copyright (c) 2009-2017 Barend Gehrels, Amsterdam, the Netherlands. + Copyright (c) 2009-2017 Mateusz Loskot, London, UK. + Copyright (c) 2009-2017 Bruno Lalande, Paris, France. + Copyright (c) 2011-2017 Adam Wulkiewicz, Lodz, Poland. Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -12,8 +13,8 @@ [library Geometry [quickbook 1.5] - [authors [Gehrels, Barend], [Lalande, Bruno], [Loskot, Mateusz], [Wulkiewicz, Adam], [Karavelas, Menelaos]] - [copyright 2009-2015 Barend Gehrels, Bruno Lalande, Mateusz Loskot, Adam Wulkiewicz, Oracle and/or its affiliates] + [authors [Gehrels, Barend], [Lalande, Bruno], [Loskot, Mateusz], [Wulkiewicz, Adam], [Karavelas, Menelaos], [Fisikopoulos, Vissarion]] + [copyright 2009-2017 Barend Gehrels, Bruno Lalande, Mateusz Loskot, Adam Wulkiewicz, Oracle and/or its affiliates] [purpose Documentation of Boost.Geometry library] [license Distributed under the Boost Software License, Version 1.0. diff --git a/doc/html/index.html b/doc/html/index.html index 841fbf5b1..d8afcf098 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -37,8 +37,12 @@

Menelaos Karavelas

-
+

+Vissarion Fisikopoulos +

+
- +

Last revised: July 17, 2014 at 20:45:09 GMT

Last revised: February 26, 2017 at 00:30:21 GMT


diff --git a/doc/quickref.xml b/doc/quickref.xml index 16047e897..5b095811a 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -521,7 +521,7 @@ Area strategy::area::surveyor - strategy::area::huiller + diff --git a/doc/reference.qbk b/doc/reference.qbk index a39f0b5ff..f73060181 100644 --- a/doc/reference.qbk +++ b/doc/reference.qbk @@ -1,15 +1,16 @@ [/============================================================================ Boost.Geometry (aka GGL, Generic Geometry Library) - Copyright (c) 2009-2014 Barend Gehrels, Amsterdam, the Netherlands. - Copyright (c) 2009-2014 Mateusz Loskot, London, UK. - Copyright (c) 2009-2014 Bruno Lalande, Paris, France. - Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. + Copyright (c) 2009-2017 Barend Gehrels, Amsterdam, the Netherlands. + Copyright (c) 2009-2017 Mateusz Loskot, London, UK. + Copyright (c) 2009-2017 Bruno Lalande, Paris, France. + Copyright (c) 2013-2017 Adam Wulkiewicz, Lodz, Poland. - This file was modified by Oracle on 2014. - Modifications copyright (c) 2014, Oracle and/or its affiliates. + This file was modified by Oracle on 2014, 2017. + Modifications copyright (c) 2014-2017, Oracle and/or its affiliates. Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -94,10 +95,15 @@ [include generated/covered_by.qbk] [endsect] +[section:crosses crosses] [include generated/crosses.qbk] +[endsect] [include generated/difference.qbk] + +[section:disjoint disjoint] [include generated/disjoint.qbk] +[endsect] [section:distance distance] [include generated/distance.qbk] @@ -107,8 +113,13 @@ [include generated/envelope.qbk] [endsect] +[section:equals equals] [include generated/equals.qbk] +[endsect] + +[section:expand expand] [include generated/expand.qbk] +[endsect] [section:for_each for_each] [include generated/for_each.qbk] @@ -143,16 +154,21 @@ [include generated/num_points.qbk] [include generated/num_segments.qbk] - +[section:overlaps overlaps] [include generated/overlaps.qbk] +[endsect] [section:perimeter perimeter] [include generated/perimeter.qbk] [endsect] +[section:relate relate] [include generated/relate.qbk] +[endsect] +[section:relation relation] [include generated/relation.qbk] +[endsect] [include generated/reverse.qbk] @@ -317,7 +333,7 @@ [include generated/distance_cross_track.qbk] [include generated/distance_cross_track_point_box.qbk] [include generated/area_surveyor.qbk] -[include generated/area_huiller.qbk] +[/include generated/area_huiller.qbk] [include generated/buffer_join_round.qbk] [include generated/buffer_join_miter.qbk] [include generated/buffer_end_round.qbk] diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk index 83909033e..95d580be1 100644 --- a/doc/release_notes.qbk +++ b/doc/release_notes.qbk @@ -1,10 +1,10 @@ [/============================================================================ Boost.Geometry (aka GGL, Generic Geometry Library) - Copyright (c) 2009-2016 Barend Gehrels, Geodan, Amsterdam, the Netherlands. - Copyright (c) 2009-2016 Bruno Lalande, Paris, France. - Copyright (c) 2009-2016 Mateusz Loskot , London, UK. - Copyright (c) 2011-2016 Adam Wulkiewicz, Lodz, Poland. + Copyright (c) 2009-2017 Barend Gehrels, Geodan, Amsterdam, the Netherlands. + Copyright (c) 2009-2017 Bruno Lalande, Paris, France. + Copyright (c) 2009-2017 Mateusz Loskot , London, UK. + Copyright (c) 2011-2017 Adam Wulkiewicz, Lodz, Poland. This file was modified by Oracle on 2015, 2017. Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. From 98546ec1dd271d861974573b1c5efb2d2273802a Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Mon, 27 Feb 2017 19:12:41 +0100 Subject: [PATCH 44/59] [strategies] Add general geographic distance strategy taking formula and use it in existing strategies. --- .../strategies/geographic/distance.hpp | 194 ++++++++++++++++++ .../geographic/distance_andoyer.hpp | 84 ++------ .../strategies/geographic/distance_thomas.hpp | 66 ++---- .../geographic/distance_vincenty.hpp | 66 ++---- .../boost/geometry/strategies/strategies.hpp | 1 + 5 files changed, 244 insertions(+), 167 deletions(-) create mode 100644 include/boost/geometry/strategies/geographic/distance.hpp diff --git a/include/boost/geometry/strategies/geographic/distance.hpp b/include/boost/geometry/strategies/geographic/distance.hpp new file mode 100644 index 000000000..9be21d49c --- /dev/null +++ b/include/boost/geometry/strategies/geographic/distance.hpp @@ -0,0 +1,194 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2016 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014-2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + + +#ifndef BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_DISTANCE_HPP +#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_DISTANCE_HPP + + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace distance +{ + +template +< + template class InverseFormula = formula::andoyer_inverse, + typename Spheroid = srs::spheroid, + typename CalculationType = void +> +class geographic +{ +public : + template + struct calculation_type + : promote_floating_point + < + typename select_calculation_type + < + Point1, + Point2, + CalculationType + >::type + > + {}; + + typedef Spheroid model_type; + + inline geographic() + : m_spheroid() + {} + + explicit inline geographic(Spheroid const& spheroid) + : m_spheroid(spheroid) + {} + + template + inline typename calculation_type::type + apply(Point1 const& point1, Point2 const& point2) const + { + return InverseFormula + < + typename calculation_type::type, + true, false, false, false, false + >::apply(get_as_radian<0>(point1), get_as_radian<1>(point1), + get_as_radian<0>(point2), get_as_radian<1>(point2), + m_spheroid).distance; + } + + inline Spheroid const& model() const + { + return m_spheroid; + } + +private : + Spheroid m_spheroid; +}; + + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS +namespace services +{ + +template +< + template class Formula, + typename Spheroid, + typename CalculationType +> +struct tag > +{ + typedef strategy_tag_distance_point_point type; +}; + + +template +< + template class Formula, + typename Spheroid, + typename CalculationType, + typename P1, + typename P2 +> +struct return_type, P1, P2> + : geographic::template calculation_type +{}; + + +template +< + template class Formula, + typename Spheroid, + typename CalculationType +> +struct comparable_type > +{ + typedef geographic type; +}; + + +template +< + template class Formula, + typename Spheroid, + typename CalculationType +> +struct get_comparable > +{ + static inline geographic + apply(geographic const& input) + { + return input; + } +}; + +template +< + template class Formula, + typename Spheroid, + typename CalculationType, + typename P1, + typename P2 +> +struct result_from_distance, P1, P2> +{ + template + static inline typename return_type, P1, P2>::type + apply(geographic const& , T const& value) + { + return value; + } +}; + + +template +struct default_strategy +{ + typedef strategy::distance::geographic + < + formula::andoyer_inverse, + srs::spheroid + < + typename select_coordinate_type::type + > + > type; +}; + + +} // namespace services +#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS + + +}} // namespace strategy::distance + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_DISTANCE_HPP diff --git a/include/boost/geometry/strategies/geographic/distance_andoyer.hpp b/include/boost/geometry/strategies/geographic/distance_andoyer.hpp index d04a90956..5ff54d53c 100644 --- a/include/boost/geometry/strategies/geographic/distance_andoyer.hpp +++ b/include/boost/geometry/strategies/geographic/distance_andoyer.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2016 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014, 2016. -// Modifications copyright (c) 2014-2016 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014, 2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -11,23 +11,13 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#ifndef BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_ANDOYER_HPP -#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_ANDOYER_HPP +#ifndef BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_DISTANCE_DETAIL_HPP +#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_DISTANCE_DETAIL_HPP -#include -#include -#include -#include - #include -#include -#include - -#include -#include -#include +#include namespace boost { namespace geometry @@ -60,51 +50,24 @@ template typename CalculationType = void > class andoyer + : public strategy::distance::geographic + < + formula::andoyer_inverse, Spheroid, CalculationType + > { + typedef strategy::distance::geographic + < + formula::andoyer_inverse, Spheroid, CalculationType + > base_type; + public : - template - struct calculation_type - : promote_floating_point - < - typename select_calculation_type - < - Point1, - Point2, - CalculationType - >::type - > - {}; - - typedef Spheroid model_type; - inline andoyer() - : m_spheroid() + : base_type() {} explicit inline andoyer(Spheroid const& spheroid) - : m_spheroid(spheroid) + : base_type(spheroid) {} - - template - inline typename calculation_type::type - apply(Point1 const& point1, Point2 const& point2) const - { - return geometry::formula::andoyer_inverse - < - typename calculation_type::type, - true, false - >::apply(get_as_radian<0>(point1), get_as_radian<1>(point1), - get_as_radian<0>(point2), get_as_radian<1>(point2), - m_spheroid).distance; - } - - inline Spheroid const& model() const - { - return m_spheroid; - } - -private : - Spheroid m_spheroid; }; @@ -153,19 +116,6 @@ struct result_from_distance, P1, P2> }; -template -struct default_strategy -{ - typedef strategy::distance::andoyer - < - srs::spheroid - < - typename select_coordinate_type::type - > - > type; -}; - - } // namespace services #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS @@ -176,4 +126,4 @@ struct default_strategy -#include - -#include - -#include -#include - #include +#include + + namespace boost { namespace geometry { @@ -49,53 +44,24 @@ template typename CalculationType = void > class thomas + : public strategy::distance::geographic + < + formula::thomas_inverse, Spheroid, CalculationType + > { + typedef strategy::distance::geographic + < + formula::thomas_inverse, Spheroid, CalculationType + > base_type; + public : - template - struct calculation_type - : promote_floating_point - < - typename select_calculation_type - < - Point1, - Point2, - CalculationType - >::type - > - {}; - - typedef Spheroid model_type; - inline thomas() - : m_spheroid() + : base_type() {} explicit inline thomas(Spheroid const& spheroid) - : m_spheroid(spheroid) + : base_type(spheroid) {} - - template - inline typename calculation_type::type - apply(Point1 const& point1, Point2 const& point2) const - { - return geometry::formula::thomas_inverse - < - typename calculation_type::type, - true, false - >::apply(get_as_radian<0>(point1), - get_as_radian<1>(point1), - get_as_radian<0>(point2), - get_as_radian<1>(point2), - m_spheroid).distance; - } - - inline Spheroid const& model() const - { - return m_spheroid; - } - -private : - Spheroid m_spheroid; }; #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS diff --git a/include/boost/geometry/strategies/geographic/distance_vincenty.hpp b/include/boost/geometry/strategies/geographic/distance_vincenty.hpp index e79e9aeb4..d3f3663ad 100644 --- a/include/boost/geometry/strategies/geographic/distance_vincenty.hpp +++ b/include/boost/geometry/strategies/geographic/distance_vincenty.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014, 2016. -// Modifications copyright (c) 2014-2016 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014-2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -15,16 +15,11 @@ #define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_VINCENTY_HPP -#include -#include - -#include - -#include -#include - #include +#include + + namespace boost { namespace geometry { @@ -51,53 +46,24 @@ template typename CalculationType = void > class vincenty + : public strategy::distance::geographic + < + formula::vincenty_inverse, Spheroid, CalculationType + > { -public : - template - struct calculation_type - : promote_floating_point - < - typename select_calculation_type - < - Point1, - Point2, - CalculationType - >::type - > - {}; - - typedef Spheroid model_type; + typedef strategy::distance::geographic + < + formula::vincenty_inverse, Spheroid, CalculationType + > base_type; +public: inline vincenty() - : m_spheroid() + : base_type() {} explicit inline vincenty(Spheroid const& spheroid) - : m_spheroid(spheroid) + : base_type(spheroid) {} - - template - inline typename calculation_type::type - apply(Point1 const& point1, Point2 const& point2) const - { - return geometry::formula::vincenty_inverse - < - typename calculation_type::type, - true, false - >::apply(get_as_radian<0>(point1), - get_as_radian<1>(point1), - get_as_radian<0>(point2), - get_as_radian<1>(point2), - m_spheroid).distance; - } - - inline Spheroid const& model() const - { - return m_spheroid; - } - -private : - Spheroid m_spheroid; }; #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS diff --git a/include/boost/geometry/strategies/strategies.hpp b/include/boost/geometry/strategies/strategies.hpp index bc672c728..c6c5f5ddd 100644 --- a/include/boost/geometry/strategies/strategies.hpp +++ b/include/boost/geometry/strategies/strategies.hpp @@ -78,6 +78,7 @@ #include #include +#include #include #include #include From 3f7b2ecb354b37c50a7952e25517caa96839e3d8 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Mon, 27 Feb 2017 19:30:02 +0100 Subject: [PATCH 45/59] [strategies] Add area and distance strategies getters to geodesic intersection strategy. Also fix typos. --- .../geographic/geodesic_intersection.hpp | 65 +++++++++++++++---- .../strategies/spherical/intersection.hpp | 16 ++--- 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp b/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp index 1353e2fa1..27581e7af 100644 --- a/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp +++ b/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp @@ -32,6 +32,8 @@ #include +#include +#include #include #include #include @@ -94,6 +96,41 @@ struct relate_geodesic_segments return strategy_type(get_side_strategy()); } + template + struct area_strategy + { + typedef area::geographic + < + typename point_type::type, + Inverse, + Order, + true, + false, + Spheroid, + CalculationType + > type; + }; + + template + static inline typename area_strategy::type get_area_strategy() + { + typedef typename area_strategy::type strategy_type; + return strategy_type(m_spheroid); + } + + template + struct distance_strategy + { + typedef distance::geographic type; + }; + + template + static inline typename distance_strategy::type get_distance_strategy() + { + typedef typename distance_strategy::type strategy_type; + return strategy_type(m_spheroid); + } + enum intersection_point_flag { ipi_inters = 0, ipi_at_a1, ipi_at_a2, ipi_at_b1, ipi_at_b2 }; template @@ -240,9 +277,7 @@ private: ::type calc_t; // normalized spheroid - srs::spheroid spheroid(calc_t(1), - calc_t(get_radius<2>(m_spheroid)) // b/a - / calc_t(get_radius<0>(m_spheroid))); + srs::spheroid spheroid = normalized_spheroid(m_spheroid); // TODO: check only 2 first coordinates here? using geometry::detail::equals::equals_point_point; @@ -331,11 +366,11 @@ private: { if (a_is_point) { - return collinear_one_degenerted(a, true, b1, b2, a1, a2, res_b1_b2, res_b1_a1, is_b_reversed); + return collinear_one_degenerated(a, true, b1, b2, a1, a2, res_b1_b2, res_b1_a1, is_b_reversed); } else if (b_is_point) { - return collinear_one_degenerted(b, false, a1, a2, b1, b2, res_a1_a2, res_a1_b1, is_a_reversed); + return collinear_one_degenerated(b, false, a1, a2, b1, b2, res_a1_a2, res_a1_b1, is_a_reversed); } else { @@ -497,12 +532,12 @@ private: template static inline typename Policy::return_type - collinear_one_degenerted(Segment const& segment, bool degenerated_a, - Point1 const& a1, Point1 const& a2, - Point2 const& b1, Point2 const& b2, - ResultInverse const& res_a1_a2, - ResultInverse const& res_a1_bi, - bool is_other_reversed) + collinear_one_degenerated(Segment const& segment, bool degenerated_a, + Point1 const& a1, Point1 const& a2, + Point2 const& b1, Point2 const& b2, + ResultInverse const& res_a1_a2, + ResultInverse const& res_a1_bi, + bool is_other_reversed) { CalcT dist_1_2, dist_1_o; if (! calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_bi, dist_1_2, dist_1_o)) @@ -837,6 +872,14 @@ private: ip_flag; } + template + static inline srs::spheroid normalized_spheroid(Spheroid const& spheroid) + { + return srs::spheroid(CalcT(1), + CalcT(get_radius<2>(spheroid)) // b/a + / CalcT(get_radius<0>(spheroid))); + } + private: Spheroid m_spheroid; }; diff --git a/include/boost/geometry/strategies/spherical/intersection.hpp b/include/boost/geometry/strategies/spherical/intersection.hpp index c82177b03..0f25ffbdd 100644 --- a/include/boost/geometry/strategies/spherical/intersection.hpp +++ b/include/boost/geometry/strategies/spherical/intersection.hpp @@ -392,12 +392,12 @@ struct relate_ecef_segments { if (a_is_point) { - return collinear_one_degenerted(a, true, b1, b2, a1, a2, b1v, b2v, plane2, a1v); + return collinear_one_degenerated(a, true, b1, b2, a1, a2, b1v, b2v, plane2, a1v); } else if (b_is_point) { // b2 used to be consistent with (degenerated) checks above (is it needed?) - return collinear_one_degenerted(b, false, a1, a2, b1, b2, a1v, a2v, plane1, b1v); + return collinear_one_degenerated(b, false, a1, a2, b1, b2, a1v, a2v, plane1, b1v); } else { @@ -505,12 +505,12 @@ struct relate_ecef_segments private: template static inline typename Policy::return_type - collinear_one_degenerted(Segment const& segment, bool degenerated_a, - Point1 const& a1, Point1 const& a2, - Point2 const& b1, Point2 const& b2, - Vec3d const& v1, Vec3d const& v2, - Plane const& plane, - Vec3d const& vother) + collinear_one_degenerated(Segment const& segment, bool degenerated_a, + Point1 const& a1, Point1 const& a2, + Point2 const& b1, Point2 const& b2, + Vec3d const& v1, Vec3d const& v2, + Plane const& plane, + Vec3d const& vother) { CalcT dist_1_2, dist_1_o; return ! calculate_collinear_data(a1, a2, b1, b2, v1, v2, plane, vother, dist_1_2, dist_1_o) From bdd2d2c60cf6427ad4e9ce436b28391e8d3ffc14 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 2 Mar 2017 16:54:36 +0100 Subject: [PATCH 46/59] [formula] Fix errors in inverse formulas (manifesting near poles). vincenty - fix error in formula (missing sqr) differential_quantities - fix error in formula (wrong equation and lack of normalization) andoyer - wrong azimuth at south pole --- .../geometry/formulas/andoyer_inverse.hpp | 20 +++++++++-- .../formulas/differential_quantities.hpp | 19 +++++----- .../geometry/formulas/vincenty_inverse.hpp | 35 +++++++++++-------- 3 files changed, 48 insertions(+), 26 deletions(-) diff --git a/include/boost/geometry/formulas/andoyer_inverse.hpp b/include/boost/geometry/formulas/andoyer_inverse.hpp index b47e3c141..902fd7d8f 100644 --- a/include/boost/geometry/formulas/andoyer_inverse.hpp +++ b/include/boost/geometry/formulas/andoyer_inverse.hpp @@ -1,6 +1,6 @@ // Boost.Geometry -// Copyright (c) 2015-2016 Oracle and/or its affiliates. +// Copyright (c) 2015-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -137,7 +137,14 @@ public: CT A = c0; CT U = c0; - if ( ! math::equals(cos_lat2, c0) ) + if (math::equals(cos_lat2, c0)) + { + if (sin_lat2 < c0) + { + A = pi; + } + } + else { CT const tan_lat2 = sin_lat2/cos_lat2; CT const M = cos_lat1*tan_lat2-sin_lat1*cos_dlon; @@ -148,7 +155,14 @@ public: CT B = c0; CT V = c0; - if ( ! math::equals(cos_lat1, c0) ) + if (math::equals(cos_lat1, c0)) + { + if (sin_lat1 < c0) + { + B = pi; + } + } + else { CT const tan_lat1 = sin_lat1/cos_lat1; CT const N = cos_lat2*tan_lat1-sin_lat2*cos_dlon; diff --git a/include/boost/geometry/formulas/differential_quantities.hpp b/include/boost/geometry/formulas/differential_quantities.hpp index 9a92f14e1..ff2ec539d 100644 --- a/include/boost/geometry/formulas/differential_quantities.hpp +++ b/include/boost/geometry/formulas/differential_quantities.hpp @@ -1,6 +1,6 @@ // Boost.Geometry -// Copyright (c) 2016 Oracle and/or its affiliates. +// Copyright (c) 2016-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -64,8 +64,8 @@ public: CT const c1 = 1; CT const one_minus_f = c1 - f; - CT const sin_bet1 = one_minus_f * sin_lat1; - CT const sin_bet2 = one_minus_f * sin_lat2; + CT sin_bet1 = one_minus_f * sin_lat1; + CT sin_bet2 = one_minus_f * sin_lat2; // equator if (math::equals(sin_bet1, c0) && math::equals(sin_bet2, c0)) @@ -89,14 +89,17 @@ public: CT const e2 = f * (c2 - f); CT const ep2 = e2 / math::sqr(one_minus_f); - CT const cos_bet1 = cos_lat1; - CT const cos_bet2 = cos_lat2; - CT const sin_alp1 = sin(azimuth); CT const cos_alp1 = cos(azimuth); //CT const sin_alp2 = sin(reverse_azimuth); CT const cos_alp2 = cos(reverse_azimuth); + CT cos_bet1 = cos_lat1; + CT cos_bet2 = cos_lat2; + + normalize(sin_bet1, cos_bet1); + normalize(sin_bet2, cos_bet2); + CT sin_sig1 = sin_bet1; CT cos_sig1 = cos_alp1 * cos_bet1; CT sin_sig2 = sin_bet2; @@ -112,8 +115,8 @@ public: J12_f(sin_sig1, cos_sig1, sin_sig2, cos_sig2, cos_alp0_sqr, f) : J12_ep_sqr(sin_sig1, cos_sig1, sin_sig2, cos_sig2, cos_alp0_sqr, ep2) ; - CT const dn1 = math::sqrt(c1 + e2 * math::sqr(sin_lat1)); - CT const dn2 = math::sqrt(c1 + e2 * math::sqr(sin_lat2)); + CT const dn1 = math::sqrt(c1 + ep2 * math::sqr(sin_bet1)); + CT const dn2 = math::sqrt(c1 + ep2 * math::sqr(sin_bet2)); if (BOOST_GEOMETRY_CONDITION(EnableReducedLength)) { diff --git a/include/boost/geometry/formulas/vincenty_inverse.hpp b/include/boost/geometry/formulas/vincenty_inverse.hpp index e13ddf30e..032e16e29 100644 --- a/include/boost/geometry/formulas/vincenty_inverse.hpp +++ b/include/boost/geometry/formulas/vincenty_inverse.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014, 2016. -// Modifications copyright (c) 2014-2016 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014, 2016, 2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -40,7 +40,7 @@ namespace boost { namespace geometry { namespace formula \brief The solution of the inverse problem of geodesics on latlong coordinates, after Vincenty, 1975 \author See - http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf - - http://www.icsm.gov.au/gda/gdav2.3.pdf + - http://www.icsm.gov.au/gda/gda-v_2.4.pdf \author Adapted from various implementations to get it close to the original document - http://www.movable-type.co.uk/scripts/LatLongVincenty.html - http://exogen.case.edu/projects/geopy/source/geopy.distance.html @@ -98,10 +98,10 @@ public: CT const radius_a = CT(get_radius<0>(spheroid)); CT const radius_b = CT(get_radius<2>(spheroid)); - CT const flattening = formula::flattening(spheroid); + CT const f = formula::flattening(spheroid); // U: reduced latitude, defined by tan U = (1-f) tan phi - CT const one_min_f = c1 - flattening; + CT const one_min_f = c1 - f; CT const tan_U1 = one_min_f * tan(lat1); // above (1) CT const tan_U2 = one_min_f * tan(lat2); // above (1) @@ -112,8 +112,9 @@ public: CT const cos_U1 = c1 / temp_den_U1; CT const cos_U2 = c1 / temp_den_U2; // sin = tan / sqrt(1 + tan^2) - CT const sin_U1 = tan_U1 / temp_den_U1; - CT const sin_U2 = tan_U2 / temp_den_U2; + // sin = tan * cos + CT const sin_U1 = tan_U1 * cos_U1; + CT const sin_U2 = tan_U2 * cos_U2; // calculate sin U and cos U directly //CT const U1 = atan(tan_U1); @@ -129,7 +130,8 @@ public: CT sin_sigma; CT sin_alpha; CT cos2_alpha; - CT cos2_sigma_m; + CT cos_2sigma_m; + CT cos2_2sigma_m; CT sigma; int counter = 0; // robustness @@ -143,12 +145,13 @@ public: CT cos_sigma = sin_U1 * sin_U2 + cos_U1 * cos_U2 * cos_lambda; // (15) sin_alpha = cos_U1 * cos_U2 * sin_lambda / sin_sigma; // (17) cos2_alpha = c1 - math::sqr(sin_alpha); - cos2_sigma_m = math::equals(cos2_alpha, 0) ? 0 : cos_sigma - c2 * sin_U1 * sin_U2 / cos2_alpha; // (18) + cos_2sigma_m = math::equals(cos2_alpha, 0) ? 0 : cos_sigma - c2 * sin_U1 * sin_U2 / cos2_alpha; // (18) + cos2_2sigma_m = math::sqr(cos_2sigma_m); - CT C = flattening/c16 * cos2_alpha * (c4 + flattening * (c4 - c3 * cos2_alpha)); // (10) + CT C = f/c16 * cos2_alpha * (c4 + f * (c4 - c3 * cos2_alpha)); // (10) sigma = atan2(sin_sigma, cos_sigma); // (16) - lambda = L + (c1 - C) * flattening * sin_alpha * - (sigma + C * sin_sigma * ( cos2_sigma_m + C * cos_sigma * (-c1 + c2 * math::sqr(cos2_sigma_m)))); // (11) + lambda = L + (c1 - C) * f * sin_alpha * + (sigma + C * sin_sigma * (cos_2sigma_m + C * cos_sigma * (-c1 + c2 * cos2_2sigma_m))); // (11) ++counter; // robustness @@ -181,8 +184,10 @@ public: CT A = c1 + sqr_u/c16384 * (c4096 + sqr_u * (-c768 + sqr_u * (c320 - c175 * sqr_u))); // (3) CT B = sqr_u/c1024 * (c256 + sqr_u * ( -c128 + sqr_u * (c74 - c47 * sqr_u))); // (4) - CT delta_sigma = B * sin_sigma * ( cos2_sigma_m + (B/c4) * (cos(sigma)* (-c1 + c2 * cos2_sigma_m) - - (B/c6) * cos2_sigma_m * (-c3 + c4 * math::sqr(sin_sigma)) * (-c3 + c4 * cos2_sigma_m))); // (6) + CT const cos_sigma = cos(sigma); + CT const sin2_sigma = math::sqr(sin_sigma); + CT delta_sigma = B * sin_sigma * (cos_2sigma_m + (B/c4) * (cos_sigma* (-c1 + c2 * cos2_2sigma_m) + - (B/c6) * cos_2sigma_m * (-c3 + c4 * sin2_sigma) * (-c3 + c4 * cos2_2sigma_m))); // (6) result.distance = radius_b * A * (sigma - delta_sigma); // (19) } @@ -205,7 +210,7 @@ public: typedef differential_quantities quantities; quantities::apply(lon1, lat1, lon2, lat2, result.azimuth, result.reverse_azimuth, - radius_b, flattening, + radius_b, f, result.reduced_length, result.geodesic_scale); } From b7d62db59840163192f1966a2b1acfc3b9a83ed4 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 2 Mar 2017 17:01:47 +0100 Subject: [PATCH 47/59] [test][formulas][algorithms] Add/alter tests related to fixes in inverse formulas (length, perimeter). --- test/algorithms/length/length_geo.cpp | 2 +- test/algorithms/perimeter/perimeter_geo.cpp | 2 +- test/formulas/inverse.cpp | 26 ++-- test/formulas/inverse_cases.hpp | 131 +++++++++++++++++++- test/formulas/test_formula.hpp | 8 +- 5 files changed, 148 insertions(+), 21 deletions(-) diff --git a/test/algorithms/length/length_geo.cpp b/test/algorithms/length/length_geo.cpp index a5e340884..66754eaf0 100644 --- a/test/algorithms/length/length_geo.cpp +++ b/test/algorithms/length/length_geo.cpp @@ -80,7 +80,7 @@ template void test_vincenty() { typename geo_strategies

::vincenty_type vincenty; - test_with_strategy

(1116828.8624 + 1116161.625, vincenty); + test_with_strategy

(1116825.857 + 1116159.144, vincenty); } template diff --git a/test/algorithms/perimeter/perimeter_geo.cpp b/test/algorithms/perimeter/perimeter_geo.cpp index 675f6bf68..9b4452dcd 100644 --- a/test/algorithms/perimeter/perimeter_geo.cpp +++ b/test/algorithms/perimeter/perimeter_geo.cpp @@ -83,7 +83,7 @@ template void test_vincenty() { typename geo_strategies

::vincenty_type vincenty; - test_with_strategy

(1116828.8624 + 1116161.625, vincenty); + test_with_strategy

(1116825.857 + 1116159.144, vincenty); } template diff --git a/test/formulas/inverse.cpp b/test/formulas/inverse.cpp index 4c33cae28..8fa06d80a 100644 --- a/test/formulas/inverse.cpp +++ b/test/formulas/inverse.cpp @@ -39,25 +39,25 @@ void test_all(expected_results const& results) // WGS84 bg::srs::spheroid spheroid(6378137.0, 6356752.3142451793); - bg::formula::result_inverse result; + bg::formula::result_inverse result_v, result_t, result_a; typedef bg::formula::vincenty_inverse vi_t; - result = vi_t::apply(lon1r, lat1r, lon2r, lat2r, spheroid); - result.azimuth *= r2d; - result.reverse_azimuth *= r2d; - check_inverse(result, results.vincenty, results.karney, 0.00000001); + result_v = vi_t::apply(lon1r, lat1r, lon2r, lat2r, spheroid); + result_v.azimuth *= r2d; + result_v.reverse_azimuth *= r2d; + check_inverse(result_v, results.vincenty, results.reference, 0.0000001); typedef bg::formula::thomas_inverse th_t; - result = th_t::apply(lon1r, lat1r, lon2r, lat2r, spheroid); - result.azimuth *= r2d; - result.reverse_azimuth *= r2d; - check_inverse(result, results.thomas, results.karney, 0.0000001); + result_t = th_t::apply(lon1r, lat1r, lon2r, lat2r, spheroid); + result_t.azimuth *= r2d; + result_t.reverse_azimuth *= r2d; + check_inverse(result_t, results.thomas, results.reference, 0.00001); typedef bg::formula::andoyer_inverse an_t; - result = an_t::apply(lon1r, lat1r, lon2r, lat2r, spheroid); - result.azimuth *= r2d; - result.reverse_azimuth *= r2d; - check_inverse(result, results.andoyer, results.karney, 0.0001); + result_a = an_t::apply(lon1r, lat1r, lon2r, lat2r, spheroid); + result_a.azimuth *= r2d; + result_a.reverse_azimuth *= r2d; + check_inverse(result_a, results.andoyer, results.reference, 0.001); } int test_main(int, char*[]) diff --git a/test/formulas/inverse_cases.hpp b/test/formulas/inverse_cases.hpp index 1abc01e85..b3b868d56 100644 --- a/test/formulas/inverse_cases.hpp +++ b/test/formulas/inverse_cases.hpp @@ -1,7 +1,7 @@ // Boost.Geometry // Unit Test -// Copyright (c) 2016 Oracle and/or its affiliates. +// Copyright (c) 2016-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -31,10 +31,11 @@ struct expected_results { coordinates p1; coordinates p2; - expected_result karney; + expected_result reference; // karney or vincenty expected_result vincenty; expected_result thomas; expected_result andoyer; + }; expected_results expected[] = @@ -472,7 +473,131 @@ expected_results expected[] = { 156876.14925185780157335103, 134.82952991584656388113, 134.80335129775846780831, 156860.22615983051946386695, 0.99969549954768643918 }, { 156876.14903264911845326424, 134.82953099167994537311, 134.80335237457538255512, 156860.22319510785746388137, 0.99969549955919856377 }, { 156874.38594904550700448453, 134.82985169207830722371, 134.80367307400518939176, 156859.34477911840076558292, 0.99969550299904519353 } - } + }, { + // near pole + {90, -45}, {90, -80}, + {3900195.49395913071930408478, -180.00000000000000000000, -180.00000000000000000000, 3662502.04478993499651551247, 0.81922976250863166481}, + {3900195.49395686946809291840, 180.00000000000000000000, 180.00000000000000000000, 3662502.06126507185399532318, 0.81922976508736944368}, + {3900195.49053949490189552307, 180.00000000000000000000, 180.00000000000000000000, 3662502.06126507185399532318, 0.81922976508736944368}, + {3900203.61653685756027698517, 180.00000000000000000000, 180.00000000000000000000, 3662507.91531699104234576225, 0.81923068138101373670} + }, { + // pole, same lon + {90, -45}, {90, -90}, + {5017021.35133497882634401321, -180.00000000000000000000, -180.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017021.35133314039558172226, 180.00000000000000000000, 180.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017021.34791153203696012497, 180.00000000000000000000, 180.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017017.85355509258806705475, 180.00000000000000000000, 180.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172} + }, { + // pole + {90, -45}, {0, -90}, + {5017021.35133497882634401321, -180.00000000000000000000, -90.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017021.35133314039558172226, 180.00000000000000000000, -90.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017021.34791153203696012497, 180.00000000000000000000, -90.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017017.85355509258806705475, 180.00000000000000000000, -90.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172} + }, { + // near pole + {90, 45}, {90, 80}, + {3900195.49395913071930408478, 0.00000000000000000000, 0.00000000000000000000, 3662502.04478993499651551247, 0.81922976250863166481}, + {3900195.49395686946809291840, 0.00000000000000000000, 0.00000000000000000000, 3662502.06126507185399532318, 0.81922976508736944368}, + {3900195.49053949490189552307, 0.00000000000000000000, 0.00000000000000000000, 3662502.06126507185399532318, 0.81922976508736944368}, + {3900203.61653685756027698517, 0.00000000000000000076, 0.00000000000000000071, 3662507.91531699104234576225, 0.81923068138101373670} + }, { + // pole, same lon + {90, 45}, {90, 90}, + {5017021.35133497882634401321, 0.00000000000000000000, 0.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017021.35133314039558172226, 0.00000000000000000000, 0.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017021.34791153203696012497, 0.00000000000000000000, 0.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017017.85355509258806705475, 0.00000000000000000000, 0.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172} + }, { + // pole + {90, 45}, {0, 90}, + {5017021.35133497882634401321, -0.00000000000000000000, -90.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017021.35133314039558172226, -0.00000000000000496992, -90.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017021.34791153203696012497, 0.00000000000000000000, -90.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172}, + {5017017.85355509258806705475, 0.00000000000000000000, -90.00000000000000000000, 4517590.87884893082082271576, 0.70710678118654746172} + }, { + // pole + {0, -90}, {90, -45}, + {5017021.35133497882634401321, 90.00000000000000000000, 0.00000000000000000000, 4517590.87884893082082271576, 0.70778496454077310940}, + {5017021.35133314039558172226, 90.00000000000000000000, 0.00000000000000496992, 4517590.87884893082082271576, 0.70778494508412304054}, + {5017021.34791153203696012497, 90.00000000000000000000, 0.00000000000000000000, 4517590.87884893082082271576, 0.70778494508412304054}, + {5017017.85355509258806705475, 90.00000000000000000000, 0.00000000000000000000, 4517590.87884893082082271576, 0.70777827352623101653} + }, { + // pole + {0, 90}, {90, 45}, + {5017021.35133497882634401321, 90.00000000000000000000, -180.00000000000000000000, 4517590.87884893082082271576, 0.70778496454077310940}, + {5017021.35133314039558172226, 90.00000000000000000000, 180.00000000000000000000, 4517590.87884893082082271576, 0.70778494508412304054}, + {5017021.34791153203696012497, 90.00000000000001421085, 180.00000000000000000000, 4517590.87884893082082271576, 0.70778494508412304054}, + {5017017.85355509258806705475, 90.00000000000000000000, 180.00000000000000000000, 4517590.87884893082082271576, 0.70777827352623101653} + }, { + {90, -45}, {90, 45}, + {9969888.75595548748970031738, 0.00000000000000000000, 0.00000000000000000000, 6361290.27546359039843082428, 0.00431189731318673553}, + {9969888.75595730356872081757, 0.00000000000000000000, 0.00000000000000000000, 6361290.36011654324829578400, 0.00431188406305459994}, + {9969888.73915978334844112396, 0.00000000000000000000, 0.00000000000000000000, 6361290.36011654324829578400, 0.00431188406305459994}, + {9969881.64984572120010852814, 0.00000000000000001848, 0.00000000000000001176, 6361314.54906367603689432144, 0.00430809793699618842} + }, { + {90, 45}, {90, -45}, + {9969888.75595548748970031738, -180.00000000000000000000, -180.00000000000000000000, 6361290.27546359039843082428, 0.00431189731318673553}, + {9969888.75595730356872081757, 180.00000000000000000000, 180.00000000000000000000, 6361290.36011654324829578400, 0.00431188406305459994}, + {9969888.73915978334844112396, 180.00000000000000000000, 180.00000000000000000000, 6361290.36011654324829578400, 0.00431188406305459994}, + {9969881.64984572120010852814, 180.00000000000000000000, 180.00000000000000000000, 6361314.54906367603689432144, 0.00430809793699618842} + }, { + // pole, same lon + {90, 0}, {90, 90}, + {10001965.72931272350251674652, 0.00000000000000000000, 0.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.72931179217994213104, 0.00000000000000000000, 0.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.71749142371118068695, 0.00000000000000000000, 0.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001958.67847795411944389343, 0.00000000000000000000, 0.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000} + }, { + // pole + {90, 0}, {0, 90}, + {10001965.72931272350251674652, -0.00000000000000000000, -90.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.72931179217994213104, -0.00000000000000352016, -90.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.71749142371118068695, 0.00000000000000000000, -90.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001958.67847795411944389343, 0.00000000000000000000, -90.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000} + }, { + // pole, different lon + {0, 0}, {1, 90}, + {10001965.72931272350251674652, 0.00000000000000000000, 1.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.72931179217994213104, 0.00000000000000006144, 1.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.71749142371118068695, 0.00000000000000000000, 0.99999999999998867573, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001958.67847795411944389343, 0.00000000000000000000, 0.99999999999998867573, 6378136.99999999906867742538, 0.00000000000000000000} + }, { + // pole, different lon + {0, 0}, {-1, 90}, + {10001965.72931272350251674652, -0.00000000000000000000, -1.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.72931179217994213104, -0.00000000000000006144, -1.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.71749142371118068695, 0.00000000000000000000, -1.00000000000001421085, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001958.67847795411944389343, 0.00000000000000000000, -0.99999999999998867573, 6378136.99999999906867742538, 0.00000000000000000000} + }, { + // pole, different lon + {0, 0}, {1, -90}, + {10001965.72931272350251674652, -180.00000000000000000000, 179.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.72931179217994213104, 180.00000000000000000000, 179.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.71749142371118068695, 180.00000000000000000000, 179.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001958.67847795411944389343, 180.00000000000000000000, 179.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000} + }, { + // pole, different lon + {0, 0}, {-1, -90}, + {10001965.72931272350251674652, -180.00000000000000000000, -179.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.72931179217994213104, -180.00000000000000000000, -179.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001965.71749142371118068695, 180.00000000000000000000, -179.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000}, + {10001958.67847795411944389343, 180.00000000000000000000, -179.00000000000000000000, 6378136.99999999906867742538, 0.00000000000000000000} + }/*, { + // antipodal + {-90, -45}, {90, 45}, + {20003931.45862544700503349304, -180.00000000000000000000, 0.00000000000000000000, 33675.52452803074265830219, -0.99472900658949181540}, + {19987083.06974205747246742249, 90.00000000000000000000, 90.00000000000000000000, 00000.00000000078240703623, -1.00000000000000000000}, + {00000000.00000000000000000000, 0.00000000000000000000, 0.00000000000000000000, 000000.00000000000000000000, 1.00000000000000000000}, + {20020712.84987257421016693115, 0.00000000000000000000, 0.00000000000000000000, 6361314.54906367603689432144, 0.00430809793699618842} + },{ + // antipodal + {-90, 45}, {90, -45}, + {20003931.45862544700503349304, 0.00000000000000000000, -180.00000000000000000000, 33675.52452803074265830219, -0.99472900658949181540}, + {19987083.06974205747246742249, 90.00000000000000000000, 90.00000000000000000000, -000000.00000000078240703623, -1.00000000000000000000}, + {00000000.00000000000000000000, 0.00000000000000000000, 0.00000000000000000000, 000000.00000000000000000000, 1.00000000000000000000}, + {20020712.84987257421016693115, 180.00000000000000000000, 0.00000000000000000000, 33590.79639541531651047990, -1.00525773151080621837} + }*/ }; size_t const expected_size = sizeof(expected) / sizeof(expected_results); diff --git a/test/formulas/test_formula.hpp b/test/formulas/test_formula.hpp index 262980d98..c6967616a 100644 --- a/test/formulas/test_formula.hpp +++ b/test/formulas/test_formula.hpp @@ -1,7 +1,7 @@ // Boost.Geometry // Unit Test -// Copyright (c) 2016 Oracle and/or its affiliates. +// Copyright (c) 2016-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -43,7 +43,9 @@ void check_one(double result, double expected, double reference, double referenc double res_min = (std::min)(abs_result, abs_expected); if (res_min <= eps) // including 0 { - BOOST_CHECK(abs_result <= 10 * eps && abs_expected <= 10 * eps); + bool is_same = abs_result <= 10 * eps && abs_expected <= 10 * eps; + BOOST_CHECK_MESSAGE((is_same), + std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); } else if (res_max > 100 * eps) { @@ -64,7 +66,7 @@ void check_one(double result, double expected, double reference, double referenc double ref_diff = bg::math::abs(result - reference); double ref_max = (std::max)(bg::math::abs(result), bg::math::abs(reference)); bool is_ref_close = ref_diff <= reference_error || ref_diff <= reference_error * ref_max; - BOOST_CHECK_MESSAGE((is_ref_close), std::setprecision(16) << "{" << result << "} and {" << reference << "} not close enough."); + BOOST_CHECK_MESSAGE((is_ref_close), std::setprecision(20) << "result {" << result << "} and reference {" << reference << "} not close enough."); } #endif // BOOST_GEOMETRY_TEST_FORMULA_HPP From 6f733b64a9489d32295cafc0f8e9601942eebdbc Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 2 Mar 2017 19:20:49 +0100 Subject: [PATCH 48/59] [index] Add explicit modifier to dynamic_* parameters ctors. --- include/boost/geometry/index/parameters.hpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/boost/geometry/index/parameters.hpp b/include/boost/geometry/index/parameters.hpp index 6a3a67c6c..1a9469c10 100644 --- a/include/boost/geometry/index/parameters.hpp +++ b/include/boost/geometry/index/parameters.hpp @@ -2,7 +2,7 @@ // // R-tree algorithms parameters // -// Copyright (c) 2011-2013 Adam Wulkiewicz, Lodz, Poland. +// Copyright (c) 2011-2017 Adam Wulkiewicz, Lodz, Poland. // // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -162,8 +162,8 @@ public: \param max_elements Maximum number of elements in nodes. \param min_elements Minimum number of elements in nodes. Default: 0.3*Max. */ - dynamic_linear(size_t max_elements, - size_t min_elements = detail::default_min_elements_d()) + explicit dynamic_linear(size_t max_elements, + size_t min_elements = detail::default_min_elements_d()) : m_max_elements(max_elements) , m_min_elements(detail::default_min_elements_d_calc(max_elements, min_elements)) { @@ -191,8 +191,8 @@ public: \param max_elements Maximum number of elements in nodes. \param min_elements Minimum number of elements in nodes. Default: 0.3*Max. */ - dynamic_quadratic(size_t max_elements, - size_t min_elements = detail::default_min_elements_d()) + explicit dynamic_quadratic(size_t max_elements, + size_t min_elements = detail::default_min_elements_d()) : m_max_elements(max_elements) , m_min_elements(detail::default_min_elements_d_calc(max_elements, min_elements)) { @@ -228,10 +228,10 @@ public: nearly minimum overlap cost, otherwise all leafs are analyzed and true minimum overlap cost is calculated. Default: 32. */ - dynamic_rstar(size_t max_elements, - size_t min_elements = detail::default_min_elements_d(), - size_t reinserted_elements = detail::default_rstar_reinserted_elements_d(), - size_t overlap_cost_threshold = 32) + explicit dynamic_rstar(size_t max_elements, + size_t min_elements = detail::default_min_elements_d(), + size_t reinserted_elements = detail::default_rstar_reinserted_elements_d(), + size_t overlap_cost_threshold = 32) : m_max_elements(max_elements) , m_min_elements(detail::default_min_elements_d_calc(max_elements, min_elements)) , m_reinserted_elements(detail::default_rstar_reinserted_elements_d_calc(max_elements, reinserted_elements)) From a06e6bbb792d8e4abe3d7d36c0a725edba7421c1 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 2 Mar 2017 19:23:31 +0100 Subject: [PATCH 49/59] [strategies] Rename strategy side::detail::by_azimuth to side::geographic. --- .../geographic/{side_detail.hpp => side.hpp} | 22 +++++++++---------- .../strategies/geographic/side_andoyer.hpp | 15 ++++++++----- .../strategies/geographic/side_thomas.hpp | 15 ++++++++----- .../strategies/geographic/side_vincenty.hpp | 15 ++++++++----- .../boost/geometry/strategies/strategies.hpp | 2 ++ 5 files changed, 39 insertions(+), 30 deletions(-) rename include/boost/geometry/strategies/geographic/{side_detail.hpp => side.hpp} (86%) diff --git a/include/boost/geometry/strategies/geographic/side_detail.hpp b/include/boost/geometry/strategies/geographic/side.hpp similarity index 86% rename from include/boost/geometry/strategies/geographic/side_detail.hpp rename to include/boost/geometry/strategies/geographic/side.hpp index ece87cfc2..1c4843efc 100644 --- a/include/boost/geometry/strategies/geographic/side_detail.hpp +++ b/include/boost/geometry/strategies/geographic/side.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014, 2015, 2016. -// Modifications copyright (c) 2014-2016 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014-2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -11,8 +11,8 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#ifndef BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_SIDE_DETAIL_HPP -#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_SIDE_DETAIL_HPP +#ifndef BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_SIDE_HPP +#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_SIDE_HPP #include #include @@ -36,9 +36,6 @@ namespace boost { namespace geometry namespace strategy { namespace side { -#ifndef DOXYGEN_NO_DETAIL -namespace detail -{ /*! \brief Check at which side of a segment a point lies @@ -51,10 +48,13 @@ namespace detail template class InverseFormula, typename Model, typename CalculationType = void> -class by_azimuth +class geographic { public: - by_azimuth(Model const& model = Model()) + geographic() + {} + + explicit geographic(Model const& model) : m_model(model) {} @@ -96,8 +96,6 @@ private: Model m_model; }; -} // detail -#endif // DOXYGEN_NO_DETAIL }} // namespace strategy::side @@ -105,4 +103,4 @@ private: }} // namespace boost::geometry -#endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_SIDE_DETAIL_HPP +#endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_SIDE_HPP diff --git a/include/boost/geometry/strategies/geographic/side_andoyer.hpp b/include/boost/geometry/strategies/geographic/side_andoyer.hpp index c3e71cd1c..127ba484c 100644 --- a/include/boost/geometry/strategies/geographic/side_andoyer.hpp +++ b/include/boost/geometry/strategies/geographic/side_andoyer.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014, 2015, 2016. -// Modifications copyright (c) 2014-2016 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014-2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -17,7 +17,7 @@ #include -#include +#include namespace boost { namespace geometry @@ -36,12 +36,15 @@ namespace strategy { namespace side */ template class andoyer - : public detail::by_azimuth + : public side::geographic { - typedef detail::by_azimuth base_t; + typedef side::geographic base_t; public: - andoyer(Model const& model = Model()) + andoyer() + {} + + explicit andoyer(Model const& model) : base_t(model) {} }; diff --git a/include/boost/geometry/strategies/geographic/side_thomas.hpp b/include/boost/geometry/strategies/geographic/side_thomas.hpp index 96b032330..79f0bc988 100644 --- a/include/boost/geometry/strategies/geographic/side_thomas.hpp +++ b/include/boost/geometry/strategies/geographic/side_thomas.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014, 2015, 2016. -// Modifications copyright (c) 2014-2016 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014-2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -17,7 +17,7 @@ #include -#include +#include namespace boost { namespace geometry @@ -36,12 +36,15 @@ namespace strategy { namespace side */ template class thomas - : public detail::by_azimuth + : public side::geographic { - typedef detail::by_azimuth base_t; + typedef side::geographic base_t; public: - thomas(Model const& model = Model()) + thomas() + {} + + explicit thomas(Model const& model) : base_t(model) {} }; diff --git a/include/boost/geometry/strategies/geographic/side_vincenty.hpp b/include/boost/geometry/strategies/geographic/side_vincenty.hpp index 103277a8b..02c9ca4e0 100644 --- a/include/boost/geometry/strategies/geographic/side_vincenty.hpp +++ b/include/boost/geometry/strategies/geographic/side_vincenty.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014, 2015, 2016. -// Modifications copyright (c) 2014-2016 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014-2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -17,7 +17,7 @@ #include -#include +#include namespace boost { namespace geometry @@ -36,12 +36,15 @@ namespace strategy { namespace side */ template class vincenty - : public detail::by_azimuth + : public side::geographic { - typedef detail::by_azimuth base_t; + typedef side::geographic base_t; public: - vincenty(Model const& model = Model()) + vincenty() + {} + + explicit vincenty(Model const& model) : base_t(model) {} }; diff --git a/include/boost/geometry/strategies/strategies.hpp b/include/boost/geometry/strategies/strategies.hpp index c6c5f5ddd..6a3dca99e 100644 --- a/include/boost/geometry/strategies/strategies.hpp +++ b/include/boost/geometry/strategies/strategies.hpp @@ -83,7 +83,9 @@ #include #include #include +//#include //#include +//#include //#include //#include //#include From d8513265a23b5b514c10ae430ec9ffbb1efddccf Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 2 Mar 2017 19:24:05 +0100 Subject: [PATCH 50/59] [strategies] Fix compilation errors in geodesic_intersection. Remove static keyword from strategies getters. Rename template parameter to avoid shadowing. --- .../geographic/geodesic_intersection.hpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp b/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp index 27581e7af..e2dc96e39 100644 --- a/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp +++ b/include/boost/geometry/strategies/geographic/geodesic_intersection.hpp @@ -34,7 +34,7 @@ #include #include -#include +#include #include #include #include @@ -63,12 +63,12 @@ template > struct relate_geodesic_segments { - typedef side::detail::by_azimuth + typedef side::geographic < Inverse, Spheroid, CalculationType > side_strategy_type; - static inline side_strategy_type get_side_strategy() + inline side_strategy_type get_side_strategy() { return side_strategy_type(m_spheroid); } @@ -86,7 +86,7 @@ struct relate_geodesic_segments }; template - static inline typename point_in_geometry_strategy::type + inline typename point_in_geometry_strategy::type get_point_in_geometry_strategy() { typedef typename point_in_geometry_strategy @@ -112,7 +112,7 @@ struct relate_geodesic_segments }; template - static inline typename area_strategy::type get_area_strategy() + inline typename area_strategy::type get_area_strategy() { typedef typename area_strategy::type strategy_type; return strategy_type(m_spheroid); @@ -125,7 +125,7 @@ struct relate_geodesic_segments }; template - static inline typename distance_strategy::type get_distance_strategy() + inline typename distance_strategy::type get_distance_strategy() { typedef typename distance_strategy::type strategy_type; return strategy_type(m_spheroid); @@ -872,8 +872,8 @@ private: ip_flag; } - template - static inline srs::spheroid normalized_spheroid(Spheroid const& spheroid) + template + static inline srs::spheroid normalized_spheroid(SpheroidT const& spheroid) { return srs::spheroid(CalcT(1), CalcT(get_radius<2>(spheroid)) // b/a From be844d54e52500e5282ae59457f33e77fdb38bb6 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 2 Mar 2017 19:25:48 +0100 Subject: [PATCH 51/59] [test] Add missing variable. --- test/strategies/segment_intersection_sph.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/strategies/segment_intersection_sph.hpp b/test/strategies/segment_intersection_sph.hpp index 3d4bca57d..31f00b1e8 100644 --- a/test/strategies/segment_intersection_sph.hpp +++ b/test/strategies/segment_intersection_sph.hpp @@ -29,6 +29,7 @@ template bool equals_relaxed_val(T const& v1, T const& v2, T const& eps_scale) { + T const c1 = 1; T relaxed_eps = std::numeric_limits::epsilon() * bg::math::detail::greatest(c1, bg::math::abs(v1), bg::math::abs(v2)) * eps_scale; From 3062222a925f2c494d1e3070a9a990aecd3cc63e Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 2 Mar 2017 19:26:02 +0100 Subject: [PATCH 52/59] [doc] Update 1.64 release notes. --- doc/release_notes.qbk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk index 95d580be1..a640ab0e9 100644 --- a/doc/release_notes.qbk +++ b/doc/release_notes.qbk @@ -29,6 +29,8 @@ [*Breaking changes] * ublas_transformer is renamed to matrix_transformer +* explicit modifier is added to constructors of rtree index::dynamic_* parameters +* strategy::area::huiller replaced by strategy::area::spherical [*Solved issues] From 6fa7b888e5a0504c04f6d2b8ca0ab99f55aeb7a0 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 3 Mar 2017 03:53:29 +0100 Subject: [PATCH 53/59] [test][formulas] Tweak eps threshold in inverse formula test (for mingw-gcc). --- test/formulas/test_formula.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/formulas/test_formula.hpp b/test/formulas/test_formula.hpp index c6967616a..60502281d 100644 --- a/test/formulas/test_formula.hpp +++ b/test/formulas/test_formula.hpp @@ -43,8 +43,8 @@ void check_one(double result, double expected, double reference, double referenc double res_min = (std::min)(abs_result, abs_expected); if (res_min <= eps) // including 0 { - bool is_same = abs_result <= 10 * eps && abs_expected <= 10 * eps; - BOOST_CHECK_MESSAGE((is_same), + bool is_close = abs_result <= 30 * eps && abs_expected <= 30 * eps; + BOOST_CHECK_MESSAGE((is_close), std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); } else if (res_max > 100 * eps) From c00178f39a5d1768ea269789f25d62fbfa7b4629 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 3 Mar 2017 03:56:40 +0100 Subject: [PATCH 54/59] [test][algorithms] Use ranges of coordinates in expected boxes in some cases. In cases of long segments the envelopes may differ more than several epsilons with different compilers. --- .../envelope_expand/envelope_on_spheroid.cpp | 225 +++++++++++------- .../test_envelope_expand_on_spheroid.hpp | 212 +++++++++++++---- 2 files changed, 301 insertions(+), 136 deletions(-) diff --git a/test/algorithms/envelope_expand/envelope_on_spheroid.cpp b/test/algorithms/envelope_expand/envelope_on_spheroid.cpp index a6336132a..01deaffba 100644 --- a/test/algorithms/envelope_expand/envelope_on_spheroid.cpp +++ b/test/algorithms/envelope_expand/envelope_on_spheroid.cpp @@ -50,6 +50,7 @@ #include "test_envelope_expand_on_spheroid.hpp" + template < template class Inverse, typename CS_Tag @@ -152,11 +153,54 @@ private: BOOST_CHECK_MESSAGE(same_boxes, stream.str()); } - template + template + < + typename Geometry, typename Box, + typename T1, typename T2, typename T3, typename T4 + > + static inline void check_message(bool same_boxes, + std::string const& case_id, + std::string const& units_str, + Geometry const& geometry, + T1 const& lon_min, T2 const& lat_min, double height_min, + T3 const& lon_max, T4 const& lat_max, double height_max, + Box const& detected) + { + std::ostringstream stream; + stream << "case ID: " << case_id << ", " + << "MBR units: " << units_str << "; " + << "geometry: "; + + write_geometry::apply(stream, geometry); + + stream << std::setprecision(17); + + stream << "; " << "expected: "; + + if (BOOST_GEOMETRY_CONDITION(bg::dimension::value == 2)) + { + stream << "(" << lon_min << " " << lat_min + << ", " << lon_max << " " << lat_max << ")"; + } + else + { + stream << "(" << lon_min << " " << lat_min << " " << height_min + << ", " << lon_max << " " << lat_max << " " << height_max << ")"; + } + stream << ", " << "detected: " << bg::dsv(detected); + + BOOST_CHECK_MESSAGE(same_boxes, stream.str()); + } + + template + < + typename Box, typename Geometry, + typename T1, typename T2, typename T3, typename T4 + > static inline void base_test(std::string const& case_id, Geometry const& geometry, - double lon_min, double lat_min, double height_min, - double lon_max, double lat_max, double height_max, + T1 const& lon_min, T2 const& lat_min, double height_min, + T3 const& lon_max, T4 const& lat_max, double height_max, double tolerance) { typedef typename bg::coordinate_system::type::units box_units_type; @@ -168,27 +212,40 @@ private: typename bg::cs_tag::type >::apply(geometry, detected); - Box expected; - initialize_box::apply(expected, - lon_min, lat_min, height_min, - lon_max, lat_max, height_max); - #ifdef BOOST_GEOMETRY_TEST_DEBUG std::cout << "geometry: "; write_geometry::apply(std::cout, geometry); std::cout << std::endl << "MBR units: " << units_str - << std::endl - << "expected: " << bg::dsv(expected) - << std::endl + << std::endl; + std::cout << "expected: "; + if (BOOST_GEOMETRY_CONDITION(bg::dimension::value == 2)) + { + std::cout << "(" << lon_min << " " << lat_min + << ", " << lon_max << " " << lat_max << ")"; + } + else + { + std::cout << "(" << lon_min << " " << lat_min << " " << height_min + << ", " << lon_max << " " << lat_max << " " << height_max << ")"; + } + std::cout << std::endl << "detected: " << bg::dsv(detected) << std::endl << std::endl; #endif - check_message(box_equals::apply(detected, expected, tolerance), + bool check = box_check_equals::apply(detected, + lon_min, lat_min, height_min, + lon_max, lat_max, height_max, + tolerance); + + check_message(check, case_id, units_str, - geometry, expected, detected); + geometry, + lon_min, lat_min, height_min, + lon_max, lat_max, height_max, + detected); // if valid box is expected, check the validity if (lon_min <= lon_max && lat_min <= lat_max && height_min <= height_max) @@ -201,11 +258,15 @@ private: } public: - template + template + < + typename Geometry, + typename T1, typename T2, typename T3, typename T4 + > static inline void apply(std::string const& case_id, Geometry const& geometry, - double lon_min, double lat_min, double height_min, - double lon_max, double lat_max, double height_max, + T1 const& lon_min, T2 const& lat_min, double height_min, + T3 const& lon_max, T4 const& lat_max, double height_max, double tolerance) { typedef other_system_info @@ -291,12 +352,14 @@ template > struct test_envelope_on_sphere_or_spheroid { + template static inline void apply(std::string const& case_id, Geometry const& geometry, - double lon_min1, double lat_min1, double height_min1, - double lon_max1, double lat_max1, double height_max1, - double lon_min2, double lat_min2, double height_min2, - double lon_max2, double lat_max2, double height_max2, + T1 const& lon_min1, T2 const& lat_min1, double height_min1, + T3 const& lon_max1, T4 const& lat_max1, double height_max1, + T5 const& lon_min2, T6 const& lat_min2, double height_min2, + T7 const& lon_max2, T8 const& lat_max2, double height_max2, double tolerance = std::numeric_limits::epsilon()) { envelope_on_spheroid_basic_tester @@ -328,12 +391,14 @@ struct test_envelope_on_sphere_or_spheroid #endif } + template static inline void apply(std::string const& case_id, Geometry const& geometry, - double lon_min1, double lat_min1, - double lon_max1, double lat_max1, - double lon_min2, double lat_min2, - double lon_max2, double lat_max2, + T1 const& lon_min1, T2 const& lat_min1, + T3 const& lon_max1, T4 const& lat_max1, + T5 const& lon_min2, T6 const& lat_min2, + T7 const& lon_max2, T8 const& lat_max2, double tolerance = std::numeric_limits::epsilon()) { apply(case_id, geometry, @@ -342,10 +407,11 @@ struct test_envelope_on_sphere_or_spheroid tolerance); } + template static inline void apply(std::string const& case_id, Geometry const& geometry, - double lon_min, double lat_min, double height_min, - double lon_max, double lat_max, double height_max, + T1 const& lon_min, T2 const& lat_min, double height_min, + T3 const& lon_max, T4 const& lat_max, double height_max, double tolerance = std::numeric_limits::epsilon()) { apply(case_id, geometry, @@ -356,10 +422,11 @@ struct test_envelope_on_sphere_or_spheroid tolerance); } + template static inline void apply(std::string const& case_id, Geometry const& geometry, - double lon_min, double lat_min, - double lon_max, double lat_max, + T1 const& lon_min, T2 const& lat_min, + T3 const& lon_max, T4 const& lat_max, double tolerance = std::numeric_limits::epsilon()) { apply(case_id, geometry, @@ -373,13 +440,15 @@ struct test_envelope_on_sphere_or_spheroid template struct test_envelope_on_sphere_or_spheroid { + template static inline void apply(std::string const& case_id, Geometry const& geometry, - double lon_min1, double lat_min1, - double lon_max1, double lat_max1, - double lon_min2, double lat_min2, - double lon_max2, double lat_max2, - double tolerance = std::numeric_limits::epsilon()) + T1 const& lon_min1, T2 const& lat_min1, + T3 const& lon_max1, T4 const& lat_max1, + T5 const& lon_min2, T6 const& lat_min2, + T7 const& lon_max2, T8 const& lat_max2, + double const& tolerance = std::numeric_limits::epsilon()) { envelope_on_spheroid_basic_tester < @@ -409,10 +478,11 @@ struct test_envelope_on_sphere_or_spheroid static inline void apply(std::string const& case_id, Geometry const& geometry, - double lon_min, double lat_min, - double lon_max, double lat_max, + T1 const& lon_min, T2 const& lat_min, + T3 const& lon_max, T4 const& lat_max, double tolerance = std::numeric_limits::epsilon()) { apply(case_id, geometry, @@ -822,26 +892,24 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid ) tester::apply("s09", from_wkt("SEGMENT(1 -45,179 30)"), - 1, -85.392785243526134, 179, 30, - 3 * eps); + 1, rng(-85.392785243526134, -85.392785243525253), 179, 30); tester::apply("s09a", from_wkt("SEGMENT(2 -45,181 30)"), - 2, -87.689300911353811, 181, 30); + 2, rng(-87.689300911353811, -87.689300911353371), 181, 30); // very long segment tester::apply("s10", from_wkt("SEGMENT(0 -45,181 30)"), - -179, -87.689300911353797, 0, 30, - 2.0 * eps); + -179, rng(-87.689300911353797, -87.689300911353385), 0, 30); tester::apply("s11", from_wkt("SEGMENT(260 30,20 45)"), - -100, 30, 20, 57.990810958016965); + -100, 30, 20, rng(57.990810958016482, 57.990810958016965)); tester::apply("s11a", from_wkt("SEGMENT(260 45,20 30)"), - -100, 30, 20, 57.990810958016965); + -100, 30, 20, rng(57.990810958016453, 57.990810958016965)); // segment degenerating to the north pole tester::apply("s12", @@ -859,7 +927,7 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid ) tester::apply("s15", from_wkt("SEGMENT(50 45,185 45)"), - 50, 45, 185, 69.098479073903178); + 50, 45, 185, rng(69.098479073902851, 69.098479073903178)); // segment that lies on the equator tester::apply("s16", @@ -924,7 +992,7 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid ) 1-heps, 1, 1, 1); tester::apply("s104", G(P(2, 1), P(1, 1-heps)), - 1, 1-heps, 2, 1.0000383271569036); + 1, 1-heps, 2, rng(1.0000383271568751, 1.0000383271569036)); tester::apply("s105", G(P(1, 2), P(1-heps, 1)), 1-heps, 1, 1, 2); @@ -945,8 +1013,6 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_thomas ) bg::formula::thomas_inverse > tester; - double const eps = std::numeric_limits::epsilon(); - tester::apply("s01", from_wkt("SEGMENT(10 10,40 40)"), 10, 10, 40, 40); @@ -1011,26 +1077,24 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_thomas ) tester::apply("s09", from_wkt("SEGMENT(1 -45,179 30)"), - 1, -85.392785243526134, 179, 30, - 3 * eps); + 1, rng(-85.392785243526134, -85.392785243525253), 179, 30); tester::apply("s09a", from_wkt("SEGMENT(2 -45,181 30)"), - 2, -87.689300911353811, 181, 30); + 2, rng(-87.689300911353811, -87.689300911353371), 181, 30); // very long segment tester::apply("s10", from_wkt("SEGMENT(0 -45,181 30)"), - -179, -87.689300911353797, 0, 30, - 2.0 * eps); + -179, rng(-87.689300911353797, -87.689300911353385), 0, 30); tester::apply("s11", from_wkt("SEGMENT(260 30,20 45)"), - -100, 30, 20, 57.990810958016965); + -100, 30, 20, rng(57.990810958016482, 57.990810958016965)); tester::apply("s11a", from_wkt("SEGMENT(260 45,20 30)"), - -100, 30, 20, 57.990810958016965); + -100, 30, 20, rng(57.990810958016453, 57.990810958016965)); // segment degenerating to the north pole tester::apply("s12", @@ -1048,7 +1112,7 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_thomas ) tester::apply("s15", from_wkt("SEGMENT(50 45,185 45)"), - 50, 45, 185, 69.098479073903178); + 50, 45, 185, rng(69.098479073902851, 69.098479073903178)); // segment that lies on the equator tester::apply("s16", @@ -1103,8 +1167,6 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_andoyer ) bg::formula::andoyer_inverse > tester; - double const eps = std::numeric_limits::epsilon(); - tester::apply("s01", from_wkt("SEGMENT(10 10,40 40)"), 10, 10, 40, 40); @@ -1169,26 +1231,24 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_andoyer ) tester::apply("s09", from_wkt("SEGMENT(1 -45,179 30)"), - 1, -85.394745211091248, 179, 30, - 3 * eps); + 1, rng(-85.394745211091248, -85.394745211090353), 179, 30); tester::apply("s09a", from_wkt("SEGMENT(2 -45,181 30)"), - 2, -87.690317839849726, 181, 30); + 2, rng(-87.690317839849726, -87.690317839849271), 181, 30); // very long segment tester::apply("s10", from_wkt("SEGMENT(0 -45,181 30)"), - -179, -87.69031783984974, 0, 30, - 2.0 * eps); + -179, rng(-87.69031783984974, -87.690317839849271), 0, 30); tester::apply("s11", from_wkt("SEGMENT(260 30,20 45)"), - -100, 30, 20, 57.990742552280153); + -100, 30, 20, rng(57.990742552279649, 57.990742552280153)); tester::apply("s11a", from_wkt("SEGMENT(260 45,20 30)"), - -100, 30, 20, 57.990742552280118); + -100, 30, 20, rng(57.99074255227962, 57.990742552280118)); // segment degenerating to the north pole tester::apply("s12", @@ -1206,7 +1266,7 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_andoyer ) tester::apply("s15", from_wkt("SEGMENT(50 45,185 45)"), - 50, 45, 185, 69.09844689340845); + 50, 45, 185, rng(69.098446893408124, 69.09844689340845)); // segment that lies on the equator tester::apply("s16", @@ -1261,31 +1321,29 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_vincenty ) bg::formula::vincenty_inverse > tester; - double const eps = std::numeric_limits::epsilon(); - tester::apply("s01", from_wkt("SEGMENT(10 10,40 40)"), 10, 10, 40, 40); tester::apply("s02", from_wkt("SEGMENT(10 10,40 10)"), - 10, 10, 40, 10.347587628821941); + 10, 10, 40, rng(10.347587628821937, 10.347587628821941)); tester::apply("s02a", from_wkt("SEGMENT(40 10,10 10)"), - 10, 10, 40, 10.347587628821941); + 10, 10, 40, rng(10.347587628821937, 10.347587628821941)); tester::apply("s03", from_wkt("SEGMENT(160 10,-170 10)"), - 160, 10, 190, 10.347587628821941); + 160, 10, 190, rng(10.347587628821937, 10.347587628821941)); tester::apply("s03a", from_wkt("SEGMENT(-170 10,160 10)"), - 160, 10, 190, 10.347587628821941); + 160, 10, 190, rng(10.347587628821937, 10.347587628821941)); tester::apply("s03b", from_wkt("SEGMENT(-170 -10,160 -10)"), - 160, -10.347587628821941, 190, -10); + 160, rng(-10.347587628821941, -10.347587628821937), 190, -10); tester::apply("s04", from_wkt("SEGMENT(-40 45,140 60)"), @@ -1327,26 +1385,24 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_vincenty ) tester::apply("s09", from_wkt("SEGMENT(1 -45,179 30)"), - 1, -85.392840929577218, 179, 30, - 3 * eps); + 1, rng(-85.392840929577218, -85.392840929576352), 179, 30); tester::apply("s09a", from_wkt("SEGMENT(2 -45,181 30)"), - 2, -87.689330275867817, 181, 30); + 2, rng(-87.689330275867817, -87.689330275867405), 181, 30); // very long segment tester::apply("s10", from_wkt("SEGMENT(0 -45,181 30)"), - -179, -87.689330275867832, 0, 30, - 2.0 * eps); + -179, rng(-87.689330275867832, -87.689330275867405), 0, 30); tester::apply("s11", from_wkt("SEGMENT(260 30,20 45)"), - -100, 30, 20, 57.990810647057032); + -100, 30, 20, rng(57.990810647056549, 57.990810647057032)); tester::apply("s11a", from_wkt("SEGMENT(260 45,20 30)"), - -100, 30, 20, 57.990810647057032); + -100, 30, 20, rng(57.990810647056541, 57.990810647057032)); // segment degenerating to the north pole tester::apply("s12", @@ -1364,7 +1420,7 @@ BOOST_AUTO_TEST_CASE( envelope_segment_spheroid_with_strategy_vincenty ) tester::apply("s15", from_wkt("SEGMENT(50 45,185 45)"), - 50, 45, 185, 69.098479136978497); + 50, 45, 185, rng(69.098479136978156, 69.098479136978497)); // segment that lies on the equator tester::apply("s16", @@ -2012,8 +2068,7 @@ BOOST_AUTO_TEST_CASE( envelope_spheroid_linestring ) // linestring that circles the entire globe tester::apply("l03", from_wkt("LINESTRING(-185 0,-170 25,-50 10,10 10,20 20,100 5,180 15)"), - -180, 0, 180, 33.702476580413318, - 4.0 * std::numeric_limits::epsilon()); + -180, 0, 180, rng(33.702476580412359, 33.702476580413318)); // linestring that crosses the antimeridian but staying close to it tester::apply("l04", @@ -2261,19 +2316,19 @@ BOOST_AUTO_TEST_CASE( envelope_spheroid_multilinestring ) tester::apply("ml04", from_wkt("MULTILINESTRING((-150 40,-100 80),(10 35,100 80))"), - -150, 35, 100, 80.082544902477267); + -150, 35, 100, rng(80.07385383411011, 80.082544902477267)); tester::apply("ml04a", from_wkt("MULTILINESTRING((-150 40,-100 80),(10 35,100 80),(170 25,-160 80))"), - 10, 25, 260, 80.082544902477267); + 10, 25, 260, rng(80.07385383411011, 80.082544902477267)); tester::apply("ml05", from_wkt("MULTILINESTRING((-140 40,-100 80),(10 35,100 80))"), - -140, 35, 100, 80.082544902477267); + -140, 35, 100, rng(80.07385383411011, 80.082544902477267)); tester::apply("ml05a", from_wkt("MULTILINESTRING((-140 40,-100 80),(10 35,100 80),(170 25,-160 80))"), - 10, 25, 260, 80.082544902477267); + 10, 25, 260, rng(80.07385383411011, 80.082544902477267)); } diff --git a/test/algorithms/envelope_expand/test_envelope_expand_on_spheroid.hpp b/test/algorithms/envelope_expand/test_envelope_expand_on_spheroid.hpp index 14066cd47..ba04c85ef 100644 --- a/test/algorithms/envelope_expand/test_envelope_expand_on_spheroid.hpp +++ b/test/algorithms/envelope_expand/test_envelope_expand_on_spheroid.hpp @@ -11,9 +11,11 @@ #ifndef BOOST_GEOMETRY_TEST_ENVELOPE_EXPAND_ON_SPHEROID_HPP #define BOOST_GEOMETRY_TEST_ENVELOPE_EXPAND_ON_SPHEROID_HPP + +#include #include #include -#include +#include #include @@ -29,6 +31,37 @@ #include +struct rng +{ + typedef double type; + + rng(double l, double h) + : lo(l), hi(h) + { + BOOST_GEOMETRY_ASSERT(lo <= hi); + } + + friend rng operator*(rng const& l, double v) { return rng(l.lo * v, l.hi * v); } + + friend bool operator<=(rng const& l, rng const& r) { return l.lo <= r.hi; } + friend bool operator<=(double l, rng const& r) { return l <= r.hi; } + friend bool operator<=(rng const& l, double r) { return l.lo <= r; } + + friend bool operator<(rng const& l, rng const& r) { return !operator<=(r, l); } + friend bool operator<(double l, rng const& r) { return !operator<=(r, l); } + friend bool operator<(rng const& l, double r) { return !operator<=(r, l); } + + friend bool operator==(double l, rng const& r) { return r.lo <= l && l <= r.hi; } + + friend std::ostream & operator<<(std::ostream & os, rng const& v) + { + return (os << "[" << v.lo << ", " << v.hi << "]"); + } + + double lo, hi; +}; + + template char const* units2string() { @@ -49,10 +82,14 @@ struct other_system_info > typedef bg::degree units; typedef bg::cs::spherical_equatorial type; - template - static inline T convert(T const& value) + static inline double convert(double value) { - return value * bg::math::r2d(); + return value * bg::math::r2d(); + } + + static inline rng convert(rng const& value) + { + return value * bg::math::r2d(); } }; @@ -62,10 +99,14 @@ struct other_system_info > typedef bg::radian units; typedef bg::cs::spherical_equatorial type; - template - static inline T convert(T const& value) + static inline double convert(double value) { - return value * bg::math::d2r(); + return value * bg::math::d2r(); + } + + static inline rng convert(rng const& value) + { + return value * bg::math::d2r(); } }; @@ -75,10 +116,14 @@ struct other_system_info > typedef bg::degree units; typedef bg::cs::geographic type; - template - static inline T convert(T const& value) + static inline double convert(double value) { - return value * bg::math::r2d(); + return value * bg::math::r2d(); + } + + static inline rng convert(rng const& value) + { + return value * bg::math::r2d(); } }; @@ -88,15 +133,18 @@ struct other_system_info > typedef bg::radian units; typedef bg::cs::geographic type; - template - static inline T convert(T const& value) + static inline double convert(double value) { - return value * bg::math::d2r(); + return value * bg::math::d2r(); + } + + static inline rng convert(rng const& value) + { + return value * bg::math::d2r(); } }; - class equals_with_tolerance { private: @@ -118,11 +166,99 @@ private: public: equals_with_tolerance(double tolerence) : m_tolerence(tolerence) {} - template - inline bool operator()(T const& value1, T const& value2) const + inline bool operator()(double value1, double value2) const { return check_close(value1, value2, m_tolerence); } + + inline bool operator()(double l, rng const& r) const + { + return (r.lo < l && l < r.hi) + || check_close(l, r.lo, m_tolerence) + || check_close(l, r.hi, m_tolerence); + } +}; + + +bool equals_with_eps(double l, double r) +{ + return bg::math::equals(l, r); +} + +bool equals_with_eps(double l, rng r) +{ + return (r.lo < l && l < r.hi) + || bg::math::equals(l, r.lo) + || bg::math::equals(l, r.hi); +} + + +template +< + typename Box, + std::size_t DimensionCount = bg::dimension::value +> +struct box_check_equals +{ + template + static inline bool apply(Box const& box, + T1 const& lon_min, T2 const& lat_min, double, + T3 const& lon_max, T4 const& lat_max, double, + double tol) + { + equals_with_tolerance equals(tol); + +#ifndef BOOST_GEOMETRY_TEST_ENABLE_FAILING + // check latitude with tolerance when necessary + return equals_with_eps(bg::get<0, 0>(box), lon_min) + && (bg::get<0, 1>(box) < 0 + ? equals(bg::get<0, 1>(box), lat_min) + : equals_with_eps(bg::get<0, 1>(box), lat_min)) + && equals_with_eps(bg::get<1, 0>(box), lon_max) + && (bg::get<1, 1>(box) > 0 + ? equals(bg::get<1, 1>(box), lat_max) + : equals_with_eps(bg::get<1, 1>(box), lat_max)); +#else + // check latitude with tolerance when necessary + return bg::get<0, 0>(box) == lon_min + && (bg::get<0, 1>(box) < 0 + ? equals(bg::get<0, 1>(box), lat_min) + : bg::get<0, 1>(box) == lat_min) + && bg::get<1, 0>(box) == lon_max + && (bg::get<1, 1>(box) > 0 + ? equals(bg::get<1, 1>(box), lat_max) + : bg::get<1, 1>(box) == lat_max); +#endif + } +}; + +template +struct box_check_equals +{ + template + static inline bool apply(Box const& box, + T1 const& lon_min, T2 const& lat_min, double height_min, + T3 const& lon_max, T4 const& lat_max, double height_max, + double tol) + { +#ifndef BOOST_GEOMETRY_TEST_ENABLE_FAILING + equals_with_tolerance equals(tol); + + return box_check_equals::apply(box, + lon_min, lat_min, height_min, + lon_max, lat_max, height_max, + tol) + && equals(bg::get<0, 2>(box), height_min) + && equals(bg::get<1, 2>(box), height_max); +#else + return box_equals::apply(box, + lon_min, lat_min, height_min, + lon_max, lat_max, height_max, + tol) + && bg::get<0, 2>(box) == height_min + && bg::get<1, 2>(box) == height_max; +#endif + } }; @@ -136,48 +272,22 @@ struct box_equals { static inline bool apply(Box1 const& box1, Box2 const& box2, double tol) { - equals_with_tolerance equals(tol); - -#ifndef BOOST_GEOMETRY_TEST_ENABLE_FAILING - // check latitude with tolerance when necessary - return bg::math::equals(bg::get<0, 0>(box1), bg::get<0, 0>(box2)) - && (bg::get<0, 1>(box1) < 0 - ? equals(bg::get<0, 1>(box1), bg::get<0, 1>(box2)) - : bg::math::equals(bg::get<0, 1>(box1), bg::get<0, 1>(box2))) - && bg::math::equals(bg::get<1, 0>(box1), bg::get<1, 0>(box2)) - && (bg::get<1, 1>(box1) > 0 - ? equals(bg::get<1, 1>(box1), bg::get<1, 1>(box2)) - : bg::math::equals(bg::get<1, 1>(box1), bg::get<1, 1>(box2))); -#else - // check latitude with tolerance when necessary - return bg::get<0, 0>(box1) == bg::get<0, 0>(box2) - && (bg::get<0, 1>(box1) < 0 - ? equals(bg::get<0, 1>(box1), bg::get<0, 1>(box2)) - : bg::get<0, 1>(box1) == bg::get<0, 1>(box2)) - && bg::get<1, 0>(box1) == bg::get<1, 0>(box2) - && (bg::get<1, 1>(box1) > 0 - ? equals(bg::get<1, 1>(box1), bg::get<1, 1>(box2)) - : bg::get<1, 1>(box1) == bg::get<1, 1>(box2)); -#endif + return box_check_equals::apply(box1, + bg::get<0, 0>(box2), bg::get<0, 1>(box2), 0.0, + bg::get<1, 0>(box2), bg::get<1, 1>(box2), 0.0, + tol); } }; -template +template struct box_equals { static inline bool apply(Box1 const& box1, Box2 const& box2, double tol) { -#ifndef BOOST_GEOMETRY_TEST_ENABLE_FAILING - equals_with_tolerance equals(tol); - - return box_equals::apply(box1, box2, tol) - && equals(bg::get<0, 2>(box1), bg::get<0, 2>(box2)) - && equals(bg::get<1, 2>(box1), bg::get<1, 2>(box2)); -#else - return box_equals::apply(box1, box2, tol) - && bg::get<0, 2>(box1) == bg::get<0, 2>(box2) - && bg::get<1, 2>(box1) == bg::get<1, 2>(box2); -#endif + return box_check_equals::apply(box1, + bg::get<0, 0>(box2), bg::get<0, 1>(box2), bg::get<0, 2>(box2), + bg::get<1, 0>(box2), bg::get<1, 1>(box2), bg::get<1, 2>(box2), + tol); } }; From 056330d6106d4cff810cd72204cf3f05ed566a9f Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 5 Mar 2017 19:40:28 +0100 Subject: [PATCH 55/59] [doc][index] Add note about result sorting into the kNN query section. --- doc/index/rtree/query.qbk | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/index/rtree/query.qbk b/doc/index/rtree/query.qbk index 0cf80acc7..75f40ec29 100644 --- a/doc/index/rtree/query.qbk +++ b/doc/index/rtree/query.qbk @@ -1,7 +1,7 @@ [/============================================================================ Boost.Geometry Index - Copyright (c) 2011-2013 Adam Wulkiewicz. + Copyright (c) 2011-2017 Adam Wulkiewicz. Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -57,7 +57,7 @@ Query iterators returned by free functions __box__ box_region(...); std::copy(bgi::qbegin(rt, bgi::intersects(box_region)), bgi::qend(rt), std::back_inserter(returned_values)); -[h4 Spatial predicates] +[h4 Spatial queries] Queries using spatial predicates returns `__value__`s which are related somehow to some Geometry - box, polygon, etc. Names of spatial predicates correspond to names of __boost_geometry__ algorithms (boolean operations). @@ -129,6 +129,9 @@ The same way different query Geometries can be used: Segment seg(/*...*/); rt.query(bgi::nearest(seg, k), std::back_inserter(returned_values)); +[note In case of k-NN queries performed with `query()` function it's not guaranteed that the returned values will be sorted according to the distance. + It's different in case of k-NN queries performed with query iterator returned by `qbegin()` function which guarantees the iteration over the closest `__value__`s first. ] + [h4 User-defined unary predicate] The user may pass a `UnaryPredicate` - function, function object or lambda expression taking const reference to Value and returning bool. @@ -175,7 +178,7 @@ may use `index::satisfies()` function like on the example below: rt.query(index::intersects(box) && !index::satisfies(is_not_red), std::back_inserter(result)); -[h4 Passing a set of predicates] +[h4 Passing set of predicates] It's possible to use some number of predicates in one query by connecting them with `operator&&` e.g. `Pred1 && Pred2 && Pred3 && ...`. @@ -190,7 +193,7 @@ left-to-right so placing most restrictive predicates first should accelerate the rt.query(index::intersects(box1) && !index::within(box2) && index::overlaps(box3), std::back_inserter(result)); -Of course it's possible to connect different types of predicates together. +It's possible to connect different types of predicates together. index::query(rt, index::nearest(pt, k) && index::within(b), std::back_inserter(returned_values)); @@ -211,13 +214,11 @@ or may be stopped at some point, when all interesting values were gathered. The break; } -[note In the case of iterative k-NN queries it's guaranteed to iterate over the closest `__value__`s first. ] - [warning The modification of the `rtree`, e.g. insertion or removal of `__value__`s may invalidate the iterators. ] -[h4 Inserting query results into the other R-tree] +[h4 Inserting query results into another R-tree] -There are several ways of inserting Values returned by a query to the other R-tree container. +There are several ways of inserting Values returned by a query into another R-tree container. The most basic way is creating a temporary container for Values and insert them later. namespace bgi = boost::geometry::index; @@ -231,14 +232,13 @@ The most basic way is creating a temporary container for Values and insert them rt1.query(bgi::intersects(Box(/*...*/)), std::back_inserter(result)); RTree rt2(result.begin(), result.end()); -However there are better ways. One of these methods is mentioned in the "Creation and modification" section. +However there are other ways. One of these methods is mentioned in the "Creation and modification" section. The insert iterator may be passed directly into the query. RTree rt3; rt1.query(bgi::intersects(Box(/*...*/))), bgi::inserter(rt3)); -If you're a user of Boost.Range you'll appreciate the third option. You may pass the result Range directly into the -constructor or `insert()` member function. +You may also pass the resulting Range directly into the constructor or `insert()` member function using Boost.Range adaptor syntax. RTree rt4(rt1 | bgi::adaptors::queried(bgi::intersects(Box(/*...*/))))); From aff16c1615b9407fcde72cc20990ab410fabe739 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 5 Mar 2017 19:42:00 +0100 Subject: [PATCH 56/59] [doc][src] Fix compilation errors (lack of include, conflicts with QVM). --- doc/src/examples/algorithms/append.cpp | 4 ++-- doc/src/examples/core/tag.cpp | 4 ++-- doc/src/examples/geometries/adapted/boost_range/sliced.cpp | 4 ++-- doc/src/examples/geometries/adapted/boost_range/strided.cpp | 4 ++-- doc/src/examples/quick_start.cpp | 2 ++ 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/doc/src/examples/algorithms/append.cpp b/doc/src/examples/algorithms/append.cpp index f5a3085b5..fd90a6a04 100644 --- a/doc/src/examples/algorithms/append.cpp +++ b/doc/src/examples/algorithms/append.cpp @@ -12,12 +12,12 @@ #include -#include - #include #include #include +#include /*< At the end to avoid conflicts with Boost.QVM >*/ + BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian) int main() diff --git a/doc/src/examples/core/tag.cpp b/doc/src/examples/core/tag.cpp index bb5b765ec..5e4c70687 100644 --- a/doc/src/examples/core/tag.cpp +++ b/doc/src/examples/core/tag.cpp @@ -12,13 +12,13 @@ #include -#include - #include #include #include #include +#include /*< At the end to avoid conflicts with Boost.QVM >*/ + BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian) template struct dispatch {}; diff --git a/doc/src/examples/geometries/adapted/boost_range/sliced.cpp b/doc/src/examples/geometries/adapted/boost_range/sliced.cpp index bdfd74c62..45cab8195 100644 --- a/doc/src/examples/geometries/adapted/boost_range/sliced.cpp +++ b/doc/src/examples/geometries/adapted/boost_range/sliced.cpp @@ -12,13 +12,13 @@ #include -#include - #include #include #include #include +#include /*< At the end to avoid conflicts with Boost.QVM >*/ + int main() { diff --git a/doc/src/examples/geometries/adapted/boost_range/strided.cpp b/doc/src/examples/geometries/adapted/boost_range/strided.cpp index 8b9dc6865..1ed9b5ecc 100644 --- a/doc/src/examples/geometries/adapted/boost_range/strided.cpp +++ b/doc/src/examples/geometries/adapted/boost_range/strided.cpp @@ -12,13 +12,13 @@ #include -#include - #include #include #include #include +#include /*< At the end to avoid conflicts with Boost.QVM >*/ + int main() { diff --git a/doc/src/examples/quick_start.cpp b/doc/src/examples/quick_start.cpp index f77c8909b..41d3beb32 100644 --- a/doc/src/examples/quick_start.cpp +++ b/doc/src/examples/quick_start.cpp @@ -19,6 +19,8 @@ //#pragma warning( disable : 4244 ) #endif // defined(_MSC_VER) +#include + //[quickstart_include #include From db052113a8f224287f374cc663f2bd46e88f2569 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 5 Mar 2017 20:18:10 +0100 Subject: [PATCH 57/59] [area][strategies] Update doc-related description of area() and related strategies. --- include/boost/geometry/algorithms/area.hpp | 7 +++++- .../strategies/geographic/area_geographic.hpp | 22 +++++++++++++++---- .../strategies/spherical/area_spherical.hpp | 11 ++++++++-- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/include/boost/geometry/algorithms/area.hpp b/include/boost/geometry/algorithms/area.hpp index 4751d4e74..18aea2403 100644 --- a/include/boost/geometry/algorithms/area.hpp +++ b/include/boost/geometry/algorithms/area.hpp @@ -4,6 +4,10 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -303,7 +307,8 @@ inline typename default_area_result::type area(Geometry const& geometr [heading Available Strategies] \* [link geometry.reference.strategies.strategy_area_surveyor Surveyor (cartesian)] -\* [link geometry.reference.strategies.strategy_area_huiller Huiller (spherical)] +\* [link geometry.reference.strategies.strategy_area_spherical Spherical] +[/link geometry.reference.strategies.strategy_area_geographic Geographic] } */ template diff --git a/include/boost/geometry/strategies/geographic/area_geographic.hpp b/include/boost/geometry/strategies/geographic/area_geographic.hpp index 390da433f..d5cd240a7 100644 --- a/include/boost/geometry/strategies/geographic/area_geographic.hpp +++ b/include/boost/geometry/strategies/geographic/area_geographic.hpp @@ -23,12 +23,26 @@ namespace strategy { namespace area { /*! -\brief Geographic area calculation by trapezoidal rule plus integral -approximation that gives the ellipsoidal correction - +\brief Geographic area calculation +\ingroup strategies +\details Geographic area calculation by trapezoidal rule plus integral + approximation that gives the ellipsoidal correction +\tparam PointOfSegment \tparam_segment_point +\tparam Inverse Formula used to calculate azimuths +\tparam SeriesOrder The order of approximation of the geodesic integral +\tparam ExpandEpsN Switch between two kinds of approximation (series in eps and n v.s. series in k^2 and e'^2) +\tparam LongSegment Enables special handling of long segments +\tparam Spheroid The spheroid model +\tparam CalculationType \tparam_calculation +\author See +- Danielsen JS, The area under the geodesic. Surv Rev 30(232): 61–66, 1989 +- Charles F.F Karney, Algorithms for geodesics, 2011 https://arxiv.org/pdf/1109.4448.pdf +\qbk{ +[heading See also] +[link geometry.reference.algorithms.area.area_2_with_strategy area (with strategy)] +} */ - template < typename PointOfSegment, diff --git a/include/boost/geometry/strategies/spherical/area_spherical.hpp b/include/boost/geometry/strategies/spherical/area_spherical.hpp index 4aa038253..c03d70f64 100644 --- a/include/boost/geometry/strategies/spherical/area_spherical.hpp +++ b/include/boost/geometry/strategies/spherical/area_spherical.hpp @@ -25,10 +25,17 @@ namespace strategy { namespace area { /*! -\brief Spherical area calculation by trapezoidal rule +\brief Spherical area calculation +\ingroup strategies +\details Calculates area on the surface of a sphere using the trapezoidal rule +\tparam PointOfSegment \tparam_segment_point +\tparam LongSegment Enables special handling of long segments +\tparam CalculationType \tparam_calculation +\qbk{ +[heading See also] +[link geometry.reference.algorithms.area.area_2_with_strategy area (with strategy)] } - */ template < From 62acebb640bad19377d1a093559e353b521998c4 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 5 Mar 2017 20:22:01 +0100 Subject: [PATCH 58/59] [algorithms] Update doc-related description of set operations, is_simple and is_valid. Distinguish between versions taking and not taking strategies. --- .../geometry/algorithms/detail/intersection/interface.hpp | 1 + .../boost/geometry/algorithms/detail/is_simple/interface.hpp | 1 + .../boost/geometry/algorithms/detail/is_valid/interface.hpp | 5 +++-- include/boost/geometry/algorithms/difference.hpp | 1 + include/boost/geometry/algorithms/sym_difference.hpp | 1 + include/boost/geometry/algorithms/union.hpp | 1 + 6 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/intersection/interface.hpp b/include/boost/geometry/algorithms/detail/intersection/interface.hpp index cd806d9c7..0efc9731b 100644 --- a/include/boost/geometry/algorithms/detail/intersection/interface.hpp +++ b/include/boost/geometry/algorithms/detail/intersection/interface.hpp @@ -336,6 +336,7 @@ struct intersection, variant > \param strategy \param_strategy{is_simple} \return \return_check{is simple} +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/is_simple.qbk]} */ template diff --git a/include/boost/geometry/algorithms/detail/is_valid/interface.hpp b/include/boost/geometry/algorithms/detail/is_valid/interface.hpp index 7561a0cb8..ee013377c 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/interface.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/interface.hpp @@ -136,6 +136,7 @@ multi-geometries with no elements, linear geometries containing spikes, areal geometries with duplicate (consecutive) points} +\qbk{distinguish,with strategy} \qbk{[include reference/algorithms/is_valid.qbk]} */ template @@ -180,7 +181,7 @@ inline bool is_valid(Geometry const& geometry) linear geometries containing spikes, areal geometries with duplicate (consecutive) points} -\qbk{distinguish,with failure value} +\qbk{distinguish,with failure value and strategy} \qbk{[include reference/algorithms/is_valid_with_failure.qbk]} */ template @@ -230,7 +231,7 @@ inline bool is_valid(Geometry const& geometry, validity_failure_type& failure) linear geometries containing spikes, areal geometries with duplicate (consecutive) points} -\qbk{distinguish,with message} +\qbk{distinguish,with message and strategy} \qbk{[include reference/algorithms/is_valid_with_message.qbk]} */ template diff --git a/include/boost/geometry/algorithms/difference.hpp b/include/boost/geometry/algorithms/difference.hpp index 06c79dc15..c11ceca24 100644 --- a/include/boost/geometry/algorithms/difference.hpp +++ b/include/boost/geometry/algorithms/difference.hpp @@ -346,6 +346,7 @@ struct difference, variant, variant, variant Date: Sun, 5 Mar 2017 20:24:23 +0100 Subject: [PATCH 59/59] [doc] Update reference (area strategy, set operations, is_simple, is_valid). Remove area::huiller, add area::spherical strategy. Distinguish between versions of algorithms taking and not taking strategies. --- doc/doxy/Doxyfile | 1 + doc/make_qbk.py | 4 +++- doc/quickref.xml | 9 +++++---- doc/reference.qbk | 19 ++++++++++++------- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/doc/doxy/Doxyfile b/doc/doxy/Doxyfile index 4c9aa41d3..aab8e217b 100644 --- a/doc/doxy/Doxyfile +++ b/doc/doxy/Doxyfile @@ -211,6 +211,7 @@ INPUT = . .. ../../../../boost/geometry/core \ ../../../../boost/geometry/strategies/agnostic \ ../../../../boost/geometry/strategies/cartesian \ ../../../../boost/geometry/strategies/spherical \ + ../../../../boost/geometry/strategies/geographic \ ../../../../boost/geometry/strategies/transform \ ../../../../boost/geometry/util \ ../../../../boost/geometry/views \ diff --git a/doc/make_qbk.py b/doc/make_qbk.py index db6cc0ab9..184e42223 100755 --- a/doc/make_qbk.py +++ b/doc/make_qbk.py @@ -4,6 +4,7 @@ # Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. # Copyright (c) 2008-2012 Bruno Lalande, Paris, France. # Copyright (c) 2009-2012 Mateusz Loskot (mateusz@loskot.net), London, UK +# Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland # # Use, modification and distribution is subject to the Boost Software License, # Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -123,7 +124,8 @@ strategies = ["distance::pythagoras", "distance::pythagoras_box_box" , "distance::cross_track", "distance::cross_track_point_box" , "distance::projected_point" , "within::winding", "within::franklin", "within::crossings_multiply" - , "area::surveyor", "area::huiller" + , "area::surveyor", "area::spherical" + #, "area::geographic" , "buffer::point_circle", "buffer::point_square" , "buffer::join_round", "buffer::join_miter" , "buffer::end_round", "buffer::end_flat" diff --git a/doc/quickref.xml b/doc/quickref.xml index 5b095811a..755fdd255 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -10,10 +10,10 @@ Copyright (c) 2009-2015 Bruno Lalande, Paris, France. Copyright (c) 2013-2015 Adam Wulkiewicz, Lodz, Poland. - This file was modified by Oracle on 2014, 2015. - Modifications copyright (c) 2014-2015, Oracle and/or its affiliates. - + This file was modified by Oracle on 2014, 2015, 2017. + Modifications copyright (c) 2014-2017, Oracle and/or its affiliates. Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -521,7 +521,8 @@ Area strategy::area::surveyor - + strategy::area::spherical + diff --git a/doc/reference.qbk b/doc/reference.qbk index f73060181..e84115ece 100644 --- a/doc/reference.qbk +++ b/doc/reference.qbk @@ -99,7 +99,9 @@ [include generated/crosses.qbk] [endsect] +[section:difference difference] [include generated/difference.qbk] +[endsect] [section:disjoint disjoint] [include generated/disjoint.qbk] @@ -125,9 +127,9 @@ [include generated/for_each.qbk] [endsect] -[/section:intersection intersection] +[section:intersection intersection] [include generated/intersection.qbk] -[/endsect] +[endsect] [section:intersects intersects] [include generated/intersects.qbk] @@ -135,7 +137,9 @@ [include generated/is_empty.qbk] +[section:is_simple is_simple] [include generated/is_simple.qbk] +[endsect] [section:is_valid is_valid] [include generated/is_valid.qbk] @@ -176,9 +180,9 @@ [include generated/simplify.qbk] [endsect] -[/section:sym_difference sym_difference] +[section:sym_difference sym_difference] [include generated/sym_difference.qbk] -[/endsect] +[endsect] [section:touches touches] [include generated/touches.qbk] @@ -188,9 +192,9 @@ [include generated/transform.qbk] [endsect] -[/section:union union] +[section:union_ union_] [include generated/union.qbk] -[/endsect] +[endsect] [include generated/unique.qbk] @@ -333,7 +337,8 @@ [include generated/distance_cross_track.qbk] [include generated/distance_cross_track_point_box.qbk] [include generated/area_surveyor.qbk] -[/include generated/area_huiller.qbk] +[include generated/area_spherical.qbk] +[/include generated/area_geographic.qbk] [include generated/buffer_join_round.qbk] [include generated/buffer_join_miter.qbk] [include generated/buffer_end_round.qbk]