Merge pull request #3834 from mapycz/fix-interior-empty-polygon
Interior, Polylabel: cover empty polygon or exterior ring
This commit is contained in:
commit
8bc236ba7b
11 changed files with 157 additions and 22 deletions
|
@ -23,13 +23,16 @@
|
|||
#ifndef MAPNIK_GEOMETRY_INTERIOR_HPP
|
||||
#define MAPNIK_GEOMETRY_INTERIOR_HPP
|
||||
|
||||
#include <mapnik/config.hpp> // for MAPNIK_DECL
|
||||
#include <mapnik/geometry/polygon.hpp>
|
||||
#include <mapnik/geometry/point.hpp>
|
||||
|
||||
namespace mapnik { namespace geometry {
|
||||
|
||||
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);
|
||||
|
||||
} }
|
||||
|
||||
|
|
|
@ -23,16 +23,20 @@
|
|||
#ifndef MAPNIK_GEOMETRY_POLYLABEL_HPP
|
||||
#define MAPNIK_GEOMETRY_POLYLABEL_HPP
|
||||
|
||||
#include <mapnik/config.hpp> // for MAPNIK_DECL
|
||||
#include <mapnik/geometry/polygon.hpp>
|
||||
#include <mapnik/geometry/point.hpp>
|
||||
|
||||
namespace mapnik { namespace geometry {
|
||||
|
||||
template <class T>
|
||||
point<T> polylabel(polygon<T> const& polygon, T precision);
|
||||
MAPNIK_DECL bool polylabel(polygon<T> const& polygon,
|
||||
T precision,
|
||||
point<T> & pt);
|
||||
|
||||
template <class T>
|
||||
T polylabel_precision(polygon<T> const& polygon, double scale_factor);
|
||||
MAPNIK_DECL T polylabel_precision(polygon<T> const& polygon,
|
||||
double scale_factor);
|
||||
|
||||
} }
|
||||
|
||||
|
|
|
@ -62,8 +62,14 @@ public:
|
|||
{
|
||||
geometry::polygon_vertex_processor<double> vertex_processor;
|
||||
vertex_processor.add_path(this->locator_);
|
||||
geometry::point<double> placement = geometry::interior(vertex_processor.polygon_,
|
||||
this->params_.scale_factor);
|
||||
geometry::point<double> placement;
|
||||
if (!geometry::interior(vertex_processor.polygon_,
|
||||
this->params_.scale_factor,
|
||||
placement))
|
||||
{
|
||||
this->done_ = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
x = placement.x;
|
||||
y = placement.y;
|
||||
|
|
|
@ -56,7 +56,12 @@ public:
|
|||
vertex_processor.add_path(this->locator_);
|
||||
double precision = geometry::polylabel_precision(vertex_processor.polygon_,
|
||||
this->params_.scale_factor);
|
||||
geometry::point<double> placement = geometry::polylabel(vertex_processor.polygon_, precision);
|
||||
geometry::point<double> placement;
|
||||
if (!geometry::polylabel(vertex_processor.polygon_, precision, placement))
|
||||
{
|
||||
this->done_ = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
x = placement.x;
|
||||
y = placement.y;
|
||||
|
|
|
@ -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::polygon<double> >(geometry);
|
||||
pt = geometry::interior(poly, common.scale_factor_);
|
||||
if (!geometry::interior(poly, common.scale_factor_, pt)) return;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -31,6 +31,11 @@
|
|||
#include <iostream>
|
||||
#include <queue>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#include <mapnik/warning_ignore.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#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 <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.empty() || polygon.front().empty())
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// find the bounding box of the outer ring
|
||||
const box2d<T> bbox = envelope(polygon.at(0));
|
||||
const point<T> size { bbox.width(), bbox.height() };
|
||||
|
@ -159,14 +169,14 @@ point<T> polylabel(const polygon<T>& polygon, T precision = 1)
|
|||
|
||||
if (cell_size == 0)
|
||||
{
|
||||
return { bbox.minx(), bbox.miny() };
|
||||
return point<T>{ bbox.minx(), bbox.miny() };
|
||||
}
|
||||
|
||||
point<T> centroid;
|
||||
if (!mapnik::geometry::centroid(polygon, centroid))
|
||||
{
|
||||
auto center = bbox.center();
|
||||
return { center.x, center.y };
|
||||
return point<T>{ center.x, center.y };
|
||||
}
|
||||
|
||||
fitness_functor<T> fitness_func(centroid, size);
|
||||
|
@ -212,15 +222,21 @@ point<T> polylabel(const polygon<T>& polygon, T precision = 1)
|
|||
} // namespace detail
|
||||
|
||||
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.
|
||||
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
|
||||
point<double> interior(polygon<double> const& polygon, double scale_factor);
|
||||
bool interior(polygon<double> const& polygon, double scale_factor, point<double> & pt);
|
||||
|
||||
} }
|
||||
|
||||
|
|
|
@ -34,16 +34,25 @@ T polylabel_precision(polygon<T> const& polygon, double scale_factor)
|
|||
}
|
||||
|
||||
template <class T>
|
||||
point<T> polylabel(polygon<T> const& polygon, T precision)
|
||||
bool polylabel(polygon<T> const& polygon, T precision, point<T> & pt)
|
||||
{
|
||||
return mapbox::polylabel(polygon, precision);
|
||||
if (polygon.empty() || polygon.front().empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pt = mapbox::polylabel(polygon, precision);
|
||||
return true;
|
||||
}
|
||||
|
||||
template
|
||||
point<double> polylabel(polygon<double> const& polygon, double precision);
|
||||
bool polylabel(polygon<double> const& polygon,
|
||||
double precision,
|
||||
point<double> & pt);
|
||||
|
||||
template
|
||||
double polylabel_precision(polygon<double> const& polygon, double scale_factor);
|
||||
double polylabel_precision(polygon<double> const& polygon,
|
||||
double scale_factor);
|
||||
|
||||
} }
|
||||
|
||||
|
|
|
@ -303,14 +303,20 @@ void base_symbolizer_helper::initialize_points() const
|
|||
geometry::polygon<double> tranformed_poly(geometry::transform<double>(poly, transform_group));
|
||||
if (how_placed == INTERIOR_PLACEMENT)
|
||||
{
|
||||
geometry::point<double> pt = geometry::interior(tranformed_poly, scale_factor_);
|
||||
points_.emplace_back(pt.x, pt.y);
|
||||
geometry::point<double> pt;
|
||||
if (geometry::interior(tranformed_poly, scale_factor_, pt))
|
||||
{
|
||||
points_.emplace_back(pt.x, pt.y);
|
||||
}
|
||||
}
|
||||
else if (how_placed == POLYLABEL_PLACEMENT)
|
||||
{
|
||||
double precision = geometry::polylabel_precision(tranformed_poly, scale_factor_);
|
||||
geometry::point<double> pt = geometry::polylabel(tranformed_poly, precision);
|
||||
points_.emplace_back(pt.x, pt.y);
|
||||
geometry::point<double> pt;
|
||||
if (geometry::polylabel(tranformed_poly, precision, pt))
|
||||
{
|
||||
points_.emplace_back(pt.x, pt.y);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
43
test/unit/geometry/interior.cpp
Normal file
43
test/unit/geometry/interior.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#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("empty exterior ring") {
|
||||
|
||||
mapnik::geometry::polygon<double> poly;
|
||||
poly.emplace_back();
|
||||
|
||||
mapnik::geometry::point<double> pt;
|
||||
|
||||
CHECK(!mapnik::geometry::interior(poly, 1.0, pt));
|
||||
}
|
||||
|
||||
SECTION("interior of a square") {
|
||||
|
||||
mapnik::geometry::polygon<double> 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<double> pt{ -3, -3 };
|
||||
|
||||
CHECK(mapnik::geometry::interior(poly, 1.0, pt));
|
||||
CHECK(pt.x == Approx(0));
|
||||
CHECK(pt.y == Approx(0));
|
||||
}
|
||||
|
||||
}
|
|
@ -40,7 +40,7 @@ SECTION("empty inner ring") {
|
|||
proc.add_path(path);
|
||||
|
||||
REQUIRE(proc.polygon_.size() == 2);
|
||||
auto outer_ring = proc.polygon_.front();
|
||||
auto const& outer_ring = proc.polygon_.front();
|
||||
REQUIRE(outer_ring.size() == 5);
|
||||
|
||||
CHECK(outer_ring[0].x == Approx(-1));
|
||||
|
|
43
test/unit/geometry/polylabel.cpp
Normal file
43
test/unit/geometry/polylabel.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include "catch.hpp"
|
||||
|
||||
#include <mapnik/geometry/polylabel.hpp>
|
||||
|
||||
TEST_CASE("polylabel") {
|
||||
|
||||
SECTION("empty polygon") {
|
||||
|
||||
mapnik::geometry::polygon<double> poly;
|
||||
mapnik::geometry::point<double> pt;
|
||||
|
||||
CHECK(!mapnik::geometry::polylabel(poly, 1.0, pt));
|
||||
}
|
||||
|
||||
SECTION("empty exterior ring") {
|
||||
|
||||
mapnik::geometry::polygon<double> poly;
|
||||
poly.emplace_back();
|
||||
|
||||
mapnik::geometry::point<double> pt;
|
||||
|
||||
CHECK(!mapnik::geometry::polylabel(poly, 1.0, pt));
|
||||
}
|
||||
|
||||
SECTION("polylabel with a square") {
|
||||
|
||||
mapnik::geometry::polygon<double> 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<double> pt{ -3, -3 };
|
||||
|
||||
CHECK(mapnik::geometry::polylabel(poly, 1.0, pt));
|
||||
CHECK(pt.x == Approx(0));
|
||||
CHECK(pt.y == Approx(0));
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue