From a97eace434d53d1a456ce8ece91891d7df6c6dac Mon Sep 17 00:00:00 2001 From: Jiri Drbalek Date: Wed, 17 Jan 2018 12:58:42 +0000 Subject: [PATCH] Interior: cover the case of empty polygon or exterior ring --- include/mapnik/geometry/interior.hpp | 5 ++- .../mapnik/markers_placements/interior.hpp | 10 ++++- .../process_point_symbolizer.hpp | 2 +- src/geometry/interior.cpp | 28 +++++++++--- src/text/symbolizer_helpers.cpp | 7 ++- test/unit/geometry/interior.cpp | 43 +++++++++++++++++++ 6 files changed, 83 insertions(+), 12 deletions(-) create mode 100644 test/unit/geometry/interior.cpp diff --git a/include/mapnik/geometry/interior.hpp b/include/mapnik/geometry/interior.hpp index 232ab3321..e7aacd785 100644 --- a/include/mapnik/geometry/interior.hpp +++ b/include/mapnik/geometry/interior.hpp @@ -23,13 +23,16 @@ #ifndef MAPNIK_GEOMETRY_INTERIOR_HPP #define MAPNIK_GEOMETRY_INTERIOR_HPP +#include // for MAPNIK_DECL #include #include namespace mapnik { namespace geometry { template -point interior(polygon const& polygon, double scale_factor); +MAPNIK_DECL bool interior(polygon const& polygon, + double scale_factor, + point & pt); } } diff --git a/include/mapnik/markers_placements/interior.hpp b/include/mapnik/markers_placements/interior.hpp index 69c4b2e07..453251108 100644 --- a/include/mapnik/markers_placements/interior.hpp +++ b/include/mapnik/markers_placements/interior.hpp @@ -62,8 +62,14 @@ public: { geometry::polygon_vertex_processor vertex_processor; vertex_processor.add_path(this->locator_); - geometry::point placement = geometry::interior(vertex_processor.polygon_, - this->params_.scale_factor); + geometry::point placement; + if (!geometry::interior(vertex_processor.polygon_, + this->params_.scale_factor, + placement)) + { + this->done_ = true; + return false; + } x = placement.x; y = placement.y; diff --git a/include/mapnik/renderer_common/process_point_symbolizer.hpp b/include/mapnik/renderer_common/process_point_symbolizer.hpp index 964d4f122..44257ce78 100644 --- a/include/mapnik/renderer_common/process_point_symbolizer.hpp +++ b/include/mapnik/renderer_common/process_point_symbolizer.hpp @@ -80,7 +80,7 @@ void render_point_symbolizer(point_symbolizer const &sym, else if (type == mapnik::geometry::geometry_types::Polygon) { auto const& poly = mapnik::util::get >(geometry); - pt = geometry::interior(poly, common.scale_factor_); + if (!geometry::interior(poly, common.scale_factor_, pt)) return; } else { diff --git a/src/geometry/interior.cpp b/src/geometry/interior.cpp index d1a7c5aee..9f8f5b427 100644 --- a/src/geometry/interior.cpp +++ b/src/geometry/interior.cpp @@ -31,6 +31,11 @@ #include #include +#pragma GCC diagnostic push +#include +#include +#pragma GCC diagnostic pop + namespace mapnik { namespace geometry { // Interior algorithm is realized as a modification of Polylabel algorithm @@ -140,8 +145,13 @@ struct cell }; template -point polylabel(const polygon& polygon, T precision = 1) +boost::optional> polylabel(polygon const& polygon, T precision = 1) { + if (polygon.empty() || polygon.front().empty()) + { + return boost::none; + } + // find the bounding box of the outer ring const box2d bbox = envelope(polygon.at(0)); const point size { bbox.width(), bbox.height() }; @@ -159,14 +169,14 @@ point polylabel(const polygon& polygon, T precision = 1) if (cell_size == 0) { - return { bbox.minx(), bbox.miny() }; + return point{ bbox.minx(), bbox.miny() }; } point centroid; if (!mapnik::geometry::centroid(polygon, centroid)) { auto center = bbox.center(); - return { center.x, center.y }; + return point{ center.x, center.y }; } fitness_functor fitness_func(centroid, size); @@ -212,15 +222,21 @@ point polylabel(const polygon& polygon, T precision = 1) } // namespace detail template -point interior(polygon const& polygon, double scale_factor) +bool interior(polygon const& polygon, double scale_factor, point & pt) { // This precision has been chosen to work well in the map (viewport) coordinates. double precision = 10.0 * scale_factor; - return detail::polylabel(polygon, precision); + if (boost::optional> opt = detail::polylabel(polygon, precision)) + { + pt = *opt; + return true; + } + + return false; } template -point interior(polygon const& polygon, double scale_factor); +bool interior(polygon const& polygon, double scale_factor, point & pt); } } diff --git a/src/text/symbolizer_helpers.cpp b/src/text/symbolizer_helpers.cpp index 9d3f1880c..493e9fe73 100644 --- a/src/text/symbolizer_helpers.cpp +++ b/src/text/symbolizer_helpers.cpp @@ -303,8 +303,11 @@ void base_symbolizer_helper::initialize_points() const geometry::polygon tranformed_poly(geometry::transform(poly, transform_group)); if (how_placed == INTERIOR_PLACEMENT) { - geometry::point pt = geometry::interior(tranformed_poly, scale_factor_); - points_.emplace_back(pt.x, pt.y); + geometry::point pt; + if (geometry::interior(tranformed_poly, scale_factor_, pt)) + { + points_.emplace_back(pt.x, pt.y); + } } else if (how_placed == POLYLABEL_PLACEMENT) { diff --git a/test/unit/geometry/interior.cpp b/test/unit/geometry/interior.cpp new file mode 100644 index 000000000..bf7d49e23 --- /dev/null +++ b/test/unit/geometry/interior.cpp @@ -0,0 +1,43 @@ +#include "catch.hpp" + +#include + +TEST_CASE("polygon interior") { + +SECTION("empty polygon") { + + mapnik::geometry::polygon poly; + mapnik::geometry::point pt; + + CHECK(!mapnik::geometry::interior(poly, 1.0, pt)); +} + +SECTION("empty exterior ring") { + + mapnik::geometry::polygon poly; + poly.emplace_back(); + + mapnik::geometry::point pt; + + CHECK(!mapnik::geometry::interior(poly, 1.0, pt)); +} + +SECTION("interior of a square") { + + mapnik::geometry::polygon poly; + poly.emplace_back(); + auto & exterior_ring = poly.front(); + exterior_ring.emplace_back(-1, -1); + exterior_ring.emplace_back( 1, -1); + exterior_ring.emplace_back( 1, 1); + exterior_ring.emplace_back(-1, 1); + exterior_ring.emplace_back(-1, -1); + + mapnik::geometry::point pt{ -3, -3 }; + + CHECK(mapnik::geometry::interior(poly, 1.0, pt)); + CHECK(pt.x == Approx(0)); + CHECK(pt.y == Approx(0)); +} + +}