diff --git a/include/boost/geometry/algorithms/detail/assign_values.hpp b/include/boost/geometry/algorithms/detail/assign_values.hpp index 628c788e02..fe72bc5683 100644 --- a/include/boost/geometry/algorithms/detail/assign_values.hpp +++ b/include/boost/geometry/algorithms/detail/assign_values.hpp @@ -26,7 +26,6 @@ #include #include -#include #include #include @@ -38,7 +37,7 @@ #include #include -#include +#include #include @@ -74,8 +73,8 @@ struct assign_inverse_box_or_segment { typedef typename coordinate_type::type coordinate_type; - coordinate_type const highest = geometry::bounds::highest(); - coordinate_type const lowest = geometry::bounds::lowest(); + coordinate_type const highest = util::bounds::highest(); + coordinate_type const lowest = util::bounds::lowest(); detail::for_each_dimension([&](auto dimension) { set<0, dimension>(geometry, highest); diff --git a/include/boost/geometry/algorithms/detail/envelope/initialize.hpp b/include/boost/geometry/algorithms/detail/envelope/initialize.hpp index d8e252b53a..8d83426619 100644 --- a/include/boost/geometry/algorithms/detail/envelope/initialize.hpp +++ b/include/boost/geometry/algorithms/detail/envelope/initialize.hpp @@ -13,11 +13,10 @@ #include -#include - #include #include #include +#include namespace boost { namespace geometry @@ -67,9 +66,9 @@ struct initialize static inline void apply(Box& box, coordinate_type min_value - = boost::numeric::bounds::highest(), + = util::bounds::highest(), coordinate_type max_value - = boost::numeric::bounds::lowest()) + = util::bounds::lowest()) { initialize_loop < diff --git a/include/boost/geometry/util/bounds.hpp b/include/boost/geometry/util/bounds.hpp new file mode 100644 index 0000000000..dbd3920ffb --- /dev/null +++ b/include/boost/geometry/util/bounds.hpp @@ -0,0 +1,28 @@ +// Boost.Geometry + +// Copyright (c) 2024 Barend Gehrels, Amsterdam, the Netherlands. + +// 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_UTIL_BOUNDS_HPP +#define BOOST_GEOMETRY_UTIL_BOUNDS_HPP + +#include + +namespace boost { namespace geometry { namespace util +{ + +// Define a boost::geometry::util::bounds +// It might be specialized for other numeric types, for example Boost.Rational +template +struct bounds +{ + static CT lowest () { return boost::numeric::bounds::lowest(); } + static CT highest () { return boost::numeric::bounds::highest(); } +}; + +}}} // namespace boost::geometry::util + +#endif // BOOST_GEOMETRY_UTIL_BOUNDS_HPP diff --git a/include/boost/geometry/util/is_inverse_spheroidal_coordinates.hpp b/include/boost/geometry/util/is_inverse_spheroidal_coordinates.hpp index 59746653a6..8bae1ecf76 100644 --- a/include/boost/geometry/util/is_inverse_spheroidal_coordinates.hpp +++ b/include/boost/geometry/util/is_inverse_spheroidal_coordinates.hpp @@ -16,26 +16,20 @@ #include #include +#include #include namespace boost { namespace geometry { -template -struct bounds -{ - static CT lowest () { return boost::numeric::bounds::lowest(); } - static CT highest () { return boost::numeric::bounds::highest(); } -}; - template bool is_inverse_spheroidal_coordinates(Box const& box) { typedef typename point_type::type point_type; typedef typename coordinate_type::type bound_type; - bound_type high = bounds::highest(); - bound_type low = bounds::lowest(); + bound_type const high = util::bounds::highest(); + bound_type const low = util::bounds::lowest(); return (geometry::get<0, 0>(box) == high) && (geometry::get<0, 1>(box) == high) && diff --git a/include/boost/geometry/util/numeric_cast.hpp b/include/boost/geometry/util/numeric_cast.hpp index 5ca91092a4..3809ebae77 100644 --- a/include/boost/geometry/util/numeric_cast.hpp +++ b/include/boost/geometry/util/numeric_cast.hpp @@ -10,7 +10,6 @@ #define BOOST_GEOMETRY_UTIL_NUMERIC_CAST_HPP #include -#include namespace boost { namespace geometry { namespace util @@ -30,21 +29,11 @@ struct numeric_caster } }; -// Specialization for Boost.Rational -template -struct numeric_caster> -{ - static inline Target apply(rational const& source) - { - return boost::rational_cast(source); - } -}; - } // namespace detail #endif // Calls either boost::numeric_cast, or functionality specific for Boost.Geometry -// such as rational_cast for Boost.Rational +// (such as rational_cast for Boost.Rational) template inline Target numeric_cast(Source const& source) { diff --git a/include/boost/geometry/util/rational.hpp b/include/boost/geometry/util/rational.hpp index ee63d48b8d..bfe6161e76 100644 --- a/include/boost/geometry/util/rational.hpp +++ b/include/boost/geometry/util/rational.hpp @@ -14,9 +14,11 @@ #ifndef BOOST_GEOMETRY_UTIL_RATIONAL_HPP #define BOOST_GEOMETRY_UTIL_RATIONAL_HPP +// Contains specializations for Boost.Rational + #include -#include +#include #include #include #include @@ -111,14 +113,30 @@ struct select_most_precise, double> typedef typename boost::rational type; }; +namespace util +{ -}} // namespace boost::geometry - +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ -// Specializes boost::rational to boost::numeric::bounds -namespace boost { namespace numeric +// Specialize numeric_caster, needed for geomery::util::numeric_cast, for Boost.Rational +// Without it, code using Boost.Rational does not compile +template +struct numeric_caster> { + static inline Target apply(rational const& source) + { + return boost::rational_cast(source); + } +}; + +} // namespace detail +#endif + +// Specializes geometry::util::bounds for Boost.Rational +// Without it, bounds contains (0,1) by default for Boost.Rational template struct bounds > { @@ -132,7 +150,9 @@ struct bounds > } }; -}} // namespace boost::numeric +} // namespace util + +}} // namespace boost::geometry #endif // BOOST_GEOMETRY_UTIL_RATIONAL_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f34170500d..11287a29ce 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -48,3 +48,4 @@ if (NOT TARGET tests) endif() add_subdirectory(algorithms) +add_subdirectory(util) diff --git a/test/algorithms/CMakeLists.txt b/test/algorithms/CMakeLists.txt index fd43712036..de99aefde8 100644 --- a/test/algorithms/CMakeLists.txt +++ b/test/algorithms/CMakeLists.txt @@ -8,6 +8,7 @@ add_subdirectory(area) add_subdirectory(buffer) add_subdirectory(convex_hull) add_subdirectory(detail) +add_subdirectory(envelope_expand) add_subdirectory(overlay) add_subdirectory(relate) add_subdirectory(set_operations) diff --git a/test/algorithms/envelope_expand/CMakeLists.txt b/test/algorithms/envelope_expand/CMakeLists.txt new file mode 100644 index 0000000000..bd229c48af --- /dev/null +++ b/test/algorithms/envelope_expand/CMakeLists.txt @@ -0,0 +1,23 @@ +# Boost.Geometry +# Copyright (c) 2024 Barend Gehrels, Amsterdam, the Netherlands. +# 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) + +foreach(item IN ITEMS + envelope + envelope_multi + expand + expand_on_spheroid + ) + boost_geometry_add_unit_test("algorithms" ${item}) +endforeach() + +if (NOT APPLE) + # The results of these tests vary considerably on Apple/Darwin/arm64 using clang + foreach(item IN ITEMS + envelope_on_spheroid + ) + boost_geometry_add_unit_test("algorithms" ${item}) + endforeach() +endif() \ No newline at end of file diff --git a/test/algorithms/envelope_expand/envelope.cpp b/test/algorithms/envelope_expand/envelope.cpp index 19c5af5880..df6db7437a 100644 --- a/test/algorithms/envelope_expand/envelope.cpp +++ b/test/algorithms/envelope_expand/envelope.cpp @@ -18,8 +18,6 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#include - #include "test_envelope.hpp" #include @@ -27,6 +25,7 @@ #include #include #include +#include #include BOOST_GEOMETRY_REGISTER_C_ARRAY_CS(cs::cartesian) @@ -68,8 +67,8 @@ template void test_empty_geometry(std::string const& wkt) { typedef typename bg::coordinate_type::type ct; - ct high_val = boost::numeric::bounds::highest(); - ct low_val = boost::numeric::bounds::lowest(); + ct const high_val = bg::util::bounds::highest(); + ct const low_val = bg::util::bounds::lowest(); test_envelope(wkt, high_val, low_val, high_val, low_val); } diff --git a/test/algorithms/envelope_expand/envelope_on_spheroid.cpp b/test/algorithms/envelope_expand/envelope_on_spheroid.cpp index c2e20ce1e0..9d2bd688d8 100644 --- a/test/algorithms/envelope_expand/envelope_on_spheroid.cpp +++ b/test/algorithms/envelope_expand/envelope_on_spheroid.cpp @@ -22,8 +22,6 @@ #include #include -#include - #include #include #include "test_envelope_expand_on_spheroid.hpp" @@ -38,6 +36,7 @@ #include #include #include +#include #include #include @@ -492,8 +491,8 @@ void test_empty_geometry(std::string const& case_id, std::string const& wkt) typedef test_envelope_on_sphere_or_spheroid tester; typedef typename bg::coordinate_type::type ct; - ct high_val = boost::numeric::bounds::highest(); - ct low_val = boost::numeric::bounds::lowest(); + ct const high_val = bg::util::bounds::highest(); + ct const low_val = bg::util::bounds::lowest(); if (BOOST_GEOMETRY_CONDITION(dim == 2)) { diff --git a/test/geometries/box.cpp b/test/geometries/box.cpp index cfe5fbfdca..b5910a33da 100644 --- a/test/geometries/box.cpp +++ b/test/geometries/box.cpp @@ -69,12 +69,12 @@ void test_construction() check_box(b2, 1,2,5,3,4,6); bg::model::box

b3 = bg::make_inverse >(); - check_box(b3, boost::numeric::bounds::highest(), - boost::numeric::bounds::highest(), - boost::numeric::bounds::highest(), - boost::numeric::bounds::lowest(), - boost::numeric::bounds::lowest(), - boost::numeric::bounds::lowest()); + check_box(b3, bg::util::bounds::highest(), + bg::util::bounds::highest(), + bg::util::bounds::highest(), + bg::util::bounds::lowest(), + bg::util::bounds::lowest(), + bg::util::bounds::lowest()); } template diff --git a/test/util/CMakeLists.txt b/test/util/CMakeLists.txt new file mode 100644 index 0000000000..de954beb64 --- /dev/null +++ b/test/util/CMakeLists.txt @@ -0,0 +1,24 @@ +# Boost.Geometry +# Copyright (c) 2024 Barend Gehrels, Amsterdam, the Netherlands. +# 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) + +foreach(item IN ITEMS + algorithm + calculation_type + for_each_coordinate + math_abs + math_divide + math_equals + math_sqrt + math_normalize_spheroidal + promote_integral + range + rational + select_most_precise + tuples + write_dsv + ) + boost_geometry_add_unit_test("util" ${item}) +endforeach() diff --git a/test/util/rational.cpp b/test/util/rational.cpp index e538c38aa9..1e3056edf3 100644 --- a/test/util/rational.cpp +++ b/test/util/rational.cpp @@ -1,7 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2007-2024 Barend Gehrels, Amsterdam, the Netherlands. // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. @@ -15,6 +15,11 @@ #include +#include +#include +#include +#include +#include #include #include #include @@ -22,21 +27,93 @@ void test_coordinate_cast(std::string const& s, int expected_nom, int expected_denom) { - boost::rational a = bg::detail::coordinate_cast >::apply(s); + boost::rational const a = bg::detail::coordinate_cast >::apply(s); BOOST_CHECK_EQUAL(a.numerator(), expected_nom); BOOST_CHECK_EQUAL(a.denominator(), expected_denom); } void test_numeric_cast() { - const boost::rational r1(3, 4); + boost::rational const r1(3, 4); BOOST_CHECK_CLOSE(bg::util::numeric_cast(r1), 0.75, 0.00001); - const boost::rational r2(10, 4); + boost::rational const r2(10, 4); BOOST_CHECK_CLOSE(bg::util::numeric_cast(r2), 2.5, 0.00001); BOOST_CHECK_EQUAL(bg::util::numeric_cast(r2), 2); } +template +void test_bounds() +{ + using coordinate_t = boost::rational; + using point_t = bg::model::point; + + auto const lowest = bg::util::bounds::lowest(); + auto const highest = bg::util::bounds::highest(); + + BOOST_CHECK_MESSAGE(lowest < highest, + "Lowest should be smaller than highest, lowest: " << lowest << " highest: " << highest); +} + +// Tests box-related functionality, which depends on geometry::util::bounds +// specialization for Boost.Rational +template +void test_box() +{ + using coordinate_t = boost::rational; + using point_t = bg::model::point; + using box_t = bg::model::box; + + box_t box; + bg::assign_inverse(box); + + point_t south_west, north_east; + bg::detail::assign_point_from_index<0>(box, south_west); + bg::detail::assign_point_from_index<1>(box, north_east); + + BOOST_CHECK_MESSAGE(bg::get<0>(south_west) > bg::get<0>(north_east), + "Bounding box should be inversed. Now x-min: " << bg::get<0>(south_west) + << " x-max: " << bg::get<0>(north_east) + << " " << bg::wkt(box)); + + BOOST_CHECK_MESSAGE(bg::get<1>(south_west) > bg::get<1>(north_east), + "Bounding box should be inversed. Now y-min: " << bg::get<1>(south_west) + << " y-max: " << bg::get<1>(north_east) + << " " << bg::wkt(box)); + + // Test specifically for points larger than 0, because without specialization Boost.Rational + // will return (0,1) (== 0) by default and code will compile but give wrong results. + bg::expand(box, bg::make(4, 4)); + bg::expand(box, bg::make(8, 8)); + + // Test within (without specialization, both points are within the box) + auto const point1 = bg::make(6, 6); + auto const point2 = bg::make(2, 2); + BOOST_CHECK_MESSAGE(bg::within(point1, box), + "Point " << bg::wkt(point1) << " is not within the box " << bg::wkt(box)); + BOOST_CHECK_MESSAGE(! bg::within(point2, box), + "Point " << bg::wkt(point2) << " is within the box " << bg::wkt(box)); + + // Test area (without specialization, it will be 64) + auto const area = bg::util::numeric_cast(bg::area(box)); + T const expected_area = 16; + BOOST_CHECK_EQUAL(expected_area, area); +} + +void test_select_most_precise() +{ + using rational1_t = boost::rational; + using rational2_t = boost::rational; + + using t1 = bg::select_most_precise::type; + using t2 = bg::select_most_precise::type; + using t12 = bg::select_most_precise::type; + + BOOST_CHECK((std::is_same::value)); + BOOST_CHECK((std::is_same::value)); + BOOST_CHECK((std::is_same::value)); +} + void test_wkt(std::string const& wkt, std::string const expected_wkt) { bg::model::point, 2, bg::cs::cartesian> p; @@ -64,6 +141,14 @@ int test_main(int, char* []) test_numeric_cast(); + test_bounds(); + test_bounds(); + test_bounds(); + + test_box(); + + test_select_most_precise(); + test_wkt("POINT(1.5 2.75)", "POINT(3/2 11/4)"); test_wkt("POINT(3/2 11/4)", "POINT(3/2 11/4)"); test_wkt("POINT(-1.5 2.75)", "POINT(-3/2 11/4)");