diff --git a/include/mapnik/geometry_centroid.hpp b/include/mapnik/geometry_centroid.hpp index 71d0c0754..8162d6ec6 100644 --- a/include/mapnik/geometry_centroid.hpp +++ b/include/mapnik/geometry_centroid.hpp @@ -26,6 +26,7 @@ #include #include #include +#include namespace mapnik { namespace geometry { @@ -63,30 +64,50 @@ struct geometry_centroid result_type operator() (line_string const& geom) const { + if (mapnik::geometry::is_empty(geom)) + { + return false; + } boost::geometry::centroid(geom, pt_); return true; } result_type operator() (polygon const& geom) const { + if (mapnik::geometry::is_empty(geom)) + { + return false; + } boost::geometry::centroid(geom, pt_); return true; } result_type operator() (multi_point const& geom) const { + if (mapnik::geometry::is_empty(geom)) + { + return false; + } boost::geometry::centroid(geom, pt_); return true; } result_type operator() (multi_line_string const& geom) const { + if (mapnik::geometry::is_empty(geom) || mapnik::geometry::has_empty(geom)) + { + return false; + } boost::geometry::centroid(geom, pt_); return true; } result_type operator() (multi_polygon const& geom) const { + if (mapnik::geometry::is_empty(geom) || mapnik::geometry::has_empty(geom)) + { + return false; + } boost::geometry::centroid(geom, pt_); return true; } diff --git a/include/mapnik/geometry_is_empty.hpp b/include/mapnik/geometry_is_empty.hpp index 5d017aa06..e6aa48822 100644 --- a/include/mapnik/geometry_is_empty.hpp +++ b/include/mapnik/geometry_is_empty.hpp @@ -79,6 +79,81 @@ struct geometry_is_empty }; +struct geometry_has_empty +{ + bool operator() (mapnik::geometry::geometry const& geom) const + { + return mapnik::util::apply_visitor(*this, geom); + } + + bool operator() (mapnik::geometry::geometry_empty const&) const + { + return false; + } + + bool operator() (mapnik::geometry::point const&) const + { + return false; + } + + bool operator() (mapnik::geometry::line_string const& geom) const + { + return false; + } + + bool operator() (mapnik::geometry::polygon const& geom) const + { + return false; + } + + bool operator() (mapnik::geometry::multi_point const& geom) const + { + return false; + } + + bool operator() (mapnik::geometry::multi_line_string const& geom) const + { + return test_multigeometry(geom); + } + + bool operator() (mapnik::geometry::multi_polygon const& geom) const + { + return test_multigeometry(geom); + } + + bool operator() (mapnik::geometry::geometry_collection const& geom) const + { + for (auto const & item : geom) + { + if (geometry_is_empty()(item) || (*this)(item)) + { + return true; + } + } + return false; + } + + template + bool operator() (T const& geom) const + { + return true; + } + +private: + template + bool test_multigeometry(T const & geom) const + { + for (auto const & item : geom) + { + if (item.empty()) + { + return true; + } + } + return false; + } +}; + } template @@ -87,6 +162,12 @@ inline bool is_empty(GeomType const& geom) return detail::geometry_is_empty()(geom); } +template +inline bool has_empty(GeomType const& geom) +{ + return detail::geometry_has_empty()(geom); +} + }} #endif // MAPNIK_GEOMETRY_IS_EMPTY_HPP diff --git a/test/unit/geometry/centroid.cpp b/test/unit/geometry/centroid.cpp new file mode 100644 index 000000000..5f080fffc --- /dev/null +++ b/test/unit/geometry/centroid.cpp @@ -0,0 +1,201 @@ +#include "catch.hpp" + +#include + +TEST_CASE("geometry centroid") { + +SECTION("empty geometry") { + + mapnik::geometry::geometry_empty geom; + mapnik::geometry::point centroid; + REQUIRE(!mapnik::geometry::centroid(geom, centroid)); +} + +SECTION("geometry collection") { + + mapnik::geometry::geometry_collection geom; + mapnik::geometry::point centroid; + REQUIRE(!mapnik::geometry::centroid(geom, centroid)); +} + +SECTION("point") { + + mapnik::geometry::point pt(10, 10); + mapnik::geometry::point centroid; + REQUIRE(mapnik::geometry::centroid(pt, centroid)); + REQUIRE(pt.x == centroid.x); + REQUIRE(pt.y == centroid.y); +} + +SECTION("linestring") { + + mapnik::geometry::line_string line; + line.add_coord(0, 0); + line.add_coord(25, 25); + line.add_coord(50, 50); + mapnik::geometry::point centroid; + REQUIRE(mapnik::geometry::centroid(line, centroid)); + REQUIRE(centroid.x == 25); + REQUIRE(centroid.y == 25); +} + +SECTION("empty linestring") { + + mapnik::geometry::line_string line; + mapnik::geometry::point centroid; + REQUIRE(!mapnik::geometry::centroid(line, centroid)); +} + +SECTION("polygon") { + + mapnik::geometry::polygon poly; + mapnik::geometry::linear_ring ring; + ring.add_coord(0, 0); + ring.add_coord(1, 0); + ring.add_coord(1, 1); + ring.add_coord(0, 1); + ring.add_coord(0, 0); + poly.set_exterior_ring(std::move(ring)); + + mapnik::geometry::point centroid; + REQUIRE(mapnik::geometry::centroid(poly, centroid)); + REQUIRE(centroid.x == 0.5); + REQUIRE(centroid.y == 0.5); +} + +SECTION("polygon with empty exterior ring") { + + mapnik::geometry::polygon poly; + mapnik::geometry::linear_ring ring; + poly.set_exterior_ring(std::move(ring)); + + mapnik::geometry::point centroid; + REQUIRE(!mapnik::geometry::centroid(poly, centroid)); +} + +SECTION("empty polygon") { + + mapnik::geometry::polygon poly; + mapnik::geometry::point centroid; + REQUIRE(!mapnik::geometry::centroid(poly, centroid)); +} + +SECTION("multi-point") { + + mapnik::geometry::multi_point geom; + geom.add_coord(0, 0); + geom.add_coord(25, 25); + geom.add_coord(50, 50); + mapnik::geometry::point centroid; + REQUIRE(mapnik::geometry::centroid(geom, centroid)); + REQUIRE(centroid.x == 25); + REQUIRE(centroid.y == 25); +} + +SECTION("empty multi-point") { + + mapnik::geometry::multi_point geom; + mapnik::geometry::point centroid; + REQUIRE(!mapnik::geometry::centroid(geom, centroid)); +} + +SECTION("multi-linestring") { + + mapnik::geometry::multi_line_string geom; + { + mapnik::geometry::line_string line; + line.add_coord(0, 0); + line.add_coord(0, 25); + line.add_coord(0, 50); + geom.emplace_back(std::move(line)); + } + { + mapnik::geometry::line_string line; + line.add_coord(0, 0); + line.add_coord(25, 0); + line.add_coord(50, 0); + geom.emplace_back(std::move(line)); + } + mapnik::geometry::point centroid; + REQUIRE(mapnik::geometry::centroid(geom, centroid)); + REQUIRE(centroid.x == 12.5); + REQUIRE(centroid.y == 12.5); +} + +SECTION("multi-linestring: one component empty") { + + mapnik::geometry::multi_line_string geom; + mapnik::geometry::line_string line; + line.add_coord(0, 0); + line.add_coord(0, 25); + line.add_coord(0, 50); + geom.emplace_back(std::move(line)); + geom.emplace_back(); + mapnik::geometry::point centroid; + REQUIRE(!mapnik::geometry::centroid(geom, centroid)); +} + +SECTION("empty multi-linestring") { + + mapnik::geometry::multi_line_string geom; + mapnik::geometry::point centroid; + REQUIRE(!mapnik::geometry::centroid(geom, centroid)); +} + +SECTION("multi-polygon") { + + mapnik::geometry::multi_polygon geom; + { + mapnik::geometry::polygon poly; + mapnik::geometry::linear_ring ring; + ring.add_coord(0, 0); + ring.add_coord(1, 0); + ring.add_coord(1, 1); + ring.add_coord(0, 1); + ring.add_coord(0, 0); + poly.set_exterior_ring(std::move(ring)); + geom.emplace_back(std::move(poly)); + } + { + mapnik::geometry::polygon poly; + mapnik::geometry::linear_ring ring; + ring.add_coord(1, 1); + ring.add_coord(2, 1); + ring.add_coord(2, 2); + ring.add_coord(1, 2); + ring.add_coord(1, 1); + poly.set_exterior_ring(std::move(ring)); + geom.emplace_back(std::move(poly)); + } + + mapnik::geometry::point centroid; + REQUIRE(mapnik::geometry::centroid(geom, centroid)); + REQUIRE(centroid.x == 1); + REQUIRE(centroid.y == 1); +} + +SECTION("multi-polygon: one component empty") { + + mapnik::geometry::multi_polygon geom; + mapnik::geometry::polygon poly; + mapnik::geometry::linear_ring ring; + ring.add_coord(0, 0); + ring.add_coord(1, 0); + ring.add_coord(1, 1); + ring.add_coord(0, 1); + ring.add_coord(0, 0); + poly.set_exterior_ring(std::move(ring)); + geom.emplace_back(std::move(poly)); + geom.emplace_back(); + + mapnik::geometry::point centroid; + REQUIRE(!mapnik::geometry::centroid(geom, centroid)); +} + +SECTION("empty multi-polygon") { + + mapnik::geometry::multi_polygon geom; + mapnik::geometry::point centroid; + REQUIRE(!mapnik::geometry::centroid(geom, centroid)); +} +} diff --git a/test/unit/geometry/has_empty.cpp b/test/unit/geometry/has_empty.cpp new file mode 100644 index 000000000..a240c6f9b --- /dev/null +++ b/test/unit/geometry/has_empty.cpp @@ -0,0 +1,150 @@ +#include "catch.hpp" + +#include + +TEST_CASE("geometry has_empty") { + +SECTION("empty geometry") { + + mapnik::geometry::geometry_empty geom; + REQUIRE(!mapnik::geometry::has_empty(geom)); +} + +SECTION("geometry collection") { + + { + mapnik::geometry::geometry_collection geom; + REQUIRE(!mapnik::geometry::has_empty(geom)); + } + { + mapnik::geometry::geometry_collection geom; + mapnik::geometry::geometry_empty geom1; + geom.emplace_back(std::move(geom1)); + REQUIRE(mapnik::geometry::has_empty(geom)); + } + { + mapnik::geometry::geometry_collection geom; + mapnik::geometry::multi_line_string mls; + mapnik::geometry::line_string line; + mls.emplace_back(std::move(line)); + geom.emplace_back(std::move(mls)); + REQUIRE(mapnik::geometry::has_empty(geom)); + } + { + mapnik::geometry::geometry_collection geom; + mapnik::geometry::multi_line_string mls; + mapnik::geometry::line_string line; + line.add_coord(0, 0); + mls.emplace_back(std::move(line)); + geom.emplace_back(std::move(mls)); + REQUIRE(!mapnik::geometry::has_empty(geom)); + } +} + +SECTION("point") { + + mapnik::geometry::point pt(10, 10); + REQUIRE(!mapnik::geometry::has_empty(pt)); +} + +SECTION("linestring") { + + { + mapnik::geometry::line_string line; + REQUIRE(!mapnik::geometry::has_empty(line)); + } + { + mapnik::geometry::line_string line; + line.add_coord(0, 0); + line.add_coord(25, 25); + line.add_coord(50, 50); + REQUIRE(!mapnik::geometry::has_empty(line)); + } +} + +SECTION("polygon") { + + { + mapnik::geometry::polygon poly; + REQUIRE(!mapnik::geometry::has_empty(poly)); + } + { + mapnik::geometry::polygon poly; + mapnik::geometry::linear_ring ring; + poly.set_exterior_ring(std::move(ring)); + REQUIRE(!mapnik::geometry::has_empty(poly)); + } + { + mapnik::geometry::polygon poly; + mapnik::geometry::linear_ring ring; + ring.add_coord(0, 0); + ring.add_coord(1, 0); + ring.add_coord(1, 1); + ring.add_coord(0, 1); + ring.add_coord(0, 0); + poly.set_exterior_ring(std::move(ring)); + REQUIRE(!mapnik::geometry::has_empty(poly)); + } +} + +SECTION("multi-point") { + + { + mapnik::geometry::multi_point geom; + REQUIRE(!mapnik::geometry::has_empty(geom)); + } + { + mapnik::geometry::multi_point geom; + geom.add_coord(0, 0); + geom.add_coord(25, 25); + geom.add_coord(50, 50); + REQUIRE(!mapnik::geometry::has_empty(geom)); + } +} + +SECTION("multi-linestring") { + + { + mapnik::geometry::multi_line_string geom; + REQUIRE(!mapnik::geometry::has_empty(geom)); + } + { + mapnik::geometry::multi_line_string geom; + mapnik::geometry::line_string line; + geom.emplace_back(std::move(line)); + REQUIRE(mapnik::geometry::has_empty(geom)); + } + { + mapnik::geometry::multi_line_string geom; + mapnik::geometry::line_string line; + line.add_coord(0, 0); + geom.emplace_back(std::move(line)); + REQUIRE(!mapnik::geometry::has_empty(geom)); + } +} + +SECTION("multi-polygon") { + + { + mapnik::geometry::multi_polygon geom; + REQUIRE(!mapnik::geometry::has_empty(geom)); + } + { + mapnik::geometry::multi_polygon geom; + mapnik::geometry::polygon poly; + mapnik::geometry::linear_ring ring; + poly.set_exterior_ring(std::move(ring)); + geom.emplace_back(std::move(poly)); + REQUIRE(mapnik::geometry::has_empty(geom)); + } + { + mapnik::geometry::multi_polygon geom; + mapnik::geometry::polygon poly; + mapnik::geometry::linear_ring ring; + ring.add_coord(0, 0); + poly.set_exterior_ring(std::move(ring)); + geom.emplace_back(std::move(poly)); + REQUIRE(!mapnik::geometry::has_empty(geom)); + } +} +} diff --git a/test/unit/geometry/is_empty.cpp b/test/unit/geometry/is_empty.cpp new file mode 100644 index 000000000..bb6a6349b --- /dev/null +++ b/test/unit/geometry/is_empty.cpp @@ -0,0 +1,117 @@ +#include "catch.hpp" + +#include + +TEST_CASE("geometry is_empty") { + +SECTION("empty geometry") { + + mapnik::geometry::geometry_empty geom; + REQUIRE(mapnik::geometry::is_empty(geom)); +} + +SECTION("geometry collection") { + + { + mapnik::geometry::geometry_collection geom; + REQUIRE(mapnik::geometry::is_empty(geom)); + } + { + mapnik::geometry::geometry_collection geom; + mapnik::geometry::geometry_empty geom1; + geom.emplace_back(std::move(geom1)); + REQUIRE(!mapnik::geometry::is_empty(geom)); + } +} + +SECTION("point") { + + mapnik::geometry::point pt(10, 10); + REQUIRE(!mapnik::geometry::is_empty(pt)); +} + +SECTION("linestring") { + + { + mapnik::geometry::line_string line; + REQUIRE(mapnik::geometry::is_empty(line)); + } + { + mapnik::geometry::line_string line; + line.add_coord(0, 0); + line.add_coord(25, 25); + line.add_coord(50, 50); + REQUIRE(!mapnik::geometry::is_empty(line)); + } +} + +SECTION("polygon") { + + { + mapnik::geometry::polygon poly; + REQUIRE(mapnik::geometry::is_empty(poly)); + } + { + mapnik::geometry::polygon poly; + mapnik::geometry::linear_ring ring; + poly.set_exterior_ring(std::move(ring)); + REQUIRE(mapnik::geometry::is_empty(poly)); + } + { + mapnik::geometry::polygon poly; + mapnik::geometry::linear_ring ring; + ring.add_coord(0, 0); + ring.add_coord(1, 0); + ring.add_coord(1, 1); + ring.add_coord(0, 1); + ring.add_coord(0, 0); + poly.set_exterior_ring(std::move(ring)); + REQUIRE(!mapnik::geometry::is_empty(poly)); + } +} + +SECTION("multi-point") { + + { + mapnik::geometry::multi_point geom; + REQUIRE(mapnik::geometry::is_empty(geom)); + } + { + mapnik::geometry::multi_point geom; + geom.add_coord(0, 0); + geom.add_coord(25, 25); + geom.add_coord(50, 50); + REQUIRE(!mapnik::geometry::is_empty(geom)); + } +} + +SECTION("multi-linestring") { + + { + mapnik::geometry::multi_line_string geom; + REQUIRE(mapnik::geometry::is_empty(geom)); + } + { + mapnik::geometry::multi_line_string geom; + mapnik::geometry::line_string line; + geom.emplace_back(std::move(line)); + REQUIRE(!mapnik::geometry::is_empty(geom)); + } +} + +SECTION("multi-polygon") { + + { + mapnik::geometry::multi_polygon geom; + REQUIRE(mapnik::geometry::is_empty(geom)); + } + { + mapnik::geometry::multi_polygon geom; + mapnik::geometry::polygon poly; + mapnik::geometry::linear_ring ring; + poly.set_exterior_ring(std::move(ring)); + geom.emplace_back(std::move(poly)); + REQUIRE(!mapnik::geometry::is_empty(geom)); + } +} +} diff --git a/test/unit/geometry/label_algo_test.cpp b/test/unit/geometry/label_algo_test.cpp deleted file mode 100644 index 1c315caa4..000000000 --- a/test/unit/geometry/label_algo_test.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "catch.hpp" - -#include -#include -#include -#include - -TEST_CASE("labeling") { - -SECTION("algorithms") { - - // reused these for simplicity - mapnik::geometry::point centroid; - { - // single point - mapnik::geometry::point pt(10,10); - REQUIRE( mapnik::geometry::centroid(pt, centroid)); - REQUIRE( pt.x == centroid.x); - REQUIRE( pt.y == centroid.y); - } - - // linestring with three consecutive verticies - { - mapnik::geometry::line_string line; - line.add_coord(0, 0); - line.add_coord(25, 25); - line.add_coord(50, 50); - REQUIRE(mapnik::geometry::centroid(line, centroid)); - REQUIRE( centroid.x == 25 ); - REQUIRE( centroid.y == 25 ); - } -} -}