geometry/test/algorithms/detail/partition.cpp
Barend Gehrels 43ba70c8e1 [partition][buffer] add extra policy for inclusion. This gives opportunity
to filter items early, they are skipped for all partitions. This avoids
the need making an extra copy to select only turns with location_ok status, for buffer.
It can increase performance with 20%
2014-12-20 14:47:57 +01:00

669 lines
18 KiB
C++

// Boost.Geometry (aka GGL, Generic Geometry Library)
//
// Copyright (c) 2007-2012 Barend Gehrels, 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 <geometry_test_common.hpp>
#include <algorithms/test_overlay.hpp>
#include <boost/geometry/geometry.hpp>
#include <boost/geometry/multi/geometries/multi_point.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <boost/geometry/algorithms/detail/partition.hpp>
#include <boost/geometry/io/wkt/wkt.hpp>
#include <boost/geometry/multi/io/wkt/wkt.hpp>
#if defined(TEST_WITH_SVG)
# include <boost/geometry/io/svg/svg_mapper.hpp>
#endif
#include <boost/random/linear_congruential.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/uniform_real.hpp>
#include <boost/random/variate_generator.hpp>
template <typename Box>
struct box_item
{
int id;
Box box;
box_item(int i = 0, std::string const& wkt = "")
: id(i)
{
if (! wkt.empty())
{
bg::read_wkt(wkt, box);
}
}
};
struct get_box
{
template <typename Box, typename InputItem>
static inline void apply(Box& total, InputItem const& item)
{
bg::expand(total, item.box);
}
};
struct ovelaps_box
{
template <typename Box, typename InputItem>
static inline bool apply(Box const& box, InputItem const& item)
{
return ! bg::detail::disjoint::disjoint_box_box(box, item.box);
}
};
template <typename Box>
struct box_visitor
{
int count;
typename bg::default_area_result<Box>::type area;
box_visitor()
: count(0)
, area(0)
{}
template <typename Item>
inline void apply(Item const& item1, Item const& item2)
{
if (bg::intersects(item1.box, item2.box))
{
Box b;
bg::intersection(item1.box, item2.box, b);
area += bg::area(b);
count++;
}
}
};
struct point_in_box_visitor
{
int count;
point_in_box_visitor()
: count(0)
{}
template <typename Point, typename BoxItem>
inline void apply(Point const& point, BoxItem const& box_item)
{
if (bg::within(point, box_item.box))
{
count++;
}
}
};
struct reversed_point_in_box_visitor
{
int count;
reversed_point_in_box_visitor()
: count(0)
{}
template <typename BoxItem, typename Point>
inline void apply(BoxItem const& box_item, Point const& point)
{
if (bg::within(point, box_item.box))
{
count++;
}
}
};
template <typename Box>
void test_boxes(std::string const& wkt_box_list, double expected_area, int expected_count)
{
std::vector<std::string> wkt_boxes;
boost::split(wkt_boxes, wkt_box_list, boost::is_any_of(";"), boost::token_compress_on);
typedef box_item<Box> sample;
std::vector<sample> boxes;
int index = 1;
BOOST_FOREACH(std::string const& wkt, wkt_boxes)
{
boxes.push_back(sample(index++, wkt));
}
box_visitor<Box> visitor;
bg::partition
<
Box, get_box, ovelaps_box
>::apply(boxes, visitor, 1);
BOOST_CHECK_CLOSE(visitor.area, expected_area, 0.001);
BOOST_CHECK_EQUAL(visitor.count, expected_count);
}
struct point_item
{
point_item()
: id(0)
{}
int id;
double x;
double y;
};
BOOST_GEOMETRY_REGISTER_POINT_2D(point_item, double, cs::cartesian, x, y)
struct get_point
{
template <typename Box, typename InputItem>
static inline void apply(Box& total, InputItem const& item)
{
bg::expand(total, item);
}
};
struct ovelaps_point
{
template <typename Box, typename InputItem>
static inline bool apply(Box const& box, InputItem const& item)
{
return ! bg::disjoint(item, box);
}
};
struct point_visitor
{
int count;
point_visitor()
: count(0)
{}
template <typename Item>
inline void apply(Item const& item1, Item const& item2)
{
if (bg::equals(item1, item2))
{
count++;
}
}
};
void test_points(std::string const& wkt1, std::string const& wkt2, int expected_count)
{
bg::model::multi_point<point_item> mp1, mp2;
bg::read_wkt(wkt1, mp1);
bg::read_wkt(wkt2, mp2);
int id = 1;
BOOST_FOREACH(point_item& p, mp1)
{ p.id = id++; }
id = 1;
BOOST_FOREACH(point_item& p, mp2)
{ p.id = id++; }
point_visitor visitor;
bg::partition
<
bg::model::box<point_item>, get_point, ovelaps_point
>::apply(mp1, mp2, visitor, 1);
BOOST_CHECK_EQUAL(visitor.count, expected_count);
}
template <typename P>
void test_all()
{
typedef bg::model::box<P> box;
test_boxes<box>(
// 1 2 3 4 5 6 7
"box(0 0,1 1); box(0 0,2 2); box(9 9,10 10); box(8 8,9 9); box(4 4,6 6); box(2 4,6 8); box(7 1,8 2)",
5, // Area(Intersection(1,2)) + A(I(5,6))
3);
test_boxes<box>(
"box(0 0,10 10); box(4 4,6 6); box(3 3,7 7)",
4 + 16 + 4, // A(I(1,2)) + A(I(1,3)) + A(I(2,3))
3);
test_boxes<box>(
"box(0 2,10 3); box(3 1,4 5); box(7 1,8 5)",
1 + 1, // A(I(1,2)) + A(I(1,3))
2);
test_points("multipoint((1 1))", "multipoint((1 1))", 1);
test_points("multipoint((0 0),(1 1),(7 3),(10 10))", "multipoint((1 1),(2 2),(7 3))", 2);
}
//------------------- higher volumes
#if defined(TEST_WITH_SVG)
template <typename SvgMapper>
struct svg_visitor
{
SvgMapper& m_mapper;
svg_visitor(SvgMapper& mapper)
: m_mapper(mapper)
{}
template <typename Box>
inline void apply(Box const& box, int level)
{
/*
std::string color("rgb(64,64,64)");
switch(level)
{
case 0 : color = "rgb(255,0,0)"; break;
case 1 : color = "rgb(0,255,0)"; break;
case 2 : color = "rgb(0,0,255)"; break;
case 3 : color = "rgb(255,255,0)"; break;
case 4 : color = "rgb(255,0,255)"; break;
case 5 : color = "rgb(0,255,255)"; break;
case 6 : color = "rgb(255,128,0)"; break;
case 7 : color = "rgb(0,128,255)"; break;
}
std::ostringstream style;
style << "fill:none;stroke-width:" << (5.0 - level / 2.0) << ";stroke:" << color << ";";
m_mapper.map(box, style.str());
*/
m_mapper.map(box, "fill:none;stroke-width:2;stroke:rgb(0,0,0);");
}
};
#endif
template <typename Collection>
void fill_points(Collection& collection, int seed, int size, int count)
{
typedef boost::minstd_rand base_generator_type;
base_generator_type generator(seed);
boost::uniform_int<> random_coordinate(0, size - 1);
boost::variate_generator<base_generator_type&, boost::uniform_int<> >
coordinate_generator(generator, random_coordinate);
std::set<std::pair<int, int> > included;
int n = 0;
for (int i = 0; n < count && i < count*count; i++)
{
int x = coordinate_generator();
int y = coordinate_generator();
std::pair<int, int> pair = std::make_pair(x, y);
if (included.find(pair) == included.end())
{
included.insert(pair);
typename boost::range_value<Collection>::type item;
item.x = x;
item.y = y;
collection.push_back(item);
n++;
}
}
}
void test_many_points(int seed, int size, int count)
{
bg::model::multi_point<point_item> mp1, mp2;
fill_points(mp1, seed, size, count);
fill_points(mp2, seed * 2, size, count);
// Test equality in quadratic loop
int expected_count = 0;
BOOST_FOREACH(point_item const& item1, mp1)
{
BOOST_FOREACH(point_item const& item2, mp2)
{
if (bg::equals(item1, item2))
{
expected_count++;
}
}
}
#if defined(TEST_WITH_SVG)
std::ostringstream filename;
filename << "partition" << seed << ".svg";
std::ofstream svg(filename.str().c_str());
bg::svg_mapper<point_item> mapper(svg, 800, 800);
{
point_item p;
p.x = -1; p.y = -1; mapper.add(p);
p.x = size + 1; p.y = size + 1; mapper.add(p);
}
typedef svg_visitor<bg::svg_mapper<point_item> > box_visitor_type;
box_visitor_type box_visitor(mapper);
#else
typedef bg::detail::partition::visit_no_policy box_visitor_type;
box_visitor_type box_visitor;
#endif
point_visitor visitor;
bg::partition
<
bg::model::box<point_item>,
get_point, ovelaps_point,
get_point, ovelaps_point,
bg::detail::partition::include_all_policy,
bg::detail::partition::include_all_policy,
box_visitor_type
>::apply(mp1, mp2, visitor, 2, box_visitor);
BOOST_CHECK_EQUAL(visitor.count, expected_count);
#if defined(TEST_WITH_SVG)
BOOST_FOREACH(point_item const& item, mp1)
{
mapper.map(item, "fill:rgb(255,128,0);stroke:rgb(0,0,100);stroke-width:1", 8);
}
BOOST_FOREACH(point_item const& item, mp2)
{
mapper.map(item, "fill:rgb(0,128,255);stroke:rgb(0,0,100);stroke-width:1", 4);
}
#endif
}
template <typename Collection>
void fill_boxes(Collection& collection, int seed, int size, int count)
{
typedef boost::minstd_rand base_generator_type;
base_generator_type generator(seed);
boost::uniform_int<> random_coordinate(0, size * 10 - 1);
boost::variate_generator<base_generator_type&, boost::uniform_int<> >
coordinate_generator(generator, random_coordinate);
int n = 0;
for (int i = 0; n < count && i < count*count; i++)
{
int w = coordinate_generator() % 30;
int h = coordinate_generator() % 30;
if (w > 0 && h > 0)
{
int x = coordinate_generator();
int y = coordinate_generator();
if (x + w < size * 10 && y + h < size * 10)
{
typename boost::range_value<Collection>::type item(n+1);
bg::assign_values(item.box, x / 10.0, y / 10.0, (x + w) / 10.0, (y + h) / 10.0);
collection.push_back(item);
n++;
}
}
}
}
void test_many_boxes(int seed, int size, int count)
{
typedef bg::model::box<point_item> box_type;
std::vector<box_item<box_type> > boxes;
fill_boxes(boxes, seed, size, count);
// Test equality in quadratic loop
int expected_count = 0;
double expected_area = 0.0;
BOOST_FOREACH(box_item<box_type> const& item1, boxes)
{
BOOST_FOREACH(box_item<box_type> const& item2, boxes)
{
if (item1.id < item2.id)
{
if (bg::intersects(item1.box, item2.box))
{
box_type b;
bg::intersection(item1.box, item2.box, b);
expected_area += bg::area(b);
expected_count++;
}
}
}
}
#if defined(TEST_WITH_SVG)
std::ostringstream filename;
filename << "partition_box_" << seed << ".svg";
std::ofstream svg(filename.str().c_str());
bg::svg_mapper<point_item> mapper(svg, 800, 800);
{
point_item p;
p.x = -1; p.y = -1; mapper.add(p);
p.x = size + 1; p.y = size + 1; mapper.add(p);
}
BOOST_FOREACH(box_item<box_type> const& item, boxes)
{
mapper.map(item.box, "opacity:0.6;fill:rgb(50,50,210);stroke:rgb(0,0,0);stroke-width:1");
}
typedef svg_visitor<bg::svg_mapper<point_item> > partition_box_visitor_type;
partition_box_visitor_type partition_box_visitor(mapper);
#else
typedef bg::detail::partition::visit_no_policy partition_box_visitor_type;
partition_box_visitor_type partition_box_visitor;
#endif
box_visitor<box_type> visitor;
bg::partition
<
box_type,
get_box, ovelaps_box,
get_box, ovelaps_box,
bg::detail::partition::include_all_policy,
bg::detail::partition::include_all_policy,
partition_box_visitor_type
>::apply(boxes, visitor, 2, partition_box_visitor);
BOOST_CHECK_EQUAL(visitor.count, expected_count);
BOOST_CHECK_CLOSE(visitor.area, expected_area, 0.001);
}
void test_two_collections(int seed1, int seed2, int size, int count)
{
typedef bg::model::box<point_item> box_type;
std::vector<box_item<box_type> > boxes1, boxes2;
fill_boxes(boxes1, seed1, size, count);
fill_boxes(boxes2, seed2, size, count);
// Get expectations in quadratic loop
int expected_count = 0;
double expected_area = 0.0;
BOOST_FOREACH(box_item<box_type> const& item1, boxes1)
{
BOOST_FOREACH(box_item<box_type> const& item2, boxes2)
{
if (bg::intersects(item1.box, item2.box))
{
box_type b;
bg::intersection(item1.box, item2.box, b);
expected_area += bg::area(b);
expected_count++;
}
}
}
#if defined(TEST_WITH_SVG)
std::ostringstream filename;
filename << "partition_boxes_" << seed1 << "_" << seed2 << ".svg";
std::ofstream svg(filename.str().c_str());
bg::svg_mapper<point_item> mapper(svg, 800, 800);
{
point_item p;
p.x = -1; p.y = -1; mapper.add(p);
p.x = size + 1; p.y = size + 1; mapper.add(p);
}
BOOST_FOREACH(box_item<box_type> const& item, boxes1)
{
mapper.map(item.box, "opacity:0.6;fill:rgb(50,50,210);stroke:rgb(0,0,0);stroke-width:1");
}
BOOST_FOREACH(box_item<box_type> const& item, boxes2)
{
mapper.map(item.box, "opacity:0.6;fill:rgb(0,255,0);stroke:rgb(0,0,0);stroke-width:1");
}
typedef svg_visitor<bg::svg_mapper<point_item> > partition_box_visitor_type;
partition_box_visitor_type partition_box_visitor(mapper);
#else
typedef bg::detail::partition::visit_no_policy partition_box_visitor_type;
partition_box_visitor_type partition_box_visitor;
#endif
box_visitor<box_type> visitor;
bg::partition
<
box_type,
get_box, ovelaps_box,
get_box, ovelaps_box,
bg::detail::partition::include_all_policy,
bg::detail::partition::include_all_policy,
partition_box_visitor_type
>::apply(boxes1, boxes2, visitor, 2, partition_box_visitor);
BOOST_CHECK_EQUAL(visitor.count, expected_count);
BOOST_CHECK_CLOSE(visitor.area, expected_area, 0.001);
}
void test_heterogenuous_collections(int seed1, int seed2, int size, int count)
{
typedef bg::model::box<point_item> box_type;
std::vector<point_item> points;
std::vector<box_item<box_type> > boxes;
fill_points(points, seed1, size, count);
fill_boxes(boxes, seed2, size, count);
// Get expectations in quadratic loop
int expected_count = 0;
BOOST_FOREACH(point_item const& point, points)
{
BOOST_FOREACH(box_item<box_type> const& box_item, boxes)
{
if (bg::within(point, box_item.box))
{
expected_count++;
}
}
}
#if defined(TEST_WITH_SVG)
std::ostringstream filename;
filename << "partition_heterogeneous_" << seed1 << "_" << seed2 << ".svg";
std::ofstream svg(filename.str().c_str());
bg::svg_mapper<point_item> mapper(svg, 800, 800);
{
point_item p;
p.x = -1; p.y = -1; mapper.add(p);
p.x = size + 1; p.y = size + 1; mapper.add(p);
}
BOOST_FOREACH(point_item const& point, points)
{
mapper.map(point, "fill:rgb(255,128,0);stroke:rgb(0,0,100);stroke-width:1", 8);
}
BOOST_FOREACH(box_item<box_type> const& item, boxes)
{
mapper.map(item.box, "opacity:0.6;fill:rgb(0,255,0);stroke:rgb(0,0,0);stroke-width:1");
}
typedef svg_visitor<bg::svg_mapper<point_item> > partition_box_visitor_type;
partition_box_visitor_type partition_box_visitor(mapper);
#else
typedef bg::detail::partition::visit_no_policy partition_box_visitor_type;
partition_box_visitor_type partition_box_visitor;
#endif
point_in_box_visitor visitor1;
bg::partition
<
box_type,
get_point, ovelaps_point,
get_box, ovelaps_box,
bg::detail::partition::include_all_policy,
bg::detail::partition::include_all_policy,
partition_box_visitor_type
>::apply(points, boxes, visitor1, 2, partition_box_visitor);
reversed_point_in_box_visitor visitor2;
bg::partition
<
box_type,
get_box, ovelaps_box,
get_point, ovelaps_point,
bg::detail::partition::include_all_policy,
bg::detail::partition::include_all_policy,
partition_box_visitor_type
>::apply(boxes, points, visitor2, 2, partition_box_visitor);
BOOST_CHECK_EQUAL(visitor1.count, expected_count);
BOOST_CHECK_EQUAL(visitor2.count, expected_count);
}
int test_main( int , char* [] )
{
test_all<bg::model::d2::point_xy<double> >();
test_many_points(12345, 20, 40);
test_many_points(54321, 20, 60);
test_many_points(67890, 20, 80);
test_many_points(98765, 20, 100);
for (int i = 1; i < 10; i++)
{
test_many_points(i, 30, i * 20);
}
test_many_boxes(12345, 20, 40);
for (int i = 1; i < 10; i++)
{
test_many_boxes(i, 20, i * 10);
}
test_two_collections(12345, 54321, 20, 40);
test_two_collections(67890, 98765, 20, 60);
test_heterogenuous_collections(67890, 98765, 20, 60);
return 0;
}