[util] move bounds to geometry::util

This commit is contained in:
Barend Gehrels 2024-03-02 16:32:20 +00:00
parent 993b36515c
commit d9eface3e8
14 changed files with 217 additions and 56 deletions

View File

@ -26,7 +26,6 @@
#include <boost/concept/requires.hpp>
#include <boost/concept_check.hpp>
#include <boost/numeric/conversion/bounds.hpp>
#include <boost/geometry/algorithms/append.hpp>
#include <boost/geometry/algorithms/clear.hpp>
@ -38,7 +37,7 @@
#include <boost/geometry/geometries/concepts/check.hpp>
#include <boost/geometry/util/algorithm.hpp>
#include <boost/geometry/util/is_inverse_spheroidal_coordinates.hpp>
#include <boost/geometry/util/bounds.hpp>
#include <boost/geometry/util/numeric_cast.hpp>
@ -74,8 +73,8 @@ struct assign_inverse_box_or_segment
{
typedef typename coordinate_type<BoxOrSegment>::type coordinate_type;
coordinate_type const highest = geometry::bounds<coordinate_type>::highest();
coordinate_type const lowest = geometry::bounds<coordinate_type>::lowest();
coordinate_type const highest = util::bounds<coordinate_type>::highest();
coordinate_type const lowest = util::bounds<coordinate_type>::lowest();
detail::for_each_dimension<BoxOrSegment>([&](auto dimension)
{
set<0, dimension>(geometry, highest);

View File

@ -13,11 +13,10 @@
#include <cstddef>
#include <boost/numeric/conversion/bounds.hpp>
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/coordinate_dimension.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/util/bounds.hpp>
namespace boost { namespace geometry
@ -67,9 +66,9 @@ struct initialize
static inline void apply(Box& box,
coordinate_type min_value
= boost::numeric::bounds<coordinate_type>::highest(),
= util::bounds<coordinate_type>::highest(),
coordinate_type max_value
= boost::numeric::bounds<coordinate_type>::lowest())
= util::bounds<coordinate_type>::lowest())
{
initialize_loop
<

View File

@ -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 <boost/numeric/conversion/bounds.hpp>
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<class CT>
struct bounds
{
static CT lowest () { return boost::numeric::bounds<CT>::lowest(); }
static CT highest () { return boost::numeric::bounds<CT>::highest(); }
};
}}} // namespace boost::geometry::util
#endif // BOOST_GEOMETRY_UTIL_BOUNDS_HPP

View File

@ -16,26 +16,20 @@
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/point_type.hpp>
#include <boost/geometry/util/bounds.hpp>
#include <boost/geometry/util/math.hpp>
namespace boost { namespace geometry
{
template<class CT>
struct bounds
{
static CT lowest () { return boost::numeric::bounds<CT>::lowest(); }
static CT highest () { return boost::numeric::bounds<CT>::highest(); }
};
template <typename Box>
bool is_inverse_spheroidal_coordinates(Box const& box)
{
typedef typename point_type<Box>::type point_type;
typedef typename coordinate_type<point_type>::type bound_type;
bound_type high = bounds<bound_type>::highest();
bound_type low = bounds<bound_type>::lowest();
bound_type const high = util::bounds<bound_type>::highest();
bound_type const low = util::bounds<bound_type>::lowest();
return (geometry::get<0, 0>(box) == high) &&
(geometry::get<0, 1>(box) == high) &&

View File

@ -10,7 +10,6 @@
#define BOOST_GEOMETRY_UTIL_NUMERIC_CAST_HPP
#include <boost/numeric/conversion/cast.hpp>
#include <boost/rational.hpp>
namespace boost { namespace geometry { namespace util
@ -30,21 +29,11 @@ struct numeric_caster
}
};
// Specialization for Boost.Rational
template <typename Target, typename T>
struct numeric_caster<Target, rational<T>>
{
static inline Target apply(rational<T> const& source)
{
return boost::rational_cast<Target>(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 <typename Target, typename Source>
inline Target numeric_cast(Source const& source)
{

View File

@ -14,9 +14,11 @@
#ifndef BOOST_GEOMETRY_UTIL_RATIONAL_HPP
#define BOOST_GEOMETRY_UTIL_RATIONAL_HPP
#include <boost/rational.hpp>
#include <boost/numeric/conversion/bounds.hpp>
// Contains specializations for Boost.Rational
#include <boost/rational.hpp>
#include <boost/geometry/util/bounds.hpp>
#include <boost/geometry/util/coordinate_cast.hpp>
#include <boost/geometry/util/numeric_cast.hpp>
#include <boost/geometry/util/select_most_precise.hpp>
@ -111,14 +113,30 @@ struct select_most_precise<boost::rational<T>, double>
typedef typename boost::rational<T> type;
};
}} // namespace boost::geometry
// Specializes boost::rational to boost::numeric::bounds
namespace boost { namespace numeric
namespace util
{
#ifndef DOXYGEN_NO_DETAIL
namespace detail
{
// Specialize numeric_caster, needed for geomery::util::numeric_cast, for Boost.Rational
// Without it, code using Boost.Rational does not compile
template <typename Target, typename T>
struct numeric_caster<Target, rational<T>>
{
static inline Target apply(rational<T> const& source)
{
return boost::rational_cast<Target>(source);
}
};
} // namespace detail
#endif
// Specializes geometry::util::bounds for Boost.Rational
// Without it, bounds contains (0,1) by default for Boost.Rational
template<class T>
struct bounds<rational<T> >
{
@ -132,7 +150,9 @@ struct bounds<rational<T> >
}
};
}} // namespace boost::numeric
} // namespace util
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_UTIL_RATIONAL_HPP

View File

@ -48,3 +48,4 @@ if (NOT TARGET tests)
endif()
add_subdirectory(algorithms)
add_subdirectory(util)

View File

@ -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)

View File

@ -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()

View File

@ -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 <boost/numeric/conversion/bounds.hpp>
#include "test_envelope.hpp"
#include <boost/geometry/geometries/geometries.hpp>
@ -27,6 +25,7 @@
#include <boost/geometry/geometries/adapted/c_array.hpp>
#include <boost/geometry/geometries/adapted/boost_tuple.hpp>
#include <boost/geometry/geometries/adapted/std_pair_as_segment.hpp>
#include <boost/geometry/util/bounds.hpp>
#include <test_common/test_point.hpp>
BOOST_GEOMETRY_REGISTER_C_ARRAY_CS(cs::cartesian)
@ -68,8 +67,8 @@ template <typename Geometry>
void test_empty_geometry(std::string const& wkt)
{
typedef typename bg::coordinate_type<Geometry>::type ct;
ct high_val = boost::numeric::bounds<ct>::highest();
ct low_val = boost::numeric::bounds<ct>::lowest();
ct const high_val = bg::util::bounds<ct>::highest();
ct const low_val = bg::util::bounds<ct>::lowest();
test_envelope<Geometry>(wkt, high_val, low_val, high_val, low_val);
}

View File

@ -22,8 +22,6 @@
#include <iostream>
#include <string>
#include <boost/numeric/conversion/bounds.hpp>
#include <from_wkt.hpp>
#include <geometry_test_common.hpp>
#include "test_envelope_expand_on_spheroid.hpp"
@ -38,6 +36,7 @@
#include <boost/geometry/index/detail/algorithms/is_valid.hpp>
#include <boost/geometry/io/dsv/write.hpp>
#include <boost/geometry/io/wkt/wkt.hpp>
#include <boost/geometry/util/bounds.hpp>
#include <boost/geometry/util/condition.hpp>
#include <boost/geometry/util/type_traits.hpp>
@ -492,8 +491,8 @@ void test_empty_geometry(std::string const& case_id, std::string const& wkt)
typedef test_envelope_on_sphere_or_spheroid<Geometry, B> tester;
typedef typename bg::coordinate_type<Geometry>::type ct;
ct high_val = boost::numeric::bounds<ct>::highest();
ct low_val = boost::numeric::bounds<ct>::lowest();
ct const high_val = bg::util::bounds<ct>::highest();
ct const low_val = bg::util::bounds<ct>::lowest();
if (BOOST_GEOMETRY_CONDITION(dim == 2))
{

View File

@ -69,12 +69,12 @@ void test_construction()
check_box(b2, 1,2,5,3,4,6);
bg::model::box<P> b3 = bg::make_inverse<bg::model::box<P> >();
check_box(b3, boost::numeric::bounds<T>::highest(),
boost::numeric::bounds<T>::highest(),
boost::numeric::bounds<T>::highest(),
boost::numeric::bounds<T>::lowest(),
boost::numeric::bounds<T>::lowest(),
boost::numeric::bounds<T>::lowest());
check_box(b3, bg::util::bounds<T>::highest(),
bg::util::bounds<T>::highest(),
bg::util::bounds<T>::highest(),
bg::util::bounds<T>::lowest(),
bg::util::bounds<T>::lowest(),
bg::util::bounds<T>::lowest());
}
template <typename P>

24
test/util/CMakeLists.txt Normal file
View File

@ -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()

View File

@ -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 <geometry_test_common.hpp>
#include <boost/geometry/algorithms/area.hpp>
#include <boost/geometry/algorithms/assign.hpp>
#include <boost/geometry/algorithms/make.hpp>
#include <boost/geometry/algorithms/expand.hpp>
#include <boost/geometry/algorithms/within.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/io/wkt/wkt.hpp>
#include <boost/geometry/util/numeric_cast.hpp>
@ -22,21 +27,93 @@
void test_coordinate_cast(std::string const& s, int expected_nom, int expected_denom)
{
boost::rational<int> a = bg::detail::coordinate_cast<boost::rational<int> >::apply(s);
boost::rational<int> const a = bg::detail::coordinate_cast<boost::rational<int> >::apply(s);
BOOST_CHECK_EQUAL(a.numerator(), expected_nom);
BOOST_CHECK_EQUAL(a.denominator(), expected_denom);
}
void test_numeric_cast()
{
const boost::rational<int> r1(3, 4);
boost::rational<int> const r1(3, 4);
BOOST_CHECK_CLOSE(bg::util::numeric_cast<double>(r1), 0.75, 0.00001);
const boost::rational<int> r2(10, 4);
boost::rational<int> const r2(10, 4);
BOOST_CHECK_CLOSE(bg::util::numeric_cast<double>(r2), 2.5, 0.00001);
BOOST_CHECK_EQUAL(bg::util::numeric_cast<int>(r2), 2);
}
template <typename T>
void test_bounds()
{
using coordinate_t = boost::rational<T>;
using point_t = bg::model::point<coordinate_t, 2, bg::cs::cartesian>;
auto const lowest = bg::util::bounds<coordinate_t>::lowest();
auto const highest = bg::util::bounds<coordinate_t>::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 <typename T>
void test_box()
{
using coordinate_t = boost::rational<T>;
using point_t = bg::model::point<coordinate_t, 2, bg::cs::cartesian>;
using box_t = bg::model::box<point_t>;
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<point_t>(4, 4));
bg::expand(box, bg::make<point_t>(8, 8));
// Test within (without specialization, both points are within the box)
auto const point1 = bg::make<point_t>(6, 6);
auto const point2 = bg::make<point_t>(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<T>(bg::area(box));
T const expected_area = 16;
BOOST_CHECK_EQUAL(expected_area, area);
}
void test_select_most_precise()
{
using rational1_t = boost::rational<std::int32_t>;
using rational2_t = boost::rational<std::int64_t>;
using t1 = bg::select_most_precise<double, rational1_t>::type;
using t2 = bg::select_most_precise<double, rational2_t>::type;
using t12 = bg::select_most_precise<rational1_t, rational2_t>::type;
BOOST_CHECK((std::is_same<t1, rational1_t>::value));
BOOST_CHECK((std::is_same<t2, rational2_t>::value));
BOOST_CHECK((std::is_same<t12, rational2_t>::value));
}
void test_wkt(std::string const& wkt, std::string const expected_wkt)
{
bg::model::point<boost::rational<int>, 2, bg::cs::cartesian> p;
@ -64,6 +141,14 @@ int test_main(int, char* [])
test_numeric_cast();
test_bounds<std::int16_t>();
test_bounds<std::int32_t>();
test_bounds<std::int64_t>();
test_box<std::int64_t>();
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)");