[geos-commits] [SCM] GEOS branch main updated. 1660b7931aa7c19461bd896206a3679e7961a922
git at osgeo.org
git at osgeo.org
Sun Jan 12 10:11:13 PST 2025
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GEOS".
The branch, main has been updated
via 1660b7931aa7c19461bd896206a3679e7961a922 (commit)
from 145b8d1f1517736e150a0828e5bd9078cb8a58e2 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 1660b7931aa7c19461bd896206a3679e7961a922
Author: Daniel Baston <dbaston at gmail.com>
Date: Sun Jan 12 13:10:54 2025 -0500
GeometryCollection: Cache flags to avoid O(n) lookups (#1220)
* Coverage union: add benchmark
* GeometryCollection: Cache properties to avoid O(n) lookups
diff --git a/benchmarks/operation/CMakeLists.txt b/benchmarks/operation/CMakeLists.txt
index 8df2a6bfd..0294a6eb3 100644
--- a/benchmarks/operation/CMakeLists.txt
+++ b/benchmarks/operation/CMakeLists.txt
@@ -19,4 +19,12 @@ if (benchmark_FOUND)
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/benchmarks>)
target_link_libraries(perf_distance PRIVATE
benchmark::benchmark geos geos_cxx_flags)
+
+ add_executable(perf_coverage_union CoverageUnionPerfTest.cpp)
+ target_include_directories(perf_coverage_union PUBLIC
+ $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
+ $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
+ $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/benchmarks>)
+ target_link_libraries(perf_coverage_union PRIVATE
+ benchmark::benchmark geos geos_cxx_flags)
endif()
diff --git a/benchmarks/operation/CoverageUnionPerfTest.cpp b/benchmarks/operation/CoverageUnionPerfTest.cpp
new file mode 100644
index 000000000..8cb957f12
--- /dev/null
+++ b/benchmarks/operation/CoverageUnionPerfTest.cpp
@@ -0,0 +1,65 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2025 Daniel Baston
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include <stdexcept>
+#include <benchmark/benchmark.h>
+
+#include <BenchmarkUtils.h>
+#include <geos/geom/Coordinate.h>
+#include <geos/geom/Geometry.h>
+#include <geos/coverage/CoverageUnion.h>
+#include <geos/operation/union/CoverageUnion.h>
+
+struct SegmentSet {
+ static void Union(const geos::geom::GeometryCollection& coll) {
+ geos::operation::geounion::CoverageUnion::Union(&coll);
+ }
+};
+
+struct BoundaryChain {
+ static void Union(const geos::geom::GeometryCollection& coll) {
+ auto result = geos::coverage::CoverageUnion::Union(&coll);
+ }
+};
+
+template<typename Impl>
+static void BM_CoverageUnion(benchmark::State& state) {
+ const auto& gfact = *geos::geom::GeometryFactory::getDefaultInstance();
+
+ auto nCells = state.range(0);
+
+ auto nx = static_cast<int>(std::ceil(std::sqrt(nCells)));
+ auto ny = static_cast<int>(std::ceil(std::sqrt(nCells)));
+
+ nCells = nx*ny;
+
+ geos::geom::Envelope env(0, nx, 0, ny);
+
+ auto cells = geos::benchmark::createGeometriesOnGrid(env, static_cast<std::size_t>(nCells), [&gfact](const auto& base) {
+ geos::geom::Envelope box(base.x, base.x + 1, base.y, base.y + 1);
+ return gfact.toGeometry(&box);
+ });
+
+ auto coll = gfact.createGeometryCollection(std::move(cells));
+
+ for (auto _ : state) {
+ Impl::Union(*coll);
+ }
+}
+
+BENCHMARK_TEMPLATE(BM_CoverageUnion, SegmentSet)->Range(1000, 1000000);
+BENCHMARK_TEMPLATE(BM_CoverageUnion, BoundaryChain)->Range(1000, 1000000);
+
+BENCHMARK_MAIN();
+
diff --git a/include/geos/geom/GeometryCollection.h b/include/geos/geom/GeometryCollection.h
index 9437f6af3..fd30f9204 100644
--- a/include/geos/geom/GeometryCollection.h
+++ b/include/geos/geom/GeometryCollection.h
@@ -198,6 +198,16 @@ public:
protected:
+ struct CollectionFlags {
+ bool flagsCalculated;
+ bool hasPoints;
+ bool hasLines;
+ bool hasPolygons;
+ bool hasM;
+ bool hasZ;
+ bool hasCurves;
+ };
+
GeometryCollection(const GeometryCollection& gc);
GeometryCollection& operator=(const GeometryCollection& gc);
@@ -236,6 +246,7 @@ protected:
};
std::vector<std::unique_ptr<Geometry>> geometries;
+ mutable CollectionFlags flags;
mutable Envelope envelope;
Envelope computeEnvelopeInternal() const;
@@ -248,6 +259,7 @@ protected:
bool hasCurvedComponents() const override;
+ void setFlags() const;
};
diff --git a/src/geom/GeometryCollection.cpp b/src/geom/GeometryCollection.cpp
index 80ecbccae..25877b81a 100644
--- a/src/geom/GeometryCollection.cpp
+++ b/src/geom/GeometryCollection.cpp
@@ -39,6 +39,7 @@ GeometryCollection::GeometryCollection(const GeometryCollection& gc)
:
Geometry(gc),
geometries(gc.geometries.size()),
+ flags(gc.flags),
envelope(gc.envelope)
{
for(std::size_t i = 0; i < geometries.size(); ++i) {
@@ -51,6 +52,7 @@ GeometryCollection::operator=(const GeometryCollection& gc)
{
geometries.resize(gc.geometries.size());
envelope = gc.envelope;
+ flags = gc.flags;
for (std::size_t i = 0; i < geometries.size(); i++) {
geometries[i] = gc.geometries[i]->clone();
@@ -62,7 +64,9 @@ GeometryCollection::operator=(const GeometryCollection& gc)
GeometryCollection::GeometryCollection(std::vector<std::unique_ptr<Geometry>> && newGeoms, const GeometryFactory& factory) :
Geometry(&factory),
geometries(std::move(newGeoms)),
- envelope(computeEnvelopeInternal()) {
+ flags{}, // set all flags to zero
+ envelope(computeEnvelopeInternal())
+{
if (hasNullElements(&geometries)) {
throw util::IllegalArgumentException("geometries must not contain null elements\n");
@@ -114,31 +118,69 @@ GeometryCollection::isEmpty() const
return true;
}
+void
+GeometryCollection::setFlags() const {
+ if (flags.flagsCalculated) {
+ return;
+ }
+
+ for (const auto& geom : geometries) {
+ flags.hasPoints |= geom->hasDimension(Dimension::P);
+ flags.hasLines |= geom->hasDimension(Dimension::L);
+ flags.hasPolygons |= geom->hasDimension(Dimension::A);
+ flags.hasM |= geom->hasM();
+ flags.hasZ |= geom->hasZ();
+ flags.hasCurves |= geom->hasCurvedComponents();
+ }
+
+ flags.flagsCalculated = true;
+}
+
Dimension::DimensionType
GeometryCollection::getDimension() const
{
- Dimension::DimensionType dimension = Dimension::False;
- for(const auto& g : geometries) {
- dimension = std::max(dimension, g->getDimension());
+ setFlags();
+
+ if (flags.hasPolygons) {
+ return Dimension::A;
}
- return dimension;
+ if (flags.hasLines) {
+ return Dimension::L;
+ }
+ if (flags.hasPoints) {
+ return Dimension::P;
+ }
+ return Dimension::False;
}
bool
GeometryCollection::isDimensionStrict(Dimension::DimensionType d) const {
- return std::all_of(geometries.begin(), geometries.end(),
- [&d](const std::unique_ptr<Geometry> & g) {
- return g->getDimension() == d;
- });
+ setFlags();
+
+ if (isEmpty()) {
+ return true;
+ }
+
+ switch(d) {
+ case Dimension::A: return flags.hasPolygons && !flags.hasLines && !flags.hasPoints;
+ case Dimension::L: return !flags.hasPolygons && flags.hasLines && !flags.hasPoints;
+ case Dimension::P: return !flags.hasPolygons && !flags.hasLines && flags.hasPoints;
+ default:
+ return false;
+ }
}
bool
GeometryCollection::hasDimension(Dimension::DimensionType d) const {
- return std::any_of(geometries.begin(),
- geometries.end(),
- [&d](const std::unique_ptr<Geometry>& g) {
- return g->hasDimension(d);
- });
+ setFlags();
+
+ switch (d) {
+ case Dimension:: A: return flags.hasPolygons;
+ case Dimension:: L: return flags.hasLines;
+ case Dimension:: P: return flags.hasPoints;
+ default:
+ return false;
+ }
}
int
@@ -165,23 +207,15 @@ GeometryCollection::getCoordinateDimension() const
bool
GeometryCollection::hasM() const
{
- for (const auto& g : geometries) {
- if (g->hasM()) {
- return true;
- }
- }
- return false;
+ setFlags();
+ return flags.hasM;
}
bool
GeometryCollection::hasZ() const
{
- for (const auto& g : geometries) {
- if (g->hasZ()) {
- return true;
- }
- }
- return false;
+ setFlags();
+ return flags.hasZ;
}
size_t
@@ -201,6 +235,7 @@ GeometryCollection::releaseGeometries()
{
auto ret = std::move(geometries);
geometryChanged();
+ flags.flagsCalculated = false;
return ret;
}
@@ -334,12 +369,8 @@ GeometryCollection::compareToSameClass(const Geometry* g) const
}
bool GeometryCollection::hasCurvedComponents() const {
- for (const auto& g : geometries) {
- if (g->hasCurvedComponents()) {
- return true;
- }
- }
- return false;
+ setFlags();
+ return flags.hasCurves;
}
const CoordinateXY*
-----------------------------------------------------------------------
Summary of changes:
benchmarks/operation/CMakeLists.txt | 8 +++
benchmarks/operation/CoverageUnionPerfTest.cpp | 65 ++++++++++++++++++
include/geos/geom/GeometryCollection.h | 12 ++++
src/geom/GeometryCollection.cpp | 95 +++++++++++++++++---------
4 files changed, 148 insertions(+), 32 deletions(-)
create mode 100644 benchmarks/operation/CoverageUnionPerfTest.cpp
hooks/post-receive
--
GEOS
More information about the geos-commits
mailing list