Interior: cover the case of empty polygon or exterior ring

This commit is contained in:
Jiri Drbalek 2018-01-17 12:58:42 +00:00
parent fe3c2762c0
commit 72989d440b
6 changed files with 71 additions and 12 deletions

View file

@ -24,11 +24,14 @@
#define MAPNIK_GEOMETRY_INTERIOR_HPP #define MAPNIK_GEOMETRY_INTERIOR_HPP
#include <mapnik/geometry.hpp> #include <mapnik/geometry.hpp>
#include <mapnik/config.hpp> // for MAPNIK_DECL
namespace mapnik { namespace geometry { namespace mapnik { namespace geometry {
template <class T> template <class T>
point<T> interior(polygon<T> const& polygon, double scale_factor); MAPNIK_DECL bool interior(polygon<T> const& polygon,
double scale_factor,
point<T> & pt);
} } } }

View file

@ -62,8 +62,14 @@ public:
{ {
geometry::polygon_vertex_processor<double> vertex_processor; geometry::polygon_vertex_processor<double> vertex_processor;
vertex_processor.add_path(this->locator_); vertex_processor.add_path(this->locator_);
geometry::point<double> placement = geometry::interior(vertex_processor.polygon_, geometry::point<double> placement;
this->params_.scale_factor); if (!geometry::interior(vertex_processor.polygon_,
this->params_.scale_factor,
placement))
{
this->done_ = true;
return false;
}
x = placement.x; x = placement.x;
y = placement.y; y = placement.y;

View file

@ -80,7 +80,7 @@ void render_point_symbolizer(point_symbolizer const &sym,
else if (type == mapnik::geometry::geometry_types::Polygon) else if (type == mapnik::geometry::geometry_types::Polygon)
{ {
auto const& poly = mapnik::util::get<geometry::polygon<double> >(geometry); auto const& poly = mapnik::util::get<geometry::polygon<double> >(geometry);
pt = geometry::interior(poly, common.scale_factor_); if (!geometry::interior(poly, common.scale_factor_, pt)) return;
} }
else else
{ {

View file

@ -30,6 +30,11 @@
#include <iostream> #include <iostream>
#include <queue> #include <queue>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/optional.hpp>
#pragma GCC diagnostic pop
namespace mapnik { namespace geometry { namespace mapnik { namespace geometry {
// Interior algorithm is realized as a modification of Polylabel algorithm // Interior algorithm is realized as a modification of Polylabel algorithm
@ -148,8 +153,13 @@ struct cell
}; };
template <class T> template <class T>
point<T> polylabel(const polygon<T>& polygon, T precision = 1) boost::optional<point<T>> polylabel(polygon<T> const& polygon, T precision = 1)
{ {
if (polygon.exterior_ring.empty())
{
return boost::none;
}
// find the bounding box of the outer ring // find the bounding box of the outer ring
const box2d<T> bbox = envelope(polygon.exterior_ring); const box2d<T> bbox = envelope(polygon.exterior_ring);
const point<T> size { bbox.width(), bbox.height() }; const point<T> size { bbox.width(), bbox.height() };
@ -167,14 +177,14 @@ point<T> polylabel(const polygon<T>& polygon, T precision = 1)
if (cell_size == 0) if (cell_size == 0)
{ {
return { bbox.minx(), bbox.miny() }; return point<T>{ bbox.minx(), bbox.miny() };
} }
point<T> centroid; point<T> centroid;
if (!mapnik::geometry::centroid(polygon, centroid)) if (!mapnik::geometry::centroid(polygon, centroid))
{ {
auto center = bbox.center(); auto center = bbox.center();
return { center.x, center.y }; return point<T>{ center.x, center.y };
} }
fitness_functor<T> fitness_func(centroid, size); fitness_functor<T> fitness_func(centroid, size);
@ -220,15 +230,21 @@ point<T> polylabel(const polygon<T>& polygon, T precision = 1)
} // namespace detail } // namespace detail
template <class T> template <class T>
point<T> interior(polygon<T> const& polygon, double scale_factor) bool interior(polygon<T> const& polygon, double scale_factor, point<T> & pt)
{ {
// This precision has been chosen to work well in the map (viewport) coordinates. // This precision has been chosen to work well in the map (viewport) coordinates.
double precision = 10.0 * scale_factor; double precision = 10.0 * scale_factor;
return detail::polylabel(polygon, precision); if (boost::optional<point<T>> opt = detail::polylabel(polygon, precision))
{
pt = *opt;
return true;
}
return false;
} }
template template
point<double> interior(polygon<double> const& polygon, double scale_factor); bool interior(polygon<double> const& polygon, double scale_factor, point<double> & pt);
} } } }

View file

@ -300,8 +300,11 @@ void base_symbolizer_helper::initialize_points() const
using transform_group_type = geometry::strategy_group<proj_strategy, view_strategy>; using transform_group_type = geometry::strategy_group<proj_strategy, view_strategy>;
transform_group_type transform_group(ps, vs); transform_group_type transform_group(ps, vs);
geometry::polygon<double> tranformed_poly(geometry::transform<double>(poly, transform_group)); geometry::polygon<double> tranformed_poly(geometry::transform<double>(poly, transform_group));
geometry::point<double> pt = geometry::interior(tranformed_poly, scale_factor_); geometry::point<double> pt;
if (geometry::interior(tranformed_poly, scale_factor_, pt))
{
points_.emplace_back(pt.x, pt.y); points_.emplace_back(pt.x, pt.y);
}
continue; continue;
} }
else else

View file

@ -0,0 +1,31 @@
#include "catch.hpp"
#include <mapnik/geometry/interior.hpp>
TEST_CASE("polygon interior") {
SECTION("empty polygon") {
mapnik::geometry::polygon<double> poly;
mapnik::geometry::point<double> pt;
CHECK(!mapnik::geometry::interior(poly, 1.0, pt));
}
SECTION("interior of a square") {
mapnik::geometry::polygon<double> poly;
poly.exterior_ring.emplace_back(-1, -1);
poly.exterior_ring.emplace_back( 1, -1);
poly.exterior_ring.emplace_back( 1, 1);
poly.exterior_ring.emplace_back(-1, 1);
poly.exterior_ring.emplace_back(-1, -1);
mapnik::geometry::point<double> pt{ -3, -3 };
CHECK(mapnik::geometry::interior(poly, 1.0, pt));
CHECK(pt.x == Approx(0));
CHECK(pt.y == Approx(0));
}
}