From 159f8341776c76828ce82f8890748d3b6b95dded Mon Sep 17 00:00:00 2001 From: Jochen Topf Date: Sun, 25 Sep 2022 12:16:15 +0200 Subject: [PATCH 1/2] Add support for area() on geometry collections Simply add up the area of all member geometries. --- src/geom-functions.cpp | 4 ++++ tests/test-geom-collections.cpp | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/geom-functions.cpp b/src/geom-functions.cpp index 7bf9917d4..1854d0489 100644 --- a/src/geom-functions.cpp +++ b/src/geom-functions.cpp @@ -373,6 +373,10 @@ double area(geometry_t const &geom) for (auto const &polygon : geom.get()) { total += get_polygon_area(polygon); } + } else if (geom.is_collection()) { + for (auto const &sgeom : geom.get()) { + total += area(sgeom); + } } return std::abs(total); diff --git a/tests/test-geom-collections.cpp b/tests/test-geom-collections.cpp index a7a91a981..ae4ce33b9 100644 --- a/tests/test-geom-collections.cpp +++ b/tests/test-geom-collections.cpp @@ -72,6 +72,22 @@ TEST_CASE("geometry collection with several geometries", "[NoDB]") REQUIRE(geometry_n(geom, 3) == geom::geometry_t{geom::point_t{2, 2}}); } +TEST_CASE("geometry collection with polygon", "[NoDB]") +{ + geom::geometry_t geom{geom::collection_t{}}; + auto &c = geom.get(); + + c.add_geometry(geom::geometry_t{geom::point_t{1, 1}}); + c.add_geometry(geom::geometry_t{ + geom::polygon_t{geom::ring_t{{1, 1}, {1, 2}, {2, 2}, {2, 1}, {1, 1}}}}); + + REQUIRE(geometry_type(geom) == "GEOMETRYCOLLECTION"); + REQUIRE(num_geometries(geom) == 2); + REQUIRE(area(geom) == Approx(1.0)); + REQUIRE(length(geom) == Approx(0.0)); + REQUIRE(centroid(geom) == geom::geometry_t{geom::point_t{1.5, 1.5}}); +} + TEST_CASE("create_collection from OSM data", "[NoDB]") { test_buffer_t buffer; From 6327a635bd38e24b790fa75b53ecde631cd6a2c1 Mon Sep 17 00:00:00 2001 From: Jochen Topf Date: Sun, 25 Sep 2022 12:43:44 +0200 Subject: [PATCH 2/2] Use area() implementation from boost::geometry --- src/geom-functions.cpp | 56 +++++++++--------------------------------- 1 file changed, 12 insertions(+), 44 deletions(-) diff --git a/src/geom-functions.cpp b/src/geom-functions.cpp index 1854d0489..250798834 100644 --- a/src/geom-functions.cpp +++ b/src/geom-functions.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -334,52 +335,19 @@ geometry_t segmentize(geometry_t const &input, double max_segment_length) return output; } -static double get_ring_area(ring_t const &ring) noexcept -{ - assert(ring.size() > 3); - - double total = 0.0; - auto it = ring.begin(); - auto prev = *it++; - - while (it != ring.end()) { - auto const cur = *it; - total += prev.x() * cur.y() - cur.x() * prev.y(); - prev = cur; - ++it; - } - - return total; -} - -static double get_polygon_area(polygon_t const &polygon) -{ - double total = get_ring_area(polygon.outer()); - - for (auto const &ring : polygon.inners()) { - total += get_ring_area(ring); - } - - return total * 0.5; -} - double area(geometry_t const &geom) { - double total = 0.0; - - if (geom.is_polygon()) { - total = get_polygon_area(geom.get()); - } else if (geom.is_multipolygon()) { - for (auto const &polygon : geom.get()) { - total += get_polygon_area(polygon); - } - } else if (geom.is_collection()) { - for (auto const &sgeom : geom.get()) { - total += area(sgeom); - } - } - - return std::abs(total); + return std::abs(geom.visit( + overloaded{[&](geom::nullgeom_t const & /*input*/) { return 0.0; }, + [&](geom::collection_t const &input) { + return std::accumulate(input.cbegin(), input.cend(), 0.0, + [](double sum, auto const &geom) { + return sum + area(geom); + }); + }, + [&](auto const &input) { + return static_cast(boost::geometry::area(input)); + }})); } double length(geometry_t const &geom)