From e82e199e009fea5eaf78f51af3125dbc97e49862 Mon Sep 17 00:00:00 2001 From: Menelaos Karavelas Date: Mon, 27 Oct 2014 22:11:55 +0200 Subject: [PATCH] [test][distance] add unit test for distance of pointlike-pointlike geometries in the spherical equatorial coordinate system --- test/algorithms/Jamfile.v2 | 1 + test/algorithms/distance_se_pl_pl.cpp | 229 ++++++++ test/algorithms/test_distance_se_common.hpp | 574 ++++++++++++++++++++ 3 files changed, 804 insertions(+) create mode 100644 test/algorithms/distance_se_pl_pl.cpp create mode 100644 test/algorithms/test_distance_se_common.hpp diff --git a/test/algorithms/Jamfile.v2 b/test/algorithms/Jamfile.v2 index 79b3d4ca9..45209b5ad 100644 --- a/test/algorithms/Jamfile.v2 +++ b/test/algorithms/Jamfile.v2 @@ -38,6 +38,7 @@ test-suite boost-geometry-algorithms [ run distance_pointlike_areal.cpp ] [ run distance_pointlike_linear.cpp ] [ run distance_pointlike_pointlike.cpp ] + [ run distance_se_pl_pl.cpp ] [ run envelope.cpp : : : msvc:/bigobj ] [ run equals.cpp : : : msvc:/bigobj ] [ run expand.cpp ] diff --git a/test/algorithms/distance_se_pl_pl.cpp b/test/algorithms/distance_se_pl_pl.cpp new file mode 100644 index 000000000..630d33794 --- /dev/null +++ b/test/algorithms/distance_se_pl_pl.cpp @@ -0,0 +1,229 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#include + +#ifndef BOOST_TEST_MODULE +#define BOOST_TEST_MODULE test_distance_spherical_equatorial_pl_pl +#endif + +#include + +#include +#include + +#include "test_distance_se_common.hpp" + + +typedef bg::cs::spherical_equatorial cs_type; +typedef bg::model::point point_type; +typedef bg::model::multi_point multi_point_type; + +namespace services = bg::strategy::distance::services; +typedef bg::default_distance_result::type return_type; + +typedef bg::strategy::distance::haversine point_point_strategy; + +//=========================================================================== + +inline bg::default_distance_result::type +distance_from_wkt(std::string const& wkt1, std::string const& wkt2) +{ + point_type p1, p2; + bg::read_wkt(wkt1, p1); + bg::read_wkt(wkt2, p2); + return bg::distance(p1, p2); +} + +inline bg::default_comparable_distance_result::type +comparable_distance_from_wkt(std::string const& wkt1, std::string const& wkt2) +{ + point_type p1, p2; + bg::read_wkt(wkt1, p1); + bg::read_wkt(wkt2, p2); + return bg::comparable_distance(p1, p2); +} + +//=========================================================================== + +template +void test_distance_point_point(Strategy const& strategy) +{ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << std::endl; + std::cout << "point/point distance tests" << std::endl; +#endif + typedef test_distance_of_geometries tester; + + tester::apply("POINT(10 10)", + "POINT(0 0)", + 0.24619691677893202, + 0.0150768448035229, + strategy); + tester::apply("POINT(10 10)", + "POINT(10 10)", + 0, 0, strategy); + + // antipodal points + tester::apply("POINT(0 10)", + "POINT(180 -10)", + 180.0 * bg::math::d2r, 1.0, strategy); +} + +//=========================================================================== + +template +void test_distance_point_multipoint(Strategy const& strategy) +{ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << std::endl; + std::cout << "point/multipoint distance tests" << std::endl; +#endif + typedef test_distance_of_geometries tester; + + tester::apply("POINT(10 10)", + "MULTIPOINT(10 10,20 10,20 20,10 20)", + 0, 0, strategy); + tester::apply("POINT(10 10)", + "MULTIPOINT(20 20,20 30,30 20,30 30)", + distance_from_wkt("POINT(10 10)", "POINT(20 20)"), + comparable_distance_from_wkt("POINT(10 10)", "POINT(20 20)"), + strategy); + tester::apply("POINT(3 0)", + "MULTIPOINT(20 20,20 40,40 20,40 40)", + distance_from_wkt("POINT(3 0)", "POINT(20 20)"), + comparable_distance_from_wkt("POINT(3 0)", "POINT(20 20)"), + strategy); + + // almost antipodal points + tester::apply("POINT(179 2)", + "MULTIPOINT(3 3,4 3,4 4,3 4)", + distance_from_wkt("POINT(179 2)", "POINT(4 4)"), + comparable_distance_from_wkt("POINT(179 2)", "POINT(4 4)"), + strategy); + + // minimum distance across the dateline + tester::apply("POINT(355 5)", + "MULTIPOINT(10 10,20 10,20 20,10 20)", + distance_from_wkt("POINT(355 5)", "POINT(10 10)"), + comparable_distance_from_wkt("POINT(355 5)", "POINT(10 10)"), + strategy); +} + +//=========================================================================== + +template +void test_distance_multipoint_multipoint(Strategy const& strategy) +{ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << std::endl; + std::cout << "multipoint/multipoint distance tests" << std::endl; +#endif + typedef test_distance_of_geometries + < + multi_point_type, multi_point_type + > tester; + + tester::apply("MULTIPOINT(10 10,11 10,10 11,11 11)", + "MULTIPOINT(11 11,12 11,12 12,11 12)", + 0, 0, strategy); + tester::apply("MULTIPOINT(10 10,11 10,10 11,11 11)", + "MULTIPOINT(12 12,12 13,13 12,13 13)", + distance_from_wkt("POINT(11 11)", "POINT(12 12)"), + comparable_distance_from_wkt("POINT(11 11)", "POINT(12 12)"), + strategy); + + // example with many points in each multi-point so that the r-tree + // does some splitting. + + tester::apply("MULTIPOINT(1 1,1 2,1 3,1 4,1 5,1 6,1 7,1 8,1 9,1 10,\ + 2 1,2 2,2 3,2 4,2 5,2 6,2 7,2 8,2 9,2 10,\ + 3 1,3 2,3 3,3 4,3 5,3 6,3 7,3 8,3 9,3 10,\ + 10 1,10 10)", + "MULTIPOINT(11 11,11 12,11 13,11 14,11 15,\ + 11 16,11 17,11 18,11 19,11 20,\ + 12 11,12 12,12 13,12 24,12 15,\ + 12 16,12 17,12 18,12 29,12 20,\ + 13 11,13 12,13 13,13 24,13 15,\ + 13 16,13 17,13 18,13 29,13 20,\ + 20 11,20 20)", + distance_from_wkt("POINT(10 10)", "POINT(11 11)"), + comparable_distance_from_wkt("POINT(10 10)", "POINT(11 11)"), + strategy); + +} + +//=========================================================================== + +template +void test_more_empty_input_pointlike_pointlike(Strategy const& strategy) +{ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << std::endl; + std::cout << "testing on empty inputs... " << std::flush; +#endif + bg::model::multi_point multipoint_empty; + + Point point = from_wkt("POINT(0 0)"); + + // 1st geometry is empty + test_empty_input(multipoint_empty, point, strategy); + + // 2nd geometry is empty + test_empty_input(point, multipoint_empty, strategy); + + // both geometries are empty + test_empty_input(multipoint_empty, multipoint_empty, strategy); + +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << "done!" << std::endl; +#endif +} + +//=========================================================================== + +BOOST_AUTO_TEST_CASE( test_all_point_point ) +{ + test_distance_point_point(point_point_strategy()); + test_distance_point_point(point_point_strategy(earth_radius_km)); + test_distance_point_point(point_point_strategy(earth_radius_miles)); +} + +BOOST_AUTO_TEST_CASE( test_all_point_multipoint ) +{ + test_distance_point_multipoint(point_point_strategy()); + test_distance_point_multipoint(point_point_strategy(earth_radius_km)); + test_distance_point_multipoint(point_point_strategy(earth_radius_miles)); +} + +BOOST_AUTO_TEST_CASE( test_all_multipoint_multipoint ) +{ + test_distance_multipoint_multipoint(point_point_strategy()); + test_distance_multipoint_multipoint(point_point_strategy(earth_radius_km)); + test_distance_multipoint_multipoint(point_point_strategy(earth_radius_miles)); +} + +BOOST_AUTO_TEST_CASE( test_all_empty_input_pointlike_pointlike ) +{ + test_more_empty_input_pointlike_pointlike + < + point_type + >(point_point_strategy()); + + test_more_empty_input_pointlike_pointlike + < + point_type + >(point_point_strategy(earth_radius_km)); + + test_more_empty_input_pointlike_pointlike + < + point_type + >(point_point_strategy(earth_radius_miles)); +} diff --git a/test/algorithms/test_distance_se_common.hpp b/test/algorithms/test_distance_se_common.hpp new file mode 100644 index 000000000..be6ffbc17 --- /dev/null +++ b/test/algorithms/test_distance_se_common.hpp @@ -0,0 +1,574 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_TEST_DISTANCE_SE_COMMON_HPP +#define BOOST_GEOMETRY_TEST_DISTANCE_SE_COMMON_HPP + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include "from_wkt.hpp" + +#include + + +#ifndef BOOST_GEOMETRY_TEST_DISTANCE_HPP + +namespace bg = ::boost::geometry; + +static const double earth_radius_km = 6371.0; +static const double earth_radius_miles = 3959.0; + +// function copied from BG's test_distance.hpp + +template +void test_empty_input(Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + try + { + bg::distance(geometry1, geometry2); + } + catch(bg::empty_input_exception const& ) + { + return; + } + BOOST_CHECK_MESSAGE(false, "A empty_input_exception should have been thrown" ); +} +#endif // BOOST_GEOMETRY_TEST_DISTANCE_HPP + + + +//======================================================================== + + + +template +< + typename PointLike1, + typename PointLike2, + typename Strategy, + typename Tag1 = typename bg::tag::type, + typename Tag2 = typename bg::tag::type +> +struct distance_brute_force +{}; + +template +< + typename PointLike1, + typename PointLike2, + typename Strategy +> +struct distance_brute_force +< + PointLike1, PointLike2, Strategy, + bg::point_tag, bg::point_tag +> +{ + typedef typename bg::distance_result + < + PointLike1, PointLike2, Strategy + >::type distance_type; + + static inline distance_type apply(PointLike1 const& p1, + PointLike2 const& p2, + Strategy const& strategy) + { + return bg::distance(p1, p2, strategy); + } +}; + +template +< + typename PointLike1, + typename PointLike2, + typename Strategy +> +struct distance_brute_force +< + PointLike1, PointLike2, Strategy, + bg::point_tag, bg::multi_point_tag +> +{ + typedef typename bg::distance_result + < + PointLike1, PointLike2, Strategy + >::type distance_type; + + static inline distance_type apply(PointLike1 const& p, + PointLike2 const& mp, + Strategy const& strategy) + { + typedef typename boost::range_iterator::type iterator; + + bool first = true; + distance_type d_min; + for (iterator it = boost::begin(mp); it != boost::end(mp); + ++it, first = false) + { + distance_type d = bg::distance(p, *it, strategy); + + if ( first || d < d_min ) + { + d_min = d; + } + } + return d_min; + } +}; + +template +< + typename PointLike1, + typename PointLike2, + typename Strategy +> +struct distance_brute_force +< + PointLike1, PointLike2, Strategy, + bg::multi_point_tag, bg::multi_point_tag +> +{ + typedef typename bg::distance_result + < + PointLike1, PointLike2, Strategy + >::type distance_type; + + static inline distance_type apply(PointLike1 const& mp1, + PointLike2 const& mp2, + Strategy const& strategy) + { + typedef typename boost::range_iterator + < + PointLike1 const + >::type iterator1; + + typedef typename boost::range_iterator + < + PointLike2 const + >::type iterator2; + + bool first = true; + distance_type d_min; + for (iterator1 it1 = boost::begin(mp1); it1 != boost::end(mp1); ++it1) + { + for (iterator2 it2 = boost::begin(mp2); it2 != boost::end(mp2); + ++it2, first = false) + { + distance_type d = bg::distance(*it1, *it2, strategy); + + if ( first || d < d_min ) + { + d_min = d; + } + } + } + return d_min; + } +}; + + + +//======================================================================== + + + +#ifdef BOOST_GEOMETRY_TEST_DEBUG +// pretty print geometry -- START +template +struct pretty_print_geometry_dispatch +{ + template + static inline Stream& apply(Geometry const& geometry, Stream& os) + { + os << bg::wkt(geometry); + return os; + } +}; + +template +struct pretty_print_geometry_dispatch +{ + template + static inline Stream& apply(Geometry const& geometry, Stream& os) + { + os << "SEGMENT" << bg::dsv(geometry); + return os; + } +}; + +template +struct pretty_print_geometry_dispatch +{ + template + static inline Stream& apply(Geometry const& geometry, Stream& os) + { + os << "BOX" << bg::dsv(geometry); + return os; + } +}; + + +template +struct pretty_print_geometry +{ + template + static inline Stream& apply(Geometry const& geometry, Stream& os) + { + return pretty_print_geometry_dispatch + < + Geometry, typename bg::tag::type + >::apply(geometry, os); + } +}; +// pretty print geometry -- END +#endif // BOOST_GEOMETRY_TEST_DEBUG + + +//======================================================================== + + +template +struct check_equal +{ + static inline void apply(T const& value1, T const& value2) + { + BOOST_CHECK( value1 == value2 ); + } +}; + +template <> +struct check_equal +{ + static inline void apply(double value1, double value2) + { + BOOST_CHECK_CLOSE( value1, value2, 0.0001 ); + } +}; + + +//======================================================================== + +template +< + typename Geometry1, typename Geometry2, + int id1 = bg::geometry_id::value, + int id2 = bg::geometry_id::value +> +struct test_distance_of_geometries + : public test_distance_of_geometries +{}; + + +template +struct test_distance_of_geometries +{ + template + < + typename DistanceType, + typename ComparableDistanceType, + typename Strategy + > + static inline + void apply(std::string const& wkt1, + std::string const& wkt2, + DistanceType const& expected_distance, + ComparableDistanceType const& expected_comparable_distance, + Strategy const& strategy, + bool test_reversed = true) + { + Geometry1 geometry1 = from_wkt(wkt1); + Geometry2 geometry2 = from_wkt(wkt2); + + apply(geometry1, geometry2, + expected_distance, expected_comparable_distance, + strategy, test_reversed); + } + + + template + < + typename DistanceType, + typename ComparableDistanceType, + typename Strategy + > + static inline + void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + DistanceType const& expected_distance, + ComparableDistanceType const& expected_comparable_distance, + Strategy const& strategy, + bool test_reversed = true) + { +#ifdef BOOST_GEOMETRY_TEST_DEBUG + typedef pretty_print_geometry PPG1; + typedef pretty_print_geometry PPG2; + PPG1::apply(geometry1, std::cout); + std::cout << " - "; + PPG2::apply(geometry2, std::cout); + std::cout << std::endl; +#endif + typedef typename bg::default_distance_result + < + Geometry1, Geometry2 + >::type default_distance_result; + + typedef typename bg::strategy::distance::services::return_type + < + Strategy, Geometry1, Geometry2 + >::type distance_result_from_strategy; + + static const bool same_regular = boost::is_same + < + default_distance_result, + distance_result_from_strategy + >::type::value; + + BOOST_CHECK( same_regular ); + + + typedef typename bg::default_comparable_distance_result + < + Geometry1, Geometry2 + >::type default_comparable_distance_result; + + typedef typename bg::strategy::distance::services::return_type + < + typename bg::strategy::distance::services::comparable_type + < + Strategy + >::type, + Geometry1, + Geometry2 + >::type comparable_distance_result_from_strategy; + + static const bool same_comparable = boost::is_same + < + default_comparable_distance_result, + comparable_distance_result_from_strategy + >::type::value; + + BOOST_CHECK( same_comparable ); + + + // check distance with default strategy + default_distance_result dist_def = bg::distance(geometry1, geometry2); + + check_equal + < + default_distance_result + >::apply(dist_def, expected_distance); + + + // check distance with passed strategy + distance_result_from_strategy dist = + bg::distance(geometry1, geometry2, strategy); + + check_equal + < + default_distance_result + >::apply(dist, expected_distance * strategy.radius()); + + // check against the comparable distance computed in a + // brute-force manner + default_distance_result dist_brute_force = distance_brute_force + < + Geometry1, Geometry2, Strategy + >::apply(geometry1, geometry2, strategy); + + check_equal + < + default_distance_result + >::apply(dist_brute_force, expected_distance * strategy.radius()); + + + // check comparable distance with default strategy + default_comparable_distance_result cdist_def = + bg::comparable_distance(geometry1, geometry2); + + check_equal + < + default_comparable_distance_result + >::apply(cdist_def, expected_comparable_distance); + + + // check comparable distance with passed strategy + comparable_distance_result_from_strategy cdist = + bg::comparable_distance(geometry1, geometry2, strategy); + + check_equal + < + default_comparable_distance_result + >::apply(cdist, expected_comparable_distance); + + // check against the comparable distance computed in a + // brute-force manner + default_comparable_distance_result cdist_brute_force + = distance_brute_force + < + Geometry1, + Geometry2, + typename bg::strategy::distance::services::comparable_type + < + Strategy + >::type + >::apply(geometry1, + geometry2, + bg::strategy::distance::services::get_comparable + < + Strategy + >::apply(strategy)); + + check_equal + < + default_comparable_distance_result + >::apply(cdist_brute_force, expected_comparable_distance); + +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << string_from_type::type>::name() + << string_from_type::type>::name() + << " -> " + << string_from_type::name() + << string_from_type::name() + << std::endl; + std::cout << "expected distance (default strategy) = " + << expected_distance << " ; " + << "expected distance (passed strategy) = " + << (expected_distance * strategy.radius()) << " ; " + << "expected comp. distance = " + << expected_comparable_distance + << std::endl; + std::cout << "distance (default strategy) = " << dist_def << " ; " + << "distance (passed strategy) = " << dist << " ; " + << "comp. distance (default strategy) = " + << cdist_def << " ; " + << "comp. distance (passed strategy) = " + << cdist << std::endl; + + if ( !test_reversed ) + { + std::cout << std::endl; + } +#endif + + if ( test_reversed ) + { + // check distance with default strategy + dist_def = bg::distance(geometry2, geometry1); + + check_equal + < + default_distance_result + >::apply(dist_def, expected_distance); + + + // check distance with given strategy + dist = bg::distance(geometry2, geometry1, strategy); + + check_equal + < + default_distance_result + >::apply(dist, expected_distance * strategy.radius()); + + + // check comparable distance with default strategy + cdist_def = bg::comparable_distance(geometry2, geometry1); + + check_equal + < + default_comparable_distance_result + >::apply(cdist_def, expected_comparable_distance); + + // check comparable distance with given strategy + cdist = bg::comparable_distance(geometry2, geometry1, strategy); + + check_equal + < + default_comparable_distance_result + >::apply(cdist, expected_comparable_distance); + +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << "expected distance (default strategy) = " + << expected_distance << " ; " + << "expected distance (passed strategy) = " + << (expected_distance * strategy.radius()) << " ; " + << "expected comp. distance = " + << expected_comparable_distance + << std::endl; + std::cout << "distance[reversed args] (def. startegy) = " + << dist_def << " ; " + << "distance[reversed args] (passed startegy) = " + << dist << " ; " + << "comp. distance[reversed args] (def. strategy) = " + << cdist_def << " ; " + << "comp. distance[reversed args] (passed strategy) = " + << cdist << std::endl; + std::cout << std::endl; +#endif + } + } +}; + + +//======================================================================== + + +template +void test_empty_input(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) +{ + try + { + bg::distance(geometry1, geometry2, strategy); + } + catch(bg::empty_input_exception const& ) + { + return; + } + BOOST_CHECK_MESSAGE(false, "A empty_input_exception should have been thrown" ); + + try + { + bg::distance(geometry2, geometry1, strategy); + } + catch(bg::empty_input_exception const& ) + { + return; + } + BOOST_CHECK_MESSAGE(false, "A empty_input_exception should have been thrown" ); +} + +#endif // BOOST_GEOMETRY_TEST_DISTANCE_SE_COMMON_HPP