1 | // Boost.Geometry (aka GGL, Generic Geometry Library) |
2 | // Unit Test |
3 | |
4 | // Copyright (c) 2007-2024 Barend Gehrels, Amsterdam, the Netherlands. |
5 | // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. |
6 | // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. |
7 | |
8 | // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library |
9 | // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. |
10 | |
11 | // Use, modification and distribution is subject to the Boost Software License, |
12 | // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at |
13 | // http://www.boost.org/LICENSE_1_0.txt) |
14 | |
15 | |
16 | #include <geometry_test_common.hpp> |
17 | |
18 | #include <boost/geometry/algorithms/area.hpp> |
19 | #include <boost/geometry/algorithms/assign.hpp> |
20 | #include <boost/geometry/algorithms/make.hpp> |
21 | #include <boost/geometry/algorithms/expand.hpp> |
22 | #include <boost/geometry/algorithms/within.hpp> |
23 | #include <boost/geometry/geometries/geometries.hpp> |
24 | #include <boost/geometry/io/wkt/wkt.hpp> |
25 | #include <boost/geometry/util/numeric_cast.hpp> |
26 | #include <boost/geometry/util/rational.hpp> |
27 | |
28 | void test_coordinate_cast(std::string const& s, int expected_nom, int expected_denom) |
29 | { |
30 | boost::rational<int> const a = bg::detail::coordinate_cast<boost::rational<int> >::apply(source: s); |
31 | BOOST_CHECK_EQUAL(a.numerator(), expected_nom); |
32 | BOOST_CHECK_EQUAL(a.denominator(), expected_denom); |
33 | } |
34 | |
35 | void test_numeric_cast() |
36 | { |
37 | boost::rational<int> const r1(3, 4); |
38 | BOOST_CHECK_CLOSE(bg::util::numeric_cast<double>(r1), 0.75, 0.00001); |
39 | |
40 | boost::rational<int> const r2(10, 4); |
41 | BOOST_CHECK_CLOSE(bg::util::numeric_cast<double>(r2), 2.5, 0.00001); |
42 | BOOST_CHECK_EQUAL(bg::util::numeric_cast<int>(r2), 2); |
43 | } |
44 | |
45 | template <typename T> |
46 | void test_bounds() |
47 | { |
48 | using coordinate_t = boost::rational<T>; |
49 | using point_t = bg::model::point<coordinate_t, 2, bg::cs::cartesian>; |
50 | |
51 | auto const lowest = bg::util::bounds<coordinate_t>::lowest(); |
52 | auto const highest = bg::util::bounds<coordinate_t>::highest(); |
53 | |
54 | BOOST_CHECK_MESSAGE(lowest < highest, |
55 | "Lowest should be smaller than highest, lowest: " << lowest << " highest: " << highest); |
56 | } |
57 | |
58 | // Tests box-related functionality, which depends on geometry::util::bounds |
59 | // specialization for Boost.Rational |
60 | template <typename T> |
61 | void test_box() |
62 | { |
63 | using coordinate_t = boost::rational<T>; |
64 | using point_t = bg::model::point<coordinate_t, 2, bg::cs::cartesian>; |
65 | using box_t = bg::model::box<point_t>; |
66 | |
67 | box_t box; |
68 | bg::assign_inverse(box); |
69 | |
70 | point_t south_west, north_east; |
71 | bg::detail::assign_point_from_index<0>(box, south_west); |
72 | bg::detail::assign_point_from_index<1>(box, north_east); |
73 | |
74 | BOOST_CHECK_MESSAGE(bg::get<0>(south_west) > bg::get<0>(north_east), |
75 | "Bounding box should be inversed. Now x-min: " << bg::get<0>(south_west) |
76 | << " x-max: " << bg::get<0>(north_east) |
77 | << " " << bg::wkt(box)); |
78 | |
79 | BOOST_CHECK_MESSAGE(bg::get<1>(south_west) > bg::get<1>(north_east), |
80 | "Bounding box should be inversed. Now y-min: " << bg::get<1>(south_west) |
81 | << " y-max: " << bg::get<1>(north_east) |
82 | << " " << bg::wkt(box)); |
83 | |
84 | // Test specifically for points larger than 0, because without specialization Boost.Rational |
85 | // will return (0,1) (== 0) by default and code will compile but give wrong results. |
86 | bg::expand(box, bg::make<point_t>(4, 4)); |
87 | bg::expand(box, bg::make<point_t>(8, 8)); |
88 | |
89 | // Test within (without specialization, both points are within the box) |
90 | auto const point1 = bg::make<point_t>(6, 6); |
91 | auto const point2 = bg::make<point_t>(2, 2); |
92 | BOOST_CHECK_MESSAGE(bg::within(point1, box), |
93 | "Point " << bg::wkt(point1) << " is not within the box " << bg::wkt(box)); |
94 | BOOST_CHECK_MESSAGE(! bg::within(point2, box), |
95 | "Point " << bg::wkt(point2) << " is within the box " << bg::wkt(box)); |
96 | |
97 | // Test area (without specialization, it will be 64) |
98 | auto const area = bg::util::numeric_cast<T>(bg::area(box)); |
99 | T const expected_area = 16; |
100 | BOOST_CHECK_EQUAL(expected_area, area); |
101 | } |
102 | |
103 | void test_select_most_precise() |
104 | { |
105 | using rational1_t = boost::rational<std::int32_t>; |
106 | using rational2_t = boost::rational<std::int64_t>; |
107 | |
108 | using t1 = bg::select_most_precise<double, rational1_t>::type; |
109 | using t2 = bg::select_most_precise<double, rational2_t>::type; |
110 | using t12 = bg::select_most_precise<rational1_t, rational2_t>::type; |
111 | |
112 | BOOST_CHECK((std::is_same<t1, rational1_t>::value)); |
113 | BOOST_CHECK((std::is_same<t2, rational2_t>::value)); |
114 | BOOST_CHECK((std::is_same<t12, rational2_t>::value)); |
115 | } |
116 | |
117 | void test_wkt(std::string const& wkt, std::string const expected_wkt) |
118 | { |
119 | bg::model::point<boost::rational<int>, 2, bg::cs::cartesian> p; |
120 | bg::read_wkt(wkt, geometry&: p); |
121 | std::ostringstream out; |
122 | out << bg::wkt(geometry: p); |
123 | |
124 | BOOST_CHECK_EQUAL(out.str(), expected_wkt); |
125 | } |
126 | |
127 | int test_main(int, char* []) |
128 | { |
129 | test_coordinate_cast(s: "0" , expected_nom: 0, expected_denom: 1); |
130 | test_coordinate_cast(s: "1" , expected_nom: 1, expected_denom: 1); |
131 | test_coordinate_cast(s: "-1" , expected_nom: -1, expected_denom: 1); |
132 | test_coordinate_cast(s: "-0.5" , expected_nom: -1, expected_denom: 2); |
133 | test_coordinate_cast(s: "-1.5" , expected_nom: -3, expected_denom: 2); |
134 | test_coordinate_cast(s: "0.5" , expected_nom: 1, expected_denom: 2); |
135 | test_coordinate_cast(s: "1.5" , expected_nom: 3, expected_denom: 2); |
136 | test_coordinate_cast(s: "2.12345" , expected_nom: 42469, expected_denom: 20000); |
137 | test_coordinate_cast(s: "1." , expected_nom: 1, expected_denom: 1); |
138 | |
139 | test_coordinate_cast(s: "3/2" , expected_nom: 3, expected_denom: 2); |
140 | test_coordinate_cast(s: "-3/2" , expected_nom: -3, expected_denom: 2); |
141 | |
142 | test_numeric_cast(); |
143 | |
144 | test_bounds<std::int16_t>(); |
145 | test_bounds<std::int32_t>(); |
146 | test_bounds<std::int64_t>(); |
147 | |
148 | test_box<std::int64_t>(); |
149 | |
150 | test_select_most_precise(); |
151 | |
152 | test_wkt(wkt: "POINT(1.5 2.75)" , expected_wkt: "POINT(3/2 11/4)" ); |
153 | test_wkt(wkt: "POINT(3/2 11/4)" , expected_wkt: "POINT(3/2 11/4)" ); |
154 | test_wkt(wkt: "POINT(-1.5 2.75)" , expected_wkt: "POINT(-3/2 11/4)" ); |
155 | test_wkt(wkt: "POINT(-3/2 11/4)" , expected_wkt: "POINT(-3/2 11/4)" ); |
156 | |
157 | return 0; |
158 | } |
159 | |