implement hit_test for new_geometry
This commit is contained in:
parent
537a392a09
commit
1a57aef9c6
2 changed files with 231 additions and 13 deletions
|
@ -25,11 +25,129 @@
|
|||
|
||||
// mapnik
|
||||
#include <mapnik/feature.hpp>
|
||||
#include <mapnik/geometry_impl.hpp>
|
||||
#include <mapnik/geom_util.hpp>
|
||||
// boost
|
||||
|
||||
|
||||
namespace mapnik {
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline bool pip(double x0,
|
||||
double y0,
|
||||
double x1,
|
||||
double y1,
|
||||
double x,
|
||||
double y)
|
||||
{
|
||||
return ((((y1 <= y) && (y < y0)) || ((y0 <= y) && (y < y1))) && (x < (x0 - x1) * (y - y1) / (y0 - y1) + x1));
|
||||
}
|
||||
|
||||
struct hit_test_visitor
|
||||
{
|
||||
hit_test_visitor(double x, double y, double tol)
|
||||
: x_(x),
|
||||
y_(y),
|
||||
tol_(tol) {}
|
||||
|
||||
bool operator() (new_geometry::point const& geom) const
|
||||
{
|
||||
return distance(geom.x, geom.y, x_, y_) <= tol_;
|
||||
}
|
||||
bool operator() (new_geometry::multi_point const& geom) const
|
||||
{
|
||||
for (auto const& pt : geom)
|
||||
{
|
||||
if (distance(pt.x, pt.y, x_, y_) <= tol_) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool operator() (new_geometry::line_string const& geom) const
|
||||
{
|
||||
std::size_t num_points = geom.num_points();
|
||||
if (num_points > 1)
|
||||
{
|
||||
for (std::size_t i = 1; i < num_points; ++i)
|
||||
{
|
||||
auto const& pt0 = geom[i-1];
|
||||
auto const& pt1 = geom[i];
|
||||
double distance = point_to_segment_distance(x_,y_,pt0.x,pt0.y,pt1.x,pt1.y);
|
||||
if (distance < tol_) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool operator() (new_geometry::multi_line_string const& geom) const
|
||||
{
|
||||
for (auto const& line: geom)
|
||||
{
|
||||
if (operator()(line)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool operator() (new_geometry::polygon const& geom) const
|
||||
{
|
||||
auto const& exterior = geom.exterior_ring;
|
||||
std::size_t num_points = exterior.num_points();
|
||||
if (num_points < 2) return false;
|
||||
bool inside = false;
|
||||
for (std::size_t i = 1; i < num_points; ++i)
|
||||
{
|
||||
auto const& pt0 = exterior[i-1];
|
||||
auto const& pt1 = exterior[i];
|
||||
// todo - account for tolerance
|
||||
if (pip(pt0.x,pt0.y,pt1.x,pt1.y,x_,y_))
|
||||
{
|
||||
inside = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!inside) return false;
|
||||
for (auto const& ring : geom.interior_rings)
|
||||
{
|
||||
std::size_t num_interior_points = ring.size();
|
||||
for (std::size_t j = 1; j < num_interior_points; ++j)
|
||||
{
|
||||
auto const& pt0 = ring[j-1];
|
||||
auto const& pt1 = ring[j];
|
||||
if (pip(pt0.x,pt0.y,pt1.x,pt1.y,x_,y_))
|
||||
{
|
||||
// TODO - account for tolerance
|
||||
inside=!inside;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return inside;
|
||||
}
|
||||
bool operator() (new_geometry::multi_polygon const& geom) const
|
||||
{
|
||||
for (auto const& poly: geom)
|
||||
{
|
||||
if (operator()(poly)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool operator() (new_geometry::geometry_collection const& collection) const
|
||||
{
|
||||
for (auto const& geom: collection)
|
||||
{
|
||||
if (mapnik::util::apply_visitor((*this),geom)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
double x_;
|
||||
double y_;
|
||||
double tol_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
inline bool hit_test(mapnik::new_geometry::geometry const& geom, double x, double y, double tol)
|
||||
{
|
||||
return mapnik::util::apply_visitor(detail::hit_test_visitor(x,y,tol), geom);
|
||||
}
|
||||
|
||||
class hit_test_filter
|
||||
{
|
||||
public:
|
||||
|
@ -38,18 +156,9 @@ public:
|
|||
y_(y),
|
||||
tol_(tol) {}
|
||||
|
||||
bool pass(feature_impl & feature)
|
||||
bool pass(feature_impl const& feature)
|
||||
{
|
||||
// FIXME
|
||||
/*
|
||||
for (geometry_type const& geom : feature.paths())
|
||||
{
|
||||
vertex_adapter va(geom);
|
||||
if (label::hit_test(va, x_,y_,tol_))
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
return false;
|
||||
return hit_test(feature.get_geometry(),x_,y_,tol_);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
109
tests/cxx/geometry_hit_test.cpp
Normal file
109
tests/cxx/geometry_hit_test.cpp
Normal file
|
@ -0,0 +1,109 @@
|
|||
|
||||
#include "catch.hpp"
|
||||
|
||||
#include <mapnik/geometry_impl.hpp>
|
||||
#include <mapnik/hit_test_filter.hpp>
|
||||
#include <mapnik/geometry_correct.hpp>
|
||||
|
||||
TEST_CASE("geometry ops") {
|
||||
|
||||
SECTION("hit_test_filter") {
|
||||
using namespace mapnik::new_geometry;
|
||||
{
|
||||
geometry geom(point(0,0));
|
||||
REQUIRE( mapnik::hit_test(geom,0,0,0) );
|
||||
}
|
||||
{
|
||||
geometry geom(point(0,0));
|
||||
REQUIRE( mapnik::hit_test(geom,1,0,1) );
|
||||
}
|
||||
{
|
||||
geometry geom(point(0,0));
|
||||
REQUIRE( mapnik::hit_test(geom,0,1,1) );
|
||||
}
|
||||
{
|
||||
geometry geom(point(0,0));
|
||||
REQUIRE( mapnik::hit_test(geom,1,1,1.5) );
|
||||
}
|
||||
{
|
||||
line_string line;
|
||||
line.add_coord(0,0);
|
||||
line.add_coord(1,1);
|
||||
line.add_coord(2,2);
|
||||
geometry geom(line);
|
||||
REQUIRE( mapnik::hit_test(geom,0,0,1.5) );
|
||||
}
|
||||
{
|
||||
line_string line;
|
||||
line.add_coord(0,0);
|
||||
line.add_coord(1,1);
|
||||
line.add_coord(2,2);
|
||||
multi_line_string multi_line;
|
||||
multi_line.emplace_back(std::move(line));
|
||||
geometry geom(multi_line);
|
||||
REQUIRE( mapnik::hit_test(geom,0,0,1.5) );
|
||||
}
|
||||
{
|
||||
polygon poly;
|
||||
linear_ring ring;
|
||||
ring.add_coord(0,0);
|
||||
ring.add_coord(-10,0);
|
||||
ring.add_coord(-10,10);
|
||||
ring.add_coord(0,10);
|
||||
ring.add_coord(0,0);
|
||||
poly.set_exterior_ring(std::move(ring));
|
||||
geometry geom(poly);
|
||||
REQUIRE( mapnik::hit_test(geom,-5,5,0) );
|
||||
|
||||
multi_polygon mp;
|
||||
mp.push_back(poly);
|
||||
geometry geom_mp(mp);
|
||||
REQUIRE( mapnik::hit_test(geom_mp,-5,5,0) );
|
||||
|
||||
correct(geom);
|
||||
REQUIRE( mapnik::hit_test(geom,-5,5,0) );
|
||||
correct(geom_mp);
|
||||
REQUIRE( mapnik::hit_test(geom_mp,-5,5,0) );
|
||||
|
||||
geometry_collection gc;
|
||||
REQUIRE( !mapnik::hit_test(geometry(gc),-5,5,0) );
|
||||
gc.push_back(geom_mp);
|
||||
REQUIRE( mapnik::hit_test(geometry(gc),-5,5,0) );
|
||||
REQUIRE( !mapnik::hit_test(geometry(gc),-50,-50,0) );
|
||||
gc.emplace_back(point(-50,-50));
|
||||
REQUIRE( mapnik::hit_test(geometry(gc),-50,-50,0) );
|
||||
}
|
||||
|
||||
{
|
||||
// polygon with hole
|
||||
polygon poly;
|
||||
linear_ring ring;
|
||||
ring.add_coord(0,0);
|
||||
ring.add_coord(-10,0);
|
||||
ring.add_coord(-10,10);
|
||||
ring.add_coord(0,10);
|
||||
ring.add_coord(0,0);
|
||||
poly.set_exterior_ring(std::move(ring));
|
||||
linear_ring hole;
|
||||
hole.add_coord(-7,7);
|
||||
hole.add_coord(-7,3);
|
||||
hole.add_coord(-3,3);
|
||||
hole.add_coord(-3,7);
|
||||
hole.add_coord(-7,7);
|
||||
poly.add_hole(std::move(hole));
|
||||
geometry geom(poly);
|
||||
REQUIRE( !mapnik::hit_test(geom,-5,5,0) );
|
||||
// add another hole inside the first hole
|
||||
// which should be considered a hit
|
||||
linear_ring fill;
|
||||
fill.add_coord(-6,4);
|
||||
fill.add_coord(-6,6);
|
||||
fill.add_coord(-4,6);
|
||||
fill.add_coord(-4,4);
|
||||
fill.add_coord(-6,4);
|
||||
poly.add_hole(std::move(fill));
|
||||
REQUIRE( mapnik::hit_test(geometry(poly),-5,5,0) );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue