// Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test // Copyright (c) 2007-2022 Barend Gehrels, Amsterdam, the Netherlands. // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. // This file was modified by Oracle on 2014-2021. // Modifications copyright (c) 2014-2021 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. // 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include template void check_wkt(G const& geometry, std::string const& expected) { std::ostringstream out; out << bg::wkt(geometry); BOOST_CHECK_EQUAL(boost::to_upper_copy(out.str()), boost::to_upper_copy(expected)); } template void check_to_wkt(G const& geometry, std::string const& expected) { std::string const out = bg::to_wkt(geometry); BOOST_CHECK_EQUAL(boost::to_upper_copy(out), boost::to_upper_copy(expected)); } template void check_precise_to_wkt(G const& geometry, std::string const& expected, int significant_digits) { std::string out_string; out_string = bg::to_wkt(geometry, significant_digits); BOOST_CHECK_EQUAL(boost::to_upper_copy(out_string), boost::to_upper_copy(expected)); } template void test_wkt_read_write(std::string const& wkt, std::string const& expected, std::size_t n, double len = 0, double ar = 0, double peri = 0) { G geometry; bg::read_wkt(wkt, geometry); #ifdef BOOST_GEOMETRY_TEST_DEBUG std::cout << "n=" << bg::num_points(geometry) << " dim=" << bg::topological_dimension::value << " length=" << bg::length(geometry) << " area=" << bg::area(geometry) << " perimeter=" << bg::perimeter(geometry) << std::endl << "\t\tgeometry=" << dsv(geometry) << std::endl; #endif BOOST_CHECK_EQUAL(bg::num_points(geometry), n); if (n > 0) { BOOST_CHECK_CLOSE(double(bg::length(geometry)), len, 0.0001); BOOST_CHECK_CLOSE(double(bg::area(geometry)), ar, 0.0001); BOOST_CHECK_CLOSE(double(bg::perimeter(geometry)), peri, 0.0001); } check_wkt(geometry, expected); boost::variant v; bg::read_wkt(wkt, v); check_wkt(v, expected); bg::model::geometry_collection> gc1{v}; bg::read_wkt(std::string("GEOMETRYCOLLECTION(") + wkt + ')', gc1); check_wkt(gc1, std::string("GEOMETRYCOLLECTION(") + expected + ')'); bg::model::geometry_collection> gc2{v, v}; bg::read_wkt(std::string("GEOMETRYCOLLECTION(") + wkt + ',' + wkt + ')', gc2); check_wkt(gc2, std::string("GEOMETRYCOLLECTION(") + expected + ',' + expected + ')'); } template void test_wkt_to_from(std::string const& wkt, std::string const& expected, std::size_t n, double len = 0, double ar = 0, double peri = 0) { G geometry; geometry = bg::from_wkt(wkt); #ifdef BOOST_GEOMETRY_TEST_DEBUG std::cout << "n=" << bg::num_points(geometry) << " dim=" << bg::topological_dimension::value << " length=" << bg::length(geometry) << " area=" << bg::area(geometry) << " perimeter=" << bg::perimeter(geometry) << std::endl << "\t\tgeometry=" << dsv(geometry) << std::endl; #endif BOOST_CHECK_EQUAL(bg::num_points(geometry), n); if (n > 0) { BOOST_CHECK_CLOSE(double(bg::length(geometry)), len, 0.0001); BOOST_CHECK_CLOSE(double(bg::area(geometry)), ar, 0.0001); BOOST_CHECK_CLOSE(double(bg::perimeter(geometry)), peri, 0.0001); } check_to_wkt(geometry, expected); check_to_wkt(boost::variant(geometry), expected); } template void test_wkt(std::string const& wkt, std::string const& expected, std::size_t n, double len = 0, double ar = 0, double peri = 0) { test_wkt_read_write(wkt, expected, n, len, ar, peri); test_wkt_to_from(wkt, expected, n, len, ar, peri); } template void test_wkt(std::string const& wkt, std::size_t n, double len = 0, double ar = 0, double peri = 0) { test_wkt(wkt, wkt, n, len, ar, peri); } template void test_relaxed_wkt_read_write(std::string const& wkt, std::string const& expected) { G geometry; bg::read_wkt(wkt, geometry); std::ostringstream out; out << bg::wkt(geometry); BOOST_CHECK_EQUAL(boost::to_upper_copy(out.str()), boost::to_upper_copy(expected)); } template void test_relaxed_wkt_to_from(std::string const& wkt, std::string const& expected) { G geometry; geometry = bg::from_wkt(wkt); std::string const out = bg::to_wkt(geometry); BOOST_CHECK_EQUAL(boost::to_upper_copy(out), boost::to_upper_copy(expected)); } template void test_relaxed_wkt(std::string const& wkt, std::string const& expected) { test_relaxed_wkt_read_write(wkt, expected); test_relaxed_wkt_to_from(wkt, expected); } template void test_wrong_wkt_read_write(std::string const& wkt, std::string const& start) { std::string e("no exception"); G geometry; try { bg::read_wkt(wkt, geometry); } catch(bg::read_wkt_exception const& ex) { e = ex.what(); boost::to_lower(e); } catch(...) { e = "other exception"; } bool check = true; #if defined(HAVE_TTMATH) // For ttmath we skip bad lexical casts typedef typename bg::coordinate_type::type ct; if (boost::is_same::type::value && boost::starts_with(start, "bad lexical cast")) { check = false; } #endif if (check) { BOOST_CHECK_MESSAGE(boost::starts_with(e, start), " Expected:" << start << " Got:" << e << " with WKT: " << wkt); } } template void test_wrong_wkt_to_from(std::string const& wkt, std::string const& start) { std::string e("no exception"); G geometry; try { geometry = bg::from_wkt(wkt); } catch(bg::read_wkt_exception const& ex) { e = ex.what(); boost::to_lower(e); } catch(...) { e = "other exception"; } BOOST_CHECK_MESSAGE(boost::starts_with(e, start), " Expected:" << start << " Got:" << e << " with WKT: " << wkt); } template void test_wrong_wkt(std::string const& wkt, std::string const& start) { test_wrong_wkt_read_write(wkt, start); test_wrong_wkt_to_from(wkt, start); } template void test_wkt_output_iterator(std::string const& wkt) { G geometry; bg::read_wkt(wkt, std::back_inserter(geometry)); } void test_precise_to_wkt() { typedef boost::geometry::model::d2::point_xy point_type; point_type point = boost::geometry::make(1.2345, 6.7890); boost::geometry::model::polygon polygon; boost::geometry::append(boost::geometry::exterior_ring(polygon), boost::geometry::make(0.00000, 0.00000)); boost::geometry::append(boost::geometry::exterior_ring(polygon), boost::geometry::make(0.00000, 4.00001)); boost::geometry::append(boost::geometry::exterior_ring(polygon), boost::geometry::make(4.00001, 4.00001)); boost::geometry::append(boost::geometry::exterior_ring(polygon), boost::geometry::make(4.00001, 0.00000)); boost::geometry::append(boost::geometry::exterior_ring(polygon), boost::geometry::make(0.00000, 0.00000)); check_precise_to_wkt(point,"POINT(1.23 6.79)",3); check_precise_to_wkt(polygon,"POLYGON((0 0,0 4,4 4,4 0,0 0))",3); } #ifndef GEOMETRY_TEST_MULTI template void test_order_closure() { using namespace boost::geometry; typedef bg::model::point Pt; typedef bg::model::polygon PCWC; typedef bg::model::polygon PCWO; typedef bg::model::polygon PCCWC; typedef bg::model::polygon PCCWO; { std::string wkt_cwc = "POLYGON((0 0,0 2,2 2,2 0,0 0))"; std::string wkt_cwo = "POLYGON((0 0,0 2,2 2,2 0))"; std::string wkt_ccwc = "POLYGON((0 0,2 0,2 2,0 2,0 0))"; std::string wkt_ccwo = "POLYGON((0 0,2 0,2 2,0 2))"; test_wkt(wkt_cwc, 5, 0, 4, 8); test_wkt(wkt_cwc, 4, 0, 4, 8); test_wkt(wkt_cwo, wkt_cwc, 4, 0, 4, 8); test_wkt(wkt_ccwc, 5, 0, 4, 8); test_wkt(wkt_ccwc, 4, 0, 4, 8); test_wkt(wkt_ccwo, wkt_ccwc, 4, 0, 4, 8); } { std::string wkt_cwc = "POLYGON((0 0,0 3,3 3,3 0,0 0),(1 1,2 1,2 2,1 2,1 1))"; std::string wkt_cwo = "POLYGON((0 0,0 3,3 3,3 0),(1 1,2 1,2 2,1 2))"; std::string wkt_ccwc = "POLYGON((0 0,3 0,3 3,0 3,0 0),(1 1,1 2,2 2,2 1,1 1))"; std::string wkt_ccwo = "POLYGON((0 0,3 0,3 3,0 3),(1 1,1 2,2 2,2 1,1 1))"; test_wkt(wkt_cwc, 10, 0, 8, 16); test_wkt(wkt_cwc, 8, 0, 8, 16); test_wkt(wkt_cwo, wkt_cwc, 8, 0, 8, 16); test_wkt(wkt_ccwc, 10, 0, 8, 16); test_wkt(wkt_ccwc, 8, 0, 8, 16); test_wkt(wkt_ccwo, wkt_ccwc, 8, 0, 8, 16); } } template void test_all() { using namespace boost::geometry; typedef bg::model::point P; test_wkt

("POINT(1 2)", 1); test_wkt >("LINESTRING(1 1,2 2,3 3)", 3, 2 * sqrt(2.0)); test_wkt >("POLYGON((0 0,0 4,4 4,4 0,0 0)" ",(1 1,1 2,2 2,2 1,1 1),(1 1,1 2,2 2,2 1,1 1))", 15, 0, 18, 24); // Non OGC: a box defined by a polygon //test_wkt >("POLYGON((0 0,0 1,1 1,1 0,0 0))", 4, 0, 1, 4); test_wkt >("POLYGON((0 0,0 1,1 1,1 0,0 0))", 5, 0, 1, 4); test_relaxed_wkt >("LINESTRING EMPTY", "LINESTRING()"); test_relaxed_wkt >("POLYGON EMPTY", "POLYGON()"); test_relaxed_wkt >("POLYGON EMPTY", "POLYGON()"); // Accept empty sequences as well test_relaxed_wkt >("LINESTRING()", "LINESTRING()"); test_relaxed_wkt >("POLYGON()", "POLYGON()"); test_relaxed_wkt >("POLYGON(())", "POLYGON()"); test_relaxed_wkt >("POLYGON((),(),())", "POLYGON()"); // Invalid polygon with an inner ring coordinate is outputted as such test_relaxed_wkt >("POLYGON((),(),(1 2))", "POLYGON((),(),(1 2))"); // Non OGC: tabs and returns are allowed and handled as normal white space. test_relaxed_wkt

("POINT(1\n2)", "POINT(1 2)"); test_relaxed_wkt

("POINT(1\t2)", "POINT(1 2)"); test_relaxed_wkt

("POINT(1\r2)", "POINT(1 2)"); // These WKT's are incomplete or abnormal but they are considered OK test_relaxed_wkt

("POINT(1)", "POINT(1 0)"); test_relaxed_wkt

("POINT()", "POINT(0 0)"); test_relaxed_wkt >("LINESTRING(1,2,3)", "LINESTRING(1 0,2 0,3 0)"); test_relaxed_wkt

("POINT ( 1 2) ", "POINT(1 2)"); test_relaxed_wkt

("POINT M ( 1 2)", "POINT(1 2)"); test_relaxed_wkt >("BOX(1 1,2 2)", "POLYGON((1 1,1 2,2 2,2 1,1 1))"); test_relaxed_wkt >("POLYGON( ( ) , ( ) , ( ) )", "POLYGON()"); // Wrong WKT's test_wrong_wkt

("POINT(1 2", "expected ')'"); test_wrong_wkt

("POINT 1 2)", "expected '('"); test_wrong_wkt

("POINT(1 2,)", "expected ')'"); test_wrong_wkt

("POINT(1 2)foo", "too many tokens at 'foo'"); test_wrong_wkt

("POINT(1 2 3)", "expected ')'"); test_wrong_wkt

("POINT(a 2 3)", "bad lexical cast"); test_wrong_wkt

("POINT 2 3", "expected '('"); test_wrong_wkt

("POINT Z (1 2 3)", "z only allowed"); test_wrong_wkt

("PIONT (1 2)", "should start with 'point'"); test_wrong_wkt >("LINESTRING())", "too many tokens"); test_wrong_wkt >("POLYGON((1 1,1 4,4 4,4 1,1 1)" ",((2 2,2 3,3 3,3 2,2 2))", "bad lexical cast"); test_wrong_wkt >("BOX(1 1,2 2,3 3)", "box should have 2"); test_wrong_wkt >("BOX(1 1,2 2) )", "too many tokens"); if ( BOOST_GEOMETRY_CONDITION(std::is_floating_point::value || ! std::is_fundamental::value ) ) { test_wkt

("POINT(1.1 2.1)", 1); } // Deprecated: // test_wkt_output_iterator >("LINESTRING(1 1,2 2,3 3)"); // test_wkt_output_iterator >("POLYGON((1 1,2 2,3 3))"); test_order_closure(); } #endif int test_main(int, char* []) { test_all(); test_all(); test_precise_to_wkt(); #if defined(HAVE_TTMATH) test_all(); #endif return 0; }