400 lines
14 KiB
C++
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;
|
|
}
|