| 1 | // Boost.Geometry (aka GGL, Generic Geometry Library) |
| 2 | // Unit Test |
| 3 | |
| 4 | // Copyright (c) 2007-2012 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 | // This file was modified by Oracle on 2015-2021. |
| 9 | // Modifications copyright (c) 2015-2021, Oracle and/or its affiliates. |
| 10 | |
| 11 | // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle |
| 12 | // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle |
| 13 | |
| 14 | // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library |
| 15 | // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. |
| 16 | |
| 17 | // Use, modification and distribution is subject to the Boost Software License, |
| 18 | // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at |
| 19 | // http://www.boost.org/LICENSE_1_0.txt) |
| 20 | |
| 21 | |
| 22 | #include <algorithms/area/test_area.hpp> |
| 23 | |
| 24 | #include <boost/geometry/geometries/box.hpp> |
| 25 | #include <boost/geometry/geometries/geometry_collection.hpp> |
| 26 | #include <boost/geometry/geometries/point_xy.hpp> |
| 27 | #include <boost/geometry/geometries/point.hpp> |
| 28 | #include <boost/geometry/geometries/polygon.hpp> |
| 29 | #include <boost/geometry/geometries/ring.hpp> |
| 30 | #include <boost/geometry/geometries/adapted/boost_variant.hpp> |
| 31 | #include <boost/geometry/geometries/adapted/boost_variant2.hpp> |
| 32 | |
| 33 | #include <boost/geometry/strategy/cartesian/precise_area.hpp> |
| 34 | |
| 35 | #include <test_geometries/all_custom_ring.hpp> |
| 36 | #include <test_geometries/all_custom_polygon.hpp> |
| 37 | //#define BOOST_GEOMETRY_TEST_DEBUG |
| 38 | |
| 39 | #include <boost/multiprecision/cpp_dec_float.hpp> |
| 40 | |
| 41 | template <typename Polygon> |
| 42 | void test_polygon() |
| 43 | { |
| 44 | // Rotated square, length=sqrt(2) -> area=2 |
| 45 | test_geometry<Polygon>("POLYGON((1 1,2 2,3 1,2 0,1 1))" , 2.0); |
| 46 | test_geometry<Polygon>("POLYGON((1 1,2 2,3 1,2 0,1 1))" , 2.0); |
| 47 | test_geometry<Polygon>("POLYGON((0 0,0 7,4 2,2 0,0 0))" , 16.0); |
| 48 | test_geometry<Polygon>("POLYGON((1 1,2 1,2 2,1 2,1 1))" , -1.0); |
| 49 | test_geometry<Polygon>("POLYGON((0 0,0 7,4 2,2 0,0 0), (1 1,2 1,2 2,1 2,1 1))" , 15.0); |
| 50 | } |
| 51 | |
| 52 | |
| 53 | template <typename P> |
| 54 | void test_all() |
| 55 | { |
| 56 | test_geometry<bg::model::box<P> >("POLYGON((0 0,2 2))" , 4.0); |
| 57 | test_geometry<bg::model::box<P> >("POLYGON((2 2,0 0))" , 4.0); |
| 58 | |
| 59 | test_polygon<bg::model::polygon<P> >(); |
| 60 | test_polygon<all_custom_polygon<P> >(); |
| 61 | |
| 62 | // clockwise rings (second is wrongly ordered) |
| 63 | test_geometry<bg::model::ring<P> >("POLYGON((0 0,0 7,4 2,2 0,0 0))" , 16.0); |
| 64 | test_geometry<bg::model::ring<P> >("POLYGON((0 0,2 0,4 2,0 7,0 0))" , -16.0); |
| 65 | |
| 66 | test_geometry<all_custom_ring<P> >("POLYGON((0 0,0 7,4 2,2 0,0 0))" , 16.0); |
| 67 | |
| 68 | // ccw |
| 69 | test_geometry<bg::model::polygon<P, false> > |
| 70 | ("POLYGON((0 0,0 7,4 2,2 0,0 0), (1 1,2 1,2 2,1 2,1 1))" , -15.0); |
| 71 | |
| 72 | test_geometry<bg::model::polygon<P, false> > |
| 73 | ("POLYGON((1 0,0 1,-1 0,0 -1,1 0))" , 2); |
| 74 | |
| 75 | typedef typename bg::coordinate_type<P>::type coord_type; |
| 76 | if (BOOST_GEOMETRY_CONDITION((std::is_same<coord_type, double>::value))) |
| 77 | { |
| 78 | test_geometry<bg::model::polygon<P, false, false> > |
| 79 | ("POLYGON((100000001 100000000, 100000000 100000001, \ |
| 80 | 99999999 100000000, 100000000 99999999))" , 2); |
| 81 | } |
| 82 | else if (BOOST_GEOMETRY_CONDITION((std::is_same<coord_type, float>::value))) |
| 83 | { |
| 84 | test_geometry<bg::model::polygon<P, false, false> > |
| 85 | ("POLYGON((100001 100000, 100000 100001, \ |
| 86 | 99999 100000, 100000 99999))" , 2); |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | template <typename P> |
| 91 | void test_ccw() |
| 92 | { |
| 93 | typedef typename bg::coordinate_type<P>::type ct; |
| 94 | bg::model::polygon<P, false> ccw_polygon; |
| 95 | // counterclockwise rings (second is wrongly ordered) |
| 96 | std::string poly1 = "POLYGON((1 1,2 2,3 1,2 0,1 1))" ; |
| 97 | std::string poly2 = "POLYGON((1 1,2 0,3 1,2 2,1 1))" ; |
| 98 | std::string poly3 = "POLYGON((0 0,0 7,4 2,2 0,0 0))" ; |
| 99 | std::string poly4 = "POLYGON((0 0,2 0,4 2,0 7,0 0))" ; |
| 100 | |
| 101 | bg::read_wkt(poly1, ccw_polygon); |
| 102 | ct area1 = bg::area(ccw_polygon); |
| 103 | bg::read_wkt(poly2, ccw_polygon); |
| 104 | ct area2 = bg::area(ccw_polygon); |
| 105 | bg::read_wkt(poly3, ccw_polygon); |
| 106 | ct area3 = bg::area(ccw_polygon); |
| 107 | bg::read_wkt(poly4, ccw_polygon); |
| 108 | ct area4 = bg::area(ccw_polygon); |
| 109 | BOOST_CHECK_CLOSE(area1, -1 * area2, 0.001); |
| 110 | BOOST_CHECK_CLOSE(area3, -1 * area4, 0.001); |
| 111 | } |
| 112 | |
| 113 | template <typename P, typename CT> |
| 114 | void test_open(CT expected_area) |
| 115 | { |
| 116 | typedef bg::model::polygon<P, true, false> open_polygon; |
| 117 | test_geometry<open_polygon>("POLYGON((1 1,2 2,3 1,2 0))" , expected_area); |
| 118 | // Note the triangular testcase used in CCW is not sensible for open/close |
| 119 | } |
| 120 | |
| 121 | template <typename P, typename CT> |
| 122 | void test_open_ccw(CT expected_area) |
| 123 | { |
| 124 | typedef bg::model::polygon<P, false, false> open_polygon; |
| 125 | test_geometry<open_polygon>("POLYGON((1 1,2 0,3 1,2 2))" , expected_area); |
| 126 | // Note the triangular testcase used in CCW is not sensible for open/close |
| 127 | } |
| 128 | |
| 129 | template <typename P> |
| 130 | void test_poles_ccw() |
| 131 | { |
| 132 | typedef typename bg::coordinate_type<P>::type ct; |
| 133 | bg::model::polygon<P, false> polygon; |
| 134 | |
| 135 | std::string poly1 = "POLYGON((45 45,45 95,95 45,45 45))" ; |
| 136 | std::string poly2 = "POLYGON((45 45,95 45,45 95,45 45))" ; |
| 137 | std::string poly3 = "POLYGON((45 -45,45 -95,95 -45,45 -45))" ; |
| 138 | std::string poly4 = "POLYGON((45 -45,95 -45,45 -95,45 -45))" ; |
| 139 | |
| 140 | bg::read_wkt(poly1, polygon); |
| 141 | ct area1 = bg::area(polygon); |
| 142 | bg::read_wkt(poly2, polygon); |
| 143 | ct area2 = bg::area(polygon); |
| 144 | bg::read_wkt(poly3, polygon); |
| 145 | ct area3 = bg::area(polygon); |
| 146 | bg::read_wkt(poly4, polygon); |
| 147 | ct area4 = bg::area(polygon); |
| 148 | BOOST_CHECK_CLOSE(area1, -1 * area2, 0.001); |
| 149 | BOOST_CHECK_CLOSE(area3, -1 * area4, 0.001); |
| 150 | } |
| 151 | |
| 152 | template <typename P> |
| 153 | void test_empty_input() |
| 154 | { |
| 155 | bg::model::polygon<P> poly_empty; |
| 156 | bg::model::ring<P> ring_empty; |
| 157 | |
| 158 | test_empty_input(poly_empty); |
| 159 | test_empty_input(ring_empty); |
| 160 | } |
| 161 | |
| 162 | void test_large_integers() |
| 163 | { |
| 164 | typedef bg::model::point<int, 2, bg::cs::cartesian> int_point_type; |
| 165 | typedef bg::model::point<double, 2, bg::cs::cartesian> double_point_type; |
| 166 | |
| 167 | bg::model::polygon<int_point_type> int_poly; |
| 168 | bg::model::polygon<double_point_type> double_poly; |
| 169 | |
| 170 | std::string const polygon_li = "POLYGON((1872000 528000,1872000 192000,\ |
| 171 | 1536119 192000,1536000 528000,1200000 528000,\ |
| 172 | 1200000 863880,1536000 863880,1872000 863880,\ |
| 173 | 1872000 528000))" ; |
| 174 | bg::read_wkt(wkt: polygon_li, geometry&: int_poly); |
| 175 | bg::read_wkt(wkt: polygon_li, geometry&: double_poly); |
| 176 | |
| 177 | double int_area = bg::area(geometry: int_poly); |
| 178 | double double_area = bg::area(geometry: double_poly); |
| 179 | |
| 180 | BOOST_CHECK_CLOSE(int_area, double_area, 0.0001); |
| 181 | } |
| 182 | |
| 183 | struct precise_cartesian : bg::strategies::detail::cartesian_base |
| 184 | { |
| 185 | template <typename Geometry> |
| 186 | static auto area(Geometry const&) |
| 187 | { |
| 188 | return bg::strategy::area::precise_cartesian<>(); |
| 189 | } |
| 190 | }; |
| 191 | |
| 192 | void test_accurate_sum_strategy() |
| 193 | { |
| 194 | typedef bg::model::point<double, 2, bg::cs::cartesian> point_type; |
| 195 | typedef bg::model::point |
| 196 | < |
| 197 | boost::multiprecision::cpp_dec_float_50, |
| 198 | 2, |
| 199 | bg::cs::cartesian |
| 200 | > mp_point_type; |
| 201 | |
| 202 | auto const poly0_string = "POLYGON((0 0,0 1,1 0,0 0))" ; |
| 203 | |
| 204 | bg::model::polygon<point_type> poly0; |
| 205 | bg::read_wkt(wkt: poly0_string, geometry&: poly0); |
| 206 | |
| 207 | BOOST_CHECK_CLOSE(bg::area(poly0), 0.5, 0.0001); |
| 208 | BOOST_CHECK_CLOSE(bg::area(poly0, precise_cartesian()), 0.5, 0.0001); |
| 209 | |
| 210 | bg::model::polygon<mp_point_type> mp_poly0; |
| 211 | bg::read_wkt(wkt: poly0_string, geometry&: mp_poly0); |
| 212 | |
| 213 | BOOST_CHECK_CLOSE(bg::area(mp_poly0), 0.5, 0.0001); |
| 214 | |
| 215 | auto const poly1_string = "POLYGON((0.10000000000000001 0.10000000000000001,\ |
| 216 | 0.20000000000000001 0.20000000000000004,\ |
| 217 | 0.79999999999999993 0.80000000000000004,\ |
| 218 | 1.267650600228229e30 1.2676506002282291e30,\ |
| 219 | 0.10000000000000001 0.10000000000000001))" ; |
| 220 | |
| 221 | bg::model::polygon<point_type> poly1; |
| 222 | bg::read_wkt(wkt: poly1_string, geometry&: poly1); |
| 223 | |
| 224 | BOOST_CHECK_CLOSE(bg::area(poly1), 0, 0.0001); |
| 225 | BOOST_CHECK_CLOSE(bg::area(poly1, precise_cartesian()), -0.315, 0.0001); |
| 226 | |
| 227 | bg::model::polygon<mp_point_type> mp_poly1; |
| 228 | bg::read_wkt(wkt: poly1_string, geometry&: mp_poly1); |
| 229 | |
| 230 | BOOST_CHECK_CLOSE(bg::area(mp_poly1), 34720783012552.6, 0.0001); |
| 231 | |
| 232 | auto const poly2_string = "POLYGON((1.267650600228229e30 1.2676506002282291e30,\ |
| 233 | 0.8 0.8,0.2 0.2,0.1 0.1,1.267650600228229e30 1.2676506002282291e30))" ; |
| 234 | |
| 235 | bg::model::polygon<point_type> poly2; |
| 236 | bg::read_wkt(wkt: poly2_string, geometry&: poly2); |
| 237 | |
| 238 | BOOST_CHECK_CLOSE(bg::area(poly2), 0, 0.0001); |
| 239 | BOOST_CHECK_CLOSE(bg::area(poly2, precise_cartesian()), 0.315, 0.0001); |
| 240 | |
| 241 | bg::model::polygon<mp_point_type> mp_poly2; |
| 242 | bg::read_wkt(wkt: poly2_string, geometry&: mp_poly2); |
| 243 | |
| 244 | BOOST_CHECK_CLOSE(bg::area(mp_poly2), 35000000000000, 0.0001); |
| 245 | } |
| 246 | |
| 247 | void test_dynamic() |
| 248 | { |
| 249 | typedef bg::model::point<double, 2, bg::cs::cartesian> double_point_type; |
| 250 | typedef bg::model::polygon<double_point_type> polygon_type; |
| 251 | typedef bg::model::box<double_point_type> box_type; |
| 252 | |
| 253 | polygon_type poly; |
| 254 | std::string const polygon_li = "POLYGON((18 5,18 1,15 1,15 5,12 5,12 8,15 8,18 8,18 5))" ; |
| 255 | bg::read_wkt(wkt: polygon_li, geometry&: poly); |
| 256 | |
| 257 | box_type box; |
| 258 | std::string const box_li = "BOX(0 0,2 2)" ; |
| 259 | bg::read_wkt(wkt: box_li, geometry&: box); |
| 260 | |
| 261 | auto apoly = bg::area(geometry: poly); |
| 262 | auto abox = bg::area(geometry: box); |
| 263 | |
| 264 | boost::variant<polygon_type, box_type> v; |
| 265 | v = poly; |
| 266 | BOOST_CHECK_CLOSE(bg::area(v), apoly, 0.0001); |
| 267 | v = box; |
| 268 | BOOST_CHECK_CLOSE(bg::area(v), abox, 0.0001); |
| 269 | |
| 270 | boost::variant2::variant<polygon_type, box_type> v2; |
| 271 | v2 = poly; |
| 272 | BOOST_CHECK_CLOSE(bg::area(v2), apoly, 0.0001); |
| 273 | v2 = box; |
| 274 | BOOST_CHECK_CLOSE(bg::area(v2), abox, 0.0001); |
| 275 | |
| 276 | bg::model::geometry_collection<boost::variant<polygon_type, box_type> > gc; |
| 277 | gc.push_back(x: poly); |
| 278 | gc.push_back(x: box); |
| 279 | BOOST_CHECK_CLOSE(bg::area(gc), apoly + abox, 0.0001); |
| 280 | } |
| 281 | |
| 282 | int test_main(int, char* []) |
| 283 | { |
| 284 | |
| 285 | test_all<bg::model::point<int, 2, bg::cs::cartesian> >(); |
| 286 | test_all<bg::model::point<float, 2, bg::cs::cartesian> >(); |
| 287 | test_all<bg::model::point<double, 2, bg::cs::cartesian> >(); |
| 288 | |
| 289 | typedef bg::model::point<double, 2, bg::cs::cartesian> pt_crt; |
| 290 | typedef bg::model::point<double, 2, bg::cs::spherical_equatorial<bg::degree> > pt_sph; |
| 291 | typedef bg::model::point<double, 2, bg::cs::geographic<bg::degree> > pt_geo; |
| 292 | |
| 293 | // mean Earth's radius^2 |
| 294 | double r2 = bg::math::sqr(value: bg::get_radius<0>(geometry: bg::srs::sphere<double>())); |
| 295 | |
| 296 | test_ccw<pt_crt>(); |
| 297 | test_ccw<pt_sph>(); |
| 298 | test_ccw<pt_geo>(); |
| 299 | |
| 300 | test_open<pt_crt>(expected_area: 2.0); |
| 301 | test_open<pt_sph>(expected_area: 24726179921.523518 / r2); |
| 302 | test_open<pt_geo >(expected_area: 24615770547.825359); |
| 303 | |
| 304 | test_open_ccw<pt_crt>(expected_area: 2.0); |
| 305 | test_open_ccw<pt_sph>(expected_area: 24726179921.523518 / r2); |
| 306 | test_open_ccw<pt_geo >(expected_area: 24615770547.825359); |
| 307 | |
| 308 | test_poles_ccw<pt_crt>(); |
| 309 | test_poles_ccw<pt_sph>(); |
| 310 | test_poles_ccw<pt_geo>(); |
| 311 | |
| 312 | test_large_integers(); |
| 313 | |
| 314 | test_dynamic(); |
| 315 | |
| 316 | test_accurate_sum_strategy(); |
| 317 | |
| 318 | // test_empty_input<bg::model::d2::point_xy<int> >(); |
| 319 | |
| 320 | return 0; |
| 321 | } |
| 322 | |