2025-01-12 20:40:08 +08:00

400 lines
14 KiB
C++

// 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 <sstream>
#include <string>
#include <type_traits>
#include <boost/algorithm/string.hpp>
#include <geometry_test_common.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/algorithms/area.hpp>
#include <boost/geometry/algorithms/make.hpp>
#include <boost/geometry/algorithms/length.hpp>
#include <boost/geometry/algorithms/num_points.hpp>
#include <boost/geometry/algorithms/perimeter.hpp>
#include <boost/geometry/strategies/strategies.hpp>
#include <boost/geometry/core/point_type.hpp>
#include <boost/geometry/core/topological_dimension.hpp>
#include <boost/geometry/io/wkt/read.hpp>
#include <boost/geometry/io/wkt/write.hpp>
#include <boost/variant/variant.hpp>
template <typename G>
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 <typename G>
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 <typename G>
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 <typename G>
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<G>::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<G> v;
bg::read_wkt(wkt, v);
check_wkt(v, expected);
bg::model::geometry_collection<boost::variant<G>> gc1{v};
bg::read_wkt(std::string("GEOMETRYCOLLECTION(") + wkt + ')', gc1);
check_wkt(gc1, std::string("GEOMETRYCOLLECTION(") + expected + ')');
bg::model::geometry_collection<boost::variant<G>> gc2{v, v};
bg::read_wkt(std::string("GEOMETRYCOLLECTION(") + wkt + ',' + wkt + ')', gc2);
check_wkt(gc2, std::string("GEOMETRYCOLLECTION(") + expected + ',' + expected + ')');
}
template <typename G>
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<G>(wkt);
#ifdef BOOST_GEOMETRY_TEST_DEBUG
std::cout << "n=" << bg::num_points(geometry)
<< " dim=" << bg::topological_dimension<G>::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<G>(geometry), expected);
}
template <typename G>
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<G>(wkt, expected, n, len, ar, peri);
test_wkt_to_from<G>(wkt, expected, n, len, ar, peri);
}
template <typename G>
void test_wkt(std::string const& wkt,
std::size_t n, double len = 0, double ar = 0, double peri = 0)
{
test_wkt<G>(wkt, wkt, n, len, ar, peri);
}
template <typename G>
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 <typename G>
void test_relaxed_wkt_to_from(std::string const& wkt, std::string const& expected)
{
G geometry;
geometry = bg::from_wkt<G>(wkt);
std::string const out = bg::to_wkt(geometry);
BOOST_CHECK_EQUAL(boost::to_upper_copy(out), boost::to_upper_copy(expected));
}
template <typename G>
void test_relaxed_wkt(std::string const& wkt, std::string const& expected)
{
test_relaxed_wkt_read_write<G>(wkt, expected);
test_relaxed_wkt_to_from<G>(wkt, expected);
}
template <typename G>
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<G>(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<G>::type ct;
if (boost::is_same<ct, ttmath_big>::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 <typename G>
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<G>(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 <typename G>
void test_wrong_wkt(std::string const& wkt, std::string const& start)
{
test_wrong_wkt_read_write<G>(wkt, start);
test_wrong_wkt_to_from<G>(wkt, start);
}
template <typename G>
void test_wkt_output_iterator(std::string const& wkt)
{
G geometry;
bg::read_wkt<G>(wkt, std::back_inserter(geometry));
}
void test_precise_to_wkt()
{
typedef boost::geometry::model::d2::point_xy<double> point_type;
point_type point = boost::geometry::make<point_type>(1.2345, 6.7890);
boost::geometry::model::polygon<point_type> polygon;
boost::geometry::append(boost::geometry::exterior_ring(polygon), boost::geometry::make<point_type>(0.00000, 0.00000));
boost::geometry::append(boost::geometry::exterior_ring(polygon), boost::geometry::make<point_type>(0.00000, 4.00001));
boost::geometry::append(boost::geometry::exterior_ring(polygon), boost::geometry::make<point_type>(4.00001, 4.00001));
boost::geometry::append(boost::geometry::exterior_ring(polygon), boost::geometry::make<point_type>(4.00001, 0.00000));
boost::geometry::append(boost::geometry::exterior_ring(polygon), boost::geometry::make<point_type>(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 <typename T>
void test_order_closure()
{
using namespace boost::geometry;
typedef bg::model::point<T, 2, bg::cs::cartesian> Pt;
typedef bg::model::polygon<Pt, true, true> PCWC;
typedef bg::model::polygon<Pt, true, false> PCWO;
typedef bg::model::polygon<Pt, false, true> PCCWC;
typedef bg::model::polygon<Pt, false, false> 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<PCWC>(wkt_cwc, 5, 0, 4, 8);
test_wkt<PCWO>(wkt_cwc, 4, 0, 4, 8);
test_wkt<PCWO>(wkt_cwo, wkt_cwc, 4, 0, 4, 8);
test_wkt<PCCWC>(wkt_ccwc, 5, 0, 4, 8);
test_wkt<PCCWO>(wkt_ccwc, 4, 0, 4, 8);
test_wkt<PCCWO>(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<PCWC>(wkt_cwc, 10, 0, 8, 16);
test_wkt<PCWO>(wkt_cwc, 8, 0, 8, 16);
test_wkt<PCWO>(wkt_cwo, wkt_cwc, 8, 0, 8, 16);
test_wkt<PCCWC>(wkt_ccwc, 10, 0, 8, 16);
test_wkt<PCCWO>(wkt_ccwc, 8, 0, 8, 16);
test_wkt<PCCWO>(wkt_ccwo, wkt_ccwc, 8, 0, 8, 16);
}
}
template <typename T>
void test_all()
{
using namespace boost::geometry;
typedef bg::model::point<T, 2, bg::cs::cartesian> P;
test_wkt<P>("POINT(1 2)", 1);
test_wkt<bg::model::linestring<P> >("LINESTRING(1 1,2 2,3 3)", 3, 2 * sqrt(2.0));
test_wkt<bg::model::polygon<P> >("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<box<P> >("POLYGON((0 0,0 1,1 1,1 0,0 0))", 4, 0, 1, 4);
test_wkt<bg::model::ring<P> >("POLYGON((0 0,0 1,1 1,1 0,0 0))", 5, 0, 1, 4);
test_relaxed_wkt<bg::model::linestring<P> >("LINESTRING EMPTY", "LINESTRING()");
test_relaxed_wkt<bg::model::polygon<P> >("POLYGON EMPTY", "POLYGON()");
test_relaxed_wkt<bg::model::ring<P> >("POLYGON EMPTY", "POLYGON()");
// Accept empty sequences as well
test_relaxed_wkt<bg::model::linestring<P> >("LINESTRING()", "LINESTRING()");
test_relaxed_wkt<bg::model::polygon<P> >("POLYGON()", "POLYGON()");
test_relaxed_wkt<bg::model::polygon<P> >("POLYGON(())", "POLYGON()");
test_relaxed_wkt<bg::model::polygon<P> >("POLYGON((),(),())", "POLYGON()");
// Invalid polygon with an inner ring coordinate is outputted as such
test_relaxed_wkt<bg::model::polygon<P> >("POLYGON((),(),(1 2))", "POLYGON((),(),(1 2))");
// Non OGC: tabs and returns are allowed and handled as normal white space.
test_relaxed_wkt<P>("POINT(1\n2)", "POINT(1 2)");
test_relaxed_wkt<P>("POINT(1\t2)", "POINT(1 2)");
test_relaxed_wkt<P>("POINT(1\r2)", "POINT(1 2)");
// These WKT's are incomplete or abnormal but they are considered OK
test_relaxed_wkt<P>("POINT(1)", "POINT(1 0)");
test_relaxed_wkt<P>("POINT()", "POINT(0 0)");
test_relaxed_wkt<bg::model::linestring<P> >("LINESTRING(1,2,3)", "LINESTRING(1 0,2 0,3 0)");
test_relaxed_wkt<P>("POINT ( 1 2) ", "POINT(1 2)");
test_relaxed_wkt<P>("POINT M ( 1 2)", "POINT(1 2)");
test_relaxed_wkt<bg::model::box<P> >("BOX(1 1,2 2)", "POLYGON((1 1,1 2,2 2,2 1,1 1))");
test_relaxed_wkt<bg::model::polygon<P> >("POLYGON( ( ) , ( ) , ( ) )", "POLYGON()");
// Wrong WKT's
test_wrong_wkt<P>("POINT(1 2", "expected ')'");
test_wrong_wkt<P>("POINT 1 2)", "expected '('");
test_wrong_wkt<P>("POINT(1 2,)", "expected ')'");
test_wrong_wkt<P>("POINT(1 2)foo", "too many tokens at 'foo'");
test_wrong_wkt<P>("POINT(1 2 3)", "expected ')'");
test_wrong_wkt<P>("POINT(a 2 3)", "bad lexical cast");
test_wrong_wkt<P>("POINT 2 3", "expected '('");
test_wrong_wkt<P>("POINT Z (1 2 3)", "z only allowed");
test_wrong_wkt<P>("PIONT (1 2)", "should start with 'point'");
test_wrong_wkt<bg::model::linestring<P> >("LINESTRING())", "too many tokens");
test_wrong_wkt<bg::model::polygon<P> >("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<bg::model::box<P> >("BOX(1 1,2 2,3 3)", "box should have 2");
test_wrong_wkt<bg::model::box<P> >("BOX(1 1,2 2) )", "too many tokens");
if ( BOOST_GEOMETRY_CONDITION(std::is_floating_point<T>::value
|| ! std::is_fundamental<T>::value ) )
{
test_wkt<P>("POINT(1.1 2.1)", 1);
}
// Deprecated:
// test_wkt_output_iterator<bg::model::linestring<P> >("LINESTRING(1 1,2 2,3 3)");
// test_wkt_output_iterator<bg::model::ring<P> >("POLYGON((1 1,2 2,3 3))");
test_order_closure<T>();
}
#endif
int test_main(int, char* [])
{
test_all<double>();
test_all<int>();
test_precise_to_wkt();
#if defined(HAVE_TTMATH)
test_all<ttmath_big>();
#endif
return 0;
}