From d8a9745433fd6129c997824059cbd55913c6dc5b Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 30 May 2025 13:00:35 +0200 Subject: [PATCH 1/7] CoverageSimplify: add progress callback and add GEOSCoverageSimplifyVWWithProgress_r() --- capi/geos_c.cpp | 10 +++ capi/geos_c.h.in | 63 ++++++++++++++++++- capi/geos_ts_c.cpp | 24 ++++++- include/geos/coverage/CoverageSimplifier.h | 24 ++++--- include/geos/coverage/TPVWSimplifier.h | 11 +++- include/geos/util/Progress.h | 63 +++++++++++++++++++ src/coverage/CoverageSimplifier.cpp | 37 ++++++----- src/coverage/TPVWSimplifier.cpp | 23 +++++-- src/util/Progress.cpp | 37 +++++++++++ tests/unit/capi/GEOSCoverageSimplifyTest.cpp | 16 ++++- .../unit/coverage/CoverageSimplifierTest.cpp | 18 ++++-- tests/unit/coverage/TPVWSimplifierTest.cpp | 6 +- util/geosop/GeometryOp.cpp | 2 +- 13 files changed, 290 insertions(+), 44 deletions(-) create mode 100644 include/geos/util/Progress.h create mode 100644 src/util/Progress.cpp diff --git a/capi/geos_c.cpp b/capi/geos_c.cpp index 9bd1068ba4..fb0a3f0c0a 100644 --- a/capi/geos_c.cpp +++ b/capi/geos_c.cpp @@ -2061,5 +2061,15 @@ extern "C" { return GEOSCoverageSimplifyVW_r(handle, input, tolerance, preserveBoundary); } + Geometry* + GEOSCoverageSimplifyVWWithProgress(const Geometry* input, double tolerance, + int preserveBoundary, + GEOSProgressCallback_r progressFunc, + void* progressUserData) + { + return GEOSCoverageSimplifyVWWithProgress_r( + handle, input, tolerance, preserveBoundary, progressFunc, progressUserData); + } + } /* extern "C" */ diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in index 39671cc696..07a2f51a6f 100644 --- a/capi/geos_c.h.in +++ b/capi/geos_c.h.in @@ -117,6 +117,23 @@ typedef void (*GEOSMessageHandler)(GEOS_PRINTF_FORMAT const char *fmt, ...) */ typedef void (*GEOSMessageHandler_r)(const char *message, void *userdata); + +/** + * A GEOS progression and cancel function. + * + * Such function takes a progression ratio, and an optional + * message, and returns true if computation must continue, or false if it + * must be interrupted. + * + * \param progressRatio Progression ratio (between 0 and 1) + * \param message Information message (can be NULL) + * \param userdata the user data pointer that was passed to GEOS together with + * this callback. + * \return TRUE if processing must continue, or FALSE to interrupt it. + */ +typedef int (*GEOSProgressCallback_r)(double progressRatio, const char* message, void* userdata); + + /* * When we're included by geos_c.cpp, these types are #defined to the * C++ definitions via preprocessor. We don't touch them to allow the @@ -886,6 +903,16 @@ GEOSCoverageSimplifyVW_r( double tolerance, int preserveBoundary); +/** \see GEOSCoverageSimplifyVWWithProgress */ +extern GEOSGeometry GEOS_DLL * +GEOSCoverageSimplifyVWWithProgress_r( + GEOSContextHandle_t extHandle, + const GEOSGeometry* input, + double tolerance, + int preserveBoundary, + GEOSProgressCallback_r progressFunc, + void* progressUserData); + /** \see GEOSCoverageCleanParams_create */ extern GEOSCoverageCleanParams GEOS_DLL * GEOSCoverageCleanParams_create_r( @@ -4321,6 +4348,40 @@ extern GEOSGeometry GEOS_DLL * GEOSCoverageSimplifyVW( double tolerance, int preserveBoundary); +/** +* Operates on a coverage (represented as a list of polygonal geometry +* with exactly matching edge geometry) to apply a Visvalingam–Whyatt +* simplification to the edges, reducing complexity in proportion with +* the provided tolerance, while retaining a valid coverage (no edges +* will cross or touch after the simplification). +* Geometries never disappear, but they may be simplified down to just +* a triangle. Also, some invalid geoms (such as Polygons which have too +* few non-repeated points) will be returned unchanged. +* If the input dataset is not a valid coverage due to overlaps, +* it will still be simplified, but invalid topology such as crossing +* edges will still be invalid. +* +* \param input The polygonal coverage to access, +* stored in a geometry collection. All members must be POLYGON +* or MULTIPOLYGON. +* \param tolerance A tolerance parameter in linear units. +* \param preserveBoundary Use 1 to preserve the outside edges +* of the coverage without simplification, +* 0 to allow them to be simplified. +* \param progressFunc Progress callback (or null) +* \param progressUserData User data passed to progress callback (can be null) +* \return A collection containing the simplified geometries, or null +* on error. +* +* \since 3.14 +*/ +extern GEOSGeometry GEOS_DLL * GEOSCoverageSimplifyVWWithProgress( + const GEOSGeometry* input, + double tolerance, + int preserveBoundary, + GEOSProgressCallback_r progressFunc, + void* progressUserData); + /** * Create a default GEOSCoverageCleanParams object for controlling * the way invalid polygon interactions are repaird by \ref GEOSCoverageCleanWithParams. @@ -5328,7 +5389,7 @@ extern char GEOS_DLL GEOSIntersects(const GEOSGeometry* g1, const GEOSGeometry* extern char GEOS_DLL GEOSCrosses(const GEOSGeometry* g1, const GEOSGeometry* g2); /** -* Tests if geometry g1 is completely within g2, +* Tests if geometry g1 is completely within g2, * but not wholly contained in the boundary of g2. * \param g1 Input geometry * \param g2 Input geometry diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp index 27ee5b8123..100912367d 100644 --- a/capi/geos_ts_c.cpp +++ b/capi/geos_ts_c.cpp @@ -114,6 +114,7 @@ #include #include #include +#include #include // This should go away @@ -4522,6 +4523,25 @@ extern "C" { double tolerance, int preserveBoundary) { + return GEOSCoverageSimplifyVWWithProgress_r(extHandle, input, tolerance, + preserveBoundary, + nullptr, nullptr); + } + + Geometry* + GEOSCoverageSimplifyVWWithProgress_r(GEOSContextHandle_t extHandle, + const Geometry* input, + double tolerance, + int preserveBoundary, + GEOSProgressCallback_r progressFunc, + void* progressUserData) + { + geos::util::ProgressFunction progressFunction = + [progressFunc, progressUserData](double progress, const char* message) + { + return progressFunc(progress, message, progressUserData); + }; + using geos::coverage::CoverageSimplifier; return execute(extHandle, [&]() -> Geometry* { @@ -4536,10 +4556,10 @@ extern "C" { CoverageSimplifier cov(coverage); std::vector> simple; if (preserveBoundary == 1) { - simple = cov.simplifyInner(tolerance); + simple = cov.simplifyInner(tolerance, progressFunc ? &progressFunction : nullptr); } else if (preserveBoundary == 0) { - simple = cov.simplify(tolerance); + simple = cov.simplify(tolerance, progressFunc ? &progressFunction : nullptr); } else return nullptr; diff --git a/include/geos/coverage/CoverageSimplifier.h b/include/geos/coverage/CoverageSimplifier.h index 74df234dda..f3c1c21c42 100644 --- a/include/geos/coverage/CoverageSimplifier.h +++ b/include/geos/coverage/CoverageSimplifier.h @@ -19,6 +19,7 @@ #include #include #include +#include namespace geos { @@ -87,15 +88,18 @@ class GEOS_DLL CoverageSimplifier { * * @param coverage a set of polygonal geometries forming a coverage * @param tolerance the simplification tolerance + * @param progressFunction Progress function, or nullptr. * @return the simplified polygons */ static std::vector> simplify( std::vector& coverage, - double tolerance); + double tolerance, + geos::util::ProgressFunction* progressFunction); static std::vector> simplify( const std::vector>& coverage, - double tolerance); + double tolerance, + geos::util::ProgressFunction* progressFunction); /** * Simplifies the inner boundaries of a set of polygonal geometries forming a coverage, @@ -104,24 +108,28 @@ class GEOS_DLL CoverageSimplifier { * * @param coverage a set of polygonal geometries forming a coverage * @param tolerance the simplification tolerance + * @param progressFunction Progress function, or nullptr. * @return the simplified polygons */ static std::vector> simplifyInner( std::vector& coverage, - double tolerance); + double tolerance, + geos::util::ProgressFunction* progressFunction); static std::vector> simplifyInner( const std::vector>& coverage, - double tolerance); + double tolerance, + geos::util::ProgressFunction* progressFunction); /** * Computes the simplified coverage, preserving the coverage topology. * * @param tolerance the simplification tolerance + * @param progressFunction Progress function, or nullptr. * @return the simplified polygons */ std::vector> simplify( - double tolerance); + double tolerance, geos::util::ProgressFunction* progressFunction); /** * Computes the inner-boundary simplified coverage, @@ -129,10 +137,11 @@ class GEOS_DLL CoverageSimplifier { * and leaving outer boundary edges unchanged. * * @param tolerance the simplification tolerance + * @param progressFunction Progress function, or nullptr. * @return the simplified polygons */ std::vector> simplifyInner( - double tolerance); + double tolerance, geos::util::ProgressFunction* progressFunction); private: @@ -145,7 +154,8 @@ class GEOS_DLL CoverageSimplifier { void simplifyEdges( std::vector edges, const MultiLineString* constraints, - double tolerance); + double tolerance, + geos::util::ProgressFunction* progressFunction); void setCoordinates( std::vector& edges, diff --git a/include/geos/coverage/TPVWSimplifier.h b/include/geos/coverage/TPVWSimplifier.h index 0b904c8a83..272d32458c 100644 --- a/include/geos/coverage/TPVWSimplifier.h +++ b/include/geos/coverage/TPVWSimplifier.h @@ -21,6 +21,7 @@ #include #include #include +#include namespace geos { @@ -164,11 +165,13 @@ class GEOS_DLL TPVWSimplifier * * @param lines the lines to simplify * @param distanceTolerance the simplification tolerance + * @param progressFunction Progress function, or nullptr. * @return the simplified lines */ static std::unique_ptr simplify( const MultiLineString* lines, - double distanceTolerance); + double distanceTolerance, + geos::util::ProgressFunction* progressFunction); /** * Simplifies a set of lines, preserving the topology of the lines between @@ -181,13 +184,15 @@ class GEOS_DLL TPVWSimplifier * @param freeRings flags indicating which ring edges do not have node endpoints * @param constraintLines the linear constraints * @param distanceTolerance the simplification tolerance + * @param progressFunction Progress function, or nullptr. * @return the simplified lines */ static std::unique_ptr simplify( const MultiLineString* lines, std::vector& freeRings, const MultiLineString* constraintLines, - double distanceTolerance); + double distanceTolerance, + geos::util::ProgressFunction* progressFunction); // Constructor TPVWSimplifier(const MultiLineString* lines, @@ -209,7 +214,7 @@ class GEOS_DLL TPVWSimplifier void setConstraints(const MultiLineString* constraints); - std::unique_ptr simplify(); + std::unique_ptr simplify(geos::util::ProgressFunction* progressFunction); std::vector createEdges( const MultiLineString* lines, diff --git a/include/geos/util/Progress.h b/include/geos/util/Progress.h new file mode 100644 index 0000000000..179e090661 --- /dev/null +++ b/include/geos/util/Progress.h @@ -0,0 +1,63 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2025 Even Rouault + * + * 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. + * + **********************************************************************/ + +#pragma once + +#include + +#include + +namespace geos { +namespace util { // geos::util + +/** Signature of a progression and cancel function. + * + * Such function takes a progression ratio (between 0 and 1), and an optional + * message, and returns true if computation must continue, or false if it + * must be interrupted. + */ +typedef std::function ProgressFunction; + +/** Do progress function related processing for an iteration loop of iterCount iterations. + * + * This function will invoke (*progressFunction) every notificationInterval + *iteration. + * + * This function will return, or throw a geos::util::InterruptedException. + * + * A typical use is: + * \code + * const size_t notificationInterval = std::max(1, iterCount / 100); + * for (size_t i = 0, iNotify = 0; i < iterCount; ++i) { + * // do something useful + * if (progressFunction) { + * geos::util::ProgressFunctionIteration(*progressFunction, i, iterCount, iNotify, notificationInterval); + * } + * } + * if (progressFunction) { + * (*progressFunction)(1.0, nullptr); + * } + * \endcode + * + * @param progressFunction Progress function + * @param i Current index of loop iteration. + * @param iterCount Total number of loop iteration. + * @param iNotify Notification counter, updated by this function. Must be set by the caller (before the loop) to 0. + * @param notificationInterval Notification interval (e.g. iterCount / 100). + */ +void ProgressFunctionIteration(ProgressFunction& progressFunction, size_t i, size_t iterCount, size_t& iNotify, size_t notificationInterval); + +} // namespace geos::util +} // namespace geos + diff --git a/src/coverage/CoverageSimplifier.cpp b/src/coverage/CoverageSimplifier.cpp index 98ba1ad877..774125001f 100644 --- a/src/coverage/CoverageSimplifier.cpp +++ b/src/coverage/CoverageSimplifier.cpp @@ -26,7 +26,7 @@ using geos::geom::Geometry; using geos::geom::GeometryFactory; using geos::geom::MultiLineString; - +using geos::util::ProgressFunction; namespace geos { // geos namespace coverage { // geos.coverage @@ -35,23 +35,25 @@ namespace coverage { // geos.coverage std::vector> CoverageSimplifier::simplify( std::vector& coverage, - double tolerance) + double tolerance, + ProgressFunction* progressFunction) { CoverageSimplifier simplifier(coverage); - return simplifier.simplify(tolerance); + return simplifier.simplify(tolerance, progressFunction); } /* public static */ std::vector> CoverageSimplifier::simplify( const std::vector>& coverage, - double tolerance) + double tolerance, + ProgressFunction* progressFunction) { std::vector geoms; for (auto& geom : coverage) { geoms.push_back(geom.get()); } - return simplify(geoms, tolerance); + return simplify(geoms, tolerance, progressFunction); } @@ -59,10 +61,11 @@ CoverageSimplifier::simplify( std::vector> CoverageSimplifier::simplifyInner( std::vector& coverage, - double tolerance) + double tolerance, + ProgressFunction* progressFunction) { CoverageSimplifier simplifier(coverage); - return simplifier.simplifyInner(tolerance); + return simplifier.simplifyInner(tolerance, progressFunction); } @@ -70,13 +73,14 @@ CoverageSimplifier::simplifyInner( std::vector> CoverageSimplifier::simplifyInner( const std::vector>& coverage, - double tolerance) + double tolerance, + ProgressFunction* progressFunction) { std::vector geoms; for (auto& geom : coverage) { geoms.push_back(geom.get()); } - return simplifyInner(geoms, tolerance); + return simplifyInner(geoms, tolerance, progressFunction); } @@ -94,23 +98,25 @@ CoverageSimplifier::CoverageSimplifier(const std::vector& cover /* public */ std::vector> -CoverageSimplifier::simplify(double tolerance) +CoverageSimplifier::simplify(double tolerance, + ProgressFunction* progressFunction) { CoverageRingEdges cov(m_input); - simplifyEdges(cov.getEdges(), nullptr, tolerance); + simplifyEdges(cov.getEdges(), nullptr, tolerance, progressFunction); return cov.buildCoverage(); } /* public */ std::vector> -CoverageSimplifier::simplifyInner(double tolerance) +CoverageSimplifier::simplifyInner(double tolerance, + ProgressFunction* progressFunction) { CoverageRingEdges cov(m_input); std::vector innerEdges = cov.selectEdges(2); std::vector outerEdges = cov.selectEdges(1); std::unique_ptr constraintEdges = CoverageEdge::createLines(outerEdges, m_geomFactory); - simplifyEdges(innerEdges, constraintEdges.get(), tolerance); + simplifyEdges(innerEdges, constraintEdges.get(), tolerance, progressFunction); return cov.buildCoverage(); } @@ -119,11 +125,12 @@ void CoverageSimplifier::simplifyEdges( std::vector edges, const MultiLineString* constraints, - double tolerance) + double tolerance, + ProgressFunction* progressFunction) { std::unique_ptr lines = CoverageEdge::createLines(edges, m_geomFactory); std::vector freeRings = getFreeRings(edges); - std::unique_ptr linesSimp = TPVWSimplifier::simplify(lines.get(), freeRings, constraints, tolerance); + std::unique_ptr linesSimp = TPVWSimplifier::simplify(lines.get(), freeRings, constraints, tolerance, progressFunction); //Assert: mlsSimp.getNumGeometries = edges.length setCoordinates(edges, linesSimp.get()); diff --git a/src/coverage/TPVWSimplifier.cpp b/src/coverage/TPVWSimplifier.cpp index 0bf36ac303..a68a3753ee 100644 --- a/src/coverage/TPVWSimplifier.cpp +++ b/src/coverage/TPVWSimplifier.cpp @@ -47,10 +47,11 @@ typedef TPVWSimplifier::EdgeIndex EdgeIndex; std::unique_ptr TPVWSimplifier::simplify( const MultiLineString* lines, - double distanceTolerance) + double distanceTolerance, + geos::util::ProgressFunction* progressFunction) { TPVWSimplifier simp(lines, distanceTolerance); - std::unique_ptr result = simp.simplify(); + std::unique_ptr result = simp.simplify(progressFunction); return result; } @@ -61,12 +62,13 @@ TPVWSimplifier::simplify( const MultiLineString* p_lines, std::vector& p_freeRings, const MultiLineString* p_constraintLines, - double distanceTolerance) + double distanceTolerance, + geos::util::ProgressFunction* progressFunction) { TPVWSimplifier simp(p_lines, distanceTolerance); simp.setFreeRingIndices(p_freeRings); simp.setConstraints(p_constraintLines); - std::unique_ptr result = simp.simplify(); + std::unique_ptr result = simp.simplify(progressFunction); return result; } @@ -99,7 +101,7 @@ TPVWSimplifier::setFreeRingIndices(std::vector& freeRing) /* private */ std::unique_ptr -TPVWSimplifier::simplify() +TPVWSimplifier::simplify(geos::util::ProgressFunction* progressFunction) { std::vector emptyList; std::vector edges = createEdges(inputLines, isFreeRing); @@ -110,10 +112,19 @@ TPVWSimplifier::simplify() edgeIndex.add(constraintEdges); std::vector> result; - for (auto& edge : edges) { + const size_t iterCount = edges.size(); + const size_t notificationInterval = std::max(1, iterCount / 100); + for (size_t i = 0, iNotify = 0; i < iterCount; ++i) { + auto& edge = edges[i]; std::unique_ptr ptsSimp = edge.simplify(edgeIndex); auto ls = geomFactory->createLineString(std::move(ptsSimp)); result.emplace_back(ls.release()); + if (progressFunction) { + geos::util::ProgressFunctionIteration(*progressFunction, i, iterCount, iNotify, notificationInterval); + } + } + if (progressFunction) { + (*progressFunction)(1.0, nullptr); } return geomFactory->createMultiLineString(std::move(result)); } diff --git a/src/util/Progress.cpp b/src/util/Progress.cpp new file mode 100644 index 0000000000..f81e4512ef --- /dev/null +++ b/src/util/Progress.cpp @@ -0,0 +1,37 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2025 Even Rouault + * + * 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 +#include + +namespace geos { +namespace util { // geos::util + +void ProgressFunctionIteration(ProgressFunction& progressFunction, size_t i, + size_t iterCount, size_t& iNotify, + size_t notificationInterval) { + if (iNotify + 1 == notificationInterval) { + if (!progressFunction(static_cast(i + 1)/static_cast(iterCount), nullptr)) { + geos::util::Interrupt(); + } + iNotify = 0; + } + else { + ++iNotify; + } +} + +} // namespace geos::util +} // namespace geos + diff --git a/tests/unit/capi/GEOSCoverageSimplifyTest.cpp b/tests/unit/capi/GEOSCoverageSimplifyTest.cpp index 4ca7e9c394..23406f246f 100644 --- a/tests/unit/capi/GEOSCoverageSimplifyTest.cpp +++ b/tests/unit/capi/GEOSCoverageSimplifyTest.cpp @@ -84,8 +84,22 @@ template<> void object::test<3> const char* inputWKT = "GEOMETRYCOLLECTION(POLYGON(( 0 0,10 0,10.1 5,10 10,0 10,0 0)),POLYGON((10 0,20 0,20 10,10 10,10.1 5,10 0)))"; input_ = fromWKT(inputWKT); - result_ = GEOSCoverageSimplifyVW(input_, 1.0, 0); + struct Cbk + { + double lastRatio = 0; + + static int Func(double progressRatio, const char* /* msg */, void* userdata) + { + Cbk* self = static_cast(userdata); + self->lastRatio = progressRatio; + return true; + } + }; + + Cbk cbk; + result_ = GEOSCoverageSimplifyVWWithProgress(input_, 1.0, 0, &Cbk::Func, &cbk); + ensure("cbk.lastRatio == 1.0", cbk.lastRatio == 1.0); ensure( result_ != nullptr ); ensure( GEOSGeomTypeId(result_) == GEOS_GEOMETRYCOLLECTION ); diff --git a/tests/unit/coverage/CoverageSimplifierTest.cpp b/tests/unit/coverage/CoverageSimplifierTest.cpp index 1973ae965e..187ed7e74b 100644 --- a/tests/unit/coverage/CoverageSimplifierTest.cpp +++ b/tests/unit/coverage/CoverageSimplifierTest.cpp @@ -27,7 +27,7 @@ struct test_coveragesimplifier_data { void checkNoop( const std::vector>& input) { - std::vector> actual = CoverageSimplifier::simplify(input, 0); + std::vector> actual = CoverageSimplifier::simplify(input, 0, nullptr); // std::cout << w.write(*input[0]) << std::endl; // std::cout << w.write(*input[1]) << std::endl; @@ -43,8 +43,16 @@ struct test_coveragesimplifier_data { double tolerance, const std::vector>& expected) { - std::vector> actual = CoverageSimplifier::simplify(input, tolerance); + double lastRatio = 0.0; + geos::util::ProgressFunction myProgress = [&lastRatio](double ratio, const char*) + { + ensure("ratio >= lastRatio", ratio >= lastRatio); + lastRatio = ratio; + return true; + }; + std::vector> actual = CoverageSimplifier::simplify(input, tolerance, &myProgress); checkArrayEqual(expected, actual); + ensure("lastRatio == 1.0", lastRatio == 1.0); } void checkResultInner( @@ -52,7 +60,7 @@ struct test_coveragesimplifier_data { double tolerance, const std::vector>& expected) { - std::vector> actual = CoverageSimplifier::simplifyInner(input, tolerance); + std::vector> actual = CoverageSimplifier::simplifyInner(input, tolerance, nullptr); checkArrayEqual(expected, actual); } @@ -473,7 +481,7 @@ void object::test<30> () }); try { std::vector> result = - CoverageSimplifier::simplify(input, 10); + CoverageSimplifier::simplify(input, 10, nullptr); } catch (geos::util::IllegalArgumentException&) { ensure("caught IllegalArgumentException", true); @@ -493,7 +501,7 @@ void object::test<31> () }); try { std::vector> result = - CoverageSimplifier::simplify(input, 10); + CoverageSimplifier::simplify(input, 10, nullptr); } catch (geos::util::IllegalArgumentException&) { ensure("caught IllegalArgumentException", true); diff --git a/tests/unit/coverage/TPVWSimplifierTest.cpp b/tests/unit/coverage/TPVWSimplifierTest.cpp index ce55c99656..e8762ce426 100644 --- a/tests/unit/coverage/TPVWSimplifierTest.cpp +++ b/tests/unit/coverage/TPVWSimplifierTest.cpp @@ -31,7 +31,7 @@ struct test_tpvwsimplifier_data { { std::unique_ptr geom = r.read(wkt); const MultiLineString* mls = static_cast(geom.get()); - std::unique_ptr actual = TPVWSimplifier::simplify(mls, tolerance); + std::unique_ptr actual = TPVWSimplifier::simplify(mls, tolerance, nullptr); ensure_equals_geometry(actual.get(), geom.get()); } @@ -42,7 +42,7 @@ struct test_tpvwsimplifier_data { const std::string& wktExpected) { auto mls = r.read(wkt); - std::unique_ptr actual = TPVWSimplifier::simplify(mls.get(), tolerance); + std::unique_ptr actual = TPVWSimplifier::simplify(mls.get(), tolerance, nullptr); std::unique_ptr expected = r.read(wktExpected); ensure_equals_geometry(actual.get(), expected.get()); } @@ -76,7 +76,7 @@ struct test_tpvwsimplifier_data { constraints = r.read(wktConstraints); } - std::unique_ptr actual = TPVWSimplifier::simplify(lines.get(), freeRings, constraints.get(), tolerance); + std::unique_ptr actual = TPVWSimplifier::simplify(lines.get(), freeRings, constraints.get(), tolerance, nullptr); std::unique_ptr expected = r.read(wktExpected); // std::cout << "-- actual" << std::endl; diff --git a/util/geosop/GeometryOp.cpp b/util/geosop/GeometryOp.cpp index 986d1856a4..4c1d7f8208 100644 --- a/util/geosop/GeometryOp.cpp +++ b/util/geosop/GeometryOp.cpp @@ -968,7 +968,7 @@ std::vector opRegistry { [](const Geometry& geom, double d) { std::vector coverage = toList(geom); std::vector> result - = geos::coverage::CoverageSimplifier::simplify(coverage, d); + = geos::coverage::CoverageSimplifier::simplify(coverage, d, nullptr); //-- convert list type (be nice to avoid this) std::vector> resultList; for (std::size_t i = 0; i < result.size(); i++) { From 5556005ca1b769f48d32e821e0d61d4f459ab4b9 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sat, 31 May 2025 14:15:58 +0200 Subject: [PATCH 2/7] Remove cancelation part from progress callback --- capi/geos_c.h.in | 10 ++++------ capi/geos_ts_c.cpp | 2 +- include/geos/util/Progress.h | 7 +++---- src/util/Progress.cpp | 5 +---- tests/unit/capi/GEOSCoverageSimplifyTest.cpp | 3 +-- 5 files changed, 10 insertions(+), 17 deletions(-) diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in index 07a2f51a6f..8519140219 100644 --- a/capi/geos_c.h.in +++ b/capi/geos_c.h.in @@ -119,19 +119,17 @@ typedef void (*GEOSMessageHandler_r)(const char *message, void *userdata); /** - * A GEOS progression and cancel function. + * A GEOS progression callback function. * - * Such function takes a progression ratio, and an optional - * message, and returns true if computation must continue, or false if it - * must be interrupted. + * Such function takes a progression ratio, and an optional message. * * \param progressRatio Progression ratio (between 0 and 1) * \param message Information message (can be NULL) * \param userdata the user data pointer that was passed to GEOS together with * this callback. - * \return TRUE if processing must continue, or FALSE to interrupt it. */ -typedef int (*GEOSProgressCallback_r)(double progressRatio, const char* message, void* userdata); +typedef void (*GEOSProgressCallback_r)(double progressRatio, + const char* message, void* userdata); /* diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp index 100912367d..608c623726 100644 --- a/capi/geos_ts_c.cpp +++ b/capi/geos_ts_c.cpp @@ -4539,7 +4539,7 @@ extern "C" { geos::util::ProgressFunction progressFunction = [progressFunc, progressUserData](double progress, const char* message) { - return progressFunc(progress, message, progressUserData); + progressFunc(progress, message, progressUserData); }; using geos::coverage::CoverageSimplifier; diff --git a/include/geos/util/Progress.h b/include/geos/util/Progress.h index 179e090661..62515a1d5e 100644 --- a/include/geos/util/Progress.h +++ b/include/geos/util/Progress.h @@ -21,13 +21,12 @@ namespace geos { namespace util { // geos::util -/** Signature of a progression and cancel function. +/** Signature of a progression function. * * Such function takes a progression ratio (between 0 and 1), and an optional - * message, and returns true if computation must continue, or false if it - * must be interrupted. + * message. */ -typedef std::function ProgressFunction; +typedef std::function ProgressFunction; /** Do progress function related processing for an iteration loop of iterCount iterations. * diff --git a/src/util/Progress.cpp b/src/util/Progress.cpp index f81e4512ef..c199dd3aa3 100644 --- a/src/util/Progress.cpp +++ b/src/util/Progress.cpp @@ -12,7 +12,6 @@ * **********************************************************************/ -#include #include namespace geos { @@ -22,9 +21,7 @@ void ProgressFunctionIteration(ProgressFunction& progressFunction, size_t i, size_t iterCount, size_t& iNotify, size_t notificationInterval) { if (iNotify + 1 == notificationInterval) { - if (!progressFunction(static_cast(i + 1)/static_cast(iterCount), nullptr)) { - geos::util::Interrupt(); - } + progressFunction(static_cast(i + 1)/static_cast(iterCount), nullptr); iNotify = 0; } else { diff --git a/tests/unit/capi/GEOSCoverageSimplifyTest.cpp b/tests/unit/capi/GEOSCoverageSimplifyTest.cpp index 23406f246f..b3c5703161 100644 --- a/tests/unit/capi/GEOSCoverageSimplifyTest.cpp +++ b/tests/unit/capi/GEOSCoverageSimplifyTest.cpp @@ -89,11 +89,10 @@ template<> void object::test<3> { double lastRatio = 0; - static int Func(double progressRatio, const char* /* msg */, void* userdata) + static void Func(double progressRatio, const char* /* msg */, void* userdata) { Cbk* self = static_cast(userdata); self->lastRatio = progressRatio; - return true; } }; From 6cde4b29e8294a2c245c2e9b9df6dd52f1aadc92 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 4 Jun 2025 16:52:23 +0200 Subject: [PATCH 3/7] TPVWSimplifier::simplify(): improve progress report --- include/geos/coverage/TPVWSimplifier.h | 3 +- include/geos/util/Progress.h | 22 ++++++++++++ src/coverage/TPVWSimplifier.cpp | 50 ++++++++++++++++++++++---- src/util/Progress.cpp | 9 +++++ 4 files changed, 77 insertions(+), 7 deletions(-) diff --git a/include/geos/coverage/TPVWSimplifier.h b/include/geos/coverage/TPVWSimplifier.h index 272d32458c..ec2bce993f 100644 --- a/include/geos/coverage/TPVWSimplifier.h +++ b/include/geos/coverage/TPVWSimplifier.h @@ -218,7 +218,8 @@ class GEOS_DLL TPVWSimplifier std::vector createEdges( const MultiLineString* lines, - std::vector& freeRing); + std::vector& freeRing, + geos::util::ProgressFunction* progressFunction); }; // TPVWSimplifier diff --git a/include/geos/util/Progress.h b/include/geos/util/Progress.h index 62515a1d5e..c581c60acf 100644 --- a/include/geos/util/Progress.h +++ b/include/geos/util/Progress.h @@ -57,6 +57,28 @@ typedef std::function ProgressFunction; */ void ProgressFunctionIteration(ProgressFunction& progressFunction, size_t i, size_t iterCount, size_t& iNotify, size_t notificationInterval); +/** Create a scaled progress function. + * + * Sometimes when an operation wants to report progress, it actually + * invokes several subprocesses which also take a ProgressFunction, + * and it is desirable to map the progress of each sub operation into + * a portion of 0.0 to 1.0 progress of the overall process. The scaled + * progress function can be used for this. + * + * For each subsection a scaled progress function is created and + * instead of passing the overall progress func down to the sub functions, + * the scale progress function is passed instead. + * + * @param ratioMin the value to which 0.0 in the sub operation is mapped. + * @param ratioMax the value to which 1.0 is the sub operation is mapped. + * @param progressFunction the overall progress function. + * + * @return scaled progress function. + */ +ProgressFunction CreateScaledProgressFunction(double ratioMin, double ratioMax, + ProgressFunction& progressFunction); + + } // namespace geos::util } // namespace geos diff --git a/src/coverage/TPVWSimplifier.cpp b/src/coverage/TPVWSimplifier.cpp index a68a3753ee..d311697744 100644 --- a/src/coverage/TPVWSimplifier.cpp +++ b/src/coverage/TPVWSimplifier.cpp @@ -104,8 +104,33 @@ std::unique_ptr TPVWSimplifier::simplify(geos::util::ProgressFunction* progressFunction) { std::vector emptyList; - std::vector edges = createEdges(inputLines, isFreeRing); - std::vector constraintEdges = createEdges(constraintLines, emptyList); + geos::util::ProgressFunction subProgress; + + constexpr double RATIO_FIRST_PASS = 0.4; + + const double ratioInputLinesOverInputAndConstraint = + RATIO_FIRST_PASS * + static_cast(inputLines ? inputLines->getNumGeometries() : 0) / + static_cast(std::max(1, + (inputLines ? inputLines->getNumGeometries() : 0) + + (constraintLines ? constraintLines->getNumGeometries() : 0))); + + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + 0, ratioInputLinesOverInputAndConstraint, *progressFunction); + } + std::vector edges = createEdges(inputLines, isFreeRing, + progressFunction ? &subProgress : nullptr); + + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + ratioInputLinesOverInputAndConstraint, RATIO_FIRST_PASS, *progressFunction); + } + std::vector constraintEdges = createEdges( + constraintLines, emptyList, + progressFunction ? &subProgress : nullptr); EdgeIndex edgeIndex; edgeIndex.add(edges); @@ -114,13 +139,18 @@ TPVWSimplifier::simplify(geos::util::ProgressFunction* progressFunction) std::vector> result; const size_t iterCount = edges.size(); const size_t notificationInterval = std::max(1, iterCount / 100); + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + RATIO_FIRST_PASS, 1.0, *progressFunction); + } for (size_t i = 0, iNotify = 0; i < iterCount; ++i) { auto& edge = edges[i]; std::unique_ptr ptsSimp = edge.simplify(edgeIndex); auto ls = geomFactory->createLineString(std::move(ptsSimp)); result.emplace_back(ls.release()); if (progressFunction) { - geos::util::ProgressFunctionIteration(*progressFunction, i, iterCount, iNotify, notificationInterval); + geos::util::ProgressFunctionIteration(subProgress, i, iterCount, iNotify, notificationInterval); } } if (progressFunction) { @@ -133,17 +163,25 @@ TPVWSimplifier::simplify(geos::util::ProgressFunction* progressFunction) std::vector TPVWSimplifier::createEdges( const MultiLineString* lines, - std::vector& freeRing) + std::vector& freeRing, + geos::util::ProgressFunction* progressFunction) { std::vector edges; if (lines == nullptr) return edges; - - for (std::size_t i = 0; i < lines->getNumGeometries(); i++) { + const size_t iterCount = lines->getNumGeometries(); + const size_t notificationInterval = std::max(1, iterCount / 100); + for (size_t i = 0, iNotify = 0; i < iterCount; ++i) { const LineString* line = lines->getGeometryN(i); bool isFree = freeRing.empty() ? false : freeRing[i]; edges.emplace_back(line, isFree, areaTolerance); + if (progressFunction) { + geos::util::ProgressFunctionIteration(*progressFunction, i, iterCount, iNotify, notificationInterval); + } + } + if (progressFunction) { + (*progressFunction)(1.0, nullptr); } return edges; } diff --git a/src/util/Progress.cpp b/src/util/Progress.cpp index c199dd3aa3..ab4041e683 100644 --- a/src/util/Progress.cpp +++ b/src/util/Progress.cpp @@ -29,6 +29,15 @@ void ProgressFunctionIteration(ProgressFunction& progressFunction, size_t i, } } +ProgressFunction CreateScaledProgressFunction(double ratioMin, double ratioMax, + ProgressFunction& progressFunction) +{ + return [ratioMin, ratioMax, &progressFunction](double ratio, const char* msg) + { + progressFunction(ratioMin + (ratioMax - ratioMin) * ratio, msg); + }; +} + } // namespace geos::util } // namespace geos From e4b8c7bf0229b547205daee018e4148926d14d93 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 4 Jun 2025 17:48:13 +0200 Subject: [PATCH 4/7] TPVWSimplifier::simplify(): finer grain progress report --- .../coverage/CoverageBoundarySegmentFinder.h | 4 +- include/geos/coverage/CoverageEdge.h | 4 +- include/geos/coverage/CoverageRingEdges.h | 17 ++- include/geos/coverage/VertexRingCounter.h | 4 +- .../CoverageBoundarySegmentFinder.cpp | 14 +- src/coverage/CoverageEdge.cpp | 14 +- src/coverage/CoverageRingEdges.cpp | 128 ++++++++++++++++-- src/coverage/CoverageSimplifier.cpp | 71 ++++++++-- src/coverage/TPVWSimplifier.cpp | 2 +- src/coverage/VertexRingCounter.cpp | 14 +- tests/unit/coverage/CoverageRingEdgesTest.cpp | 4 +- 11 files changed, 236 insertions(+), 40 deletions(-) diff --git a/include/geos/coverage/CoverageBoundarySegmentFinder.h b/include/geos/coverage/CoverageBoundarySegmentFinder.h index 359ea7aecb..fb0945ca4a 100644 --- a/include/geos/coverage/CoverageBoundarySegmentFinder.h +++ b/include/geos/coverage/CoverageBoundarySegmentFinder.h @@ -20,6 +20,7 @@ #include #include #include +#include namespace geos { namespace geom { @@ -57,7 +58,8 @@ class CoverageBoundarySegmentFinder : public geos::geom::CoordinateSequenceFilte static LineSegment::UnorderedSet - findBoundarySegments(const std::vector& geoms); + findBoundarySegments(const std::vector& geoms, + geos::util::ProgressFunction* progressFunction); static bool isBoundarySegment( const LineSegment::UnorderedSet& boundarySegs, diff --git a/include/geos/coverage/CoverageEdge.h b/include/geos/coverage/CoverageEdge.h index 60afd1c7ca..910c2310e5 100644 --- a/include/geos/coverage/CoverageEdge.h +++ b/include/geos/coverage/CoverageEdge.h @@ -20,6 +20,7 @@ #include #include #include +#include // Forward declarations namespace geos { @@ -116,7 +117,8 @@ class GEOS_DLL CoverageEdge { static std::unique_ptr createLines( const std::vector& edges, - const GeometryFactory* geomFactory); + const GeometryFactory* geomFactory, + geos::util::ProgressFunction* progressFunction); std::unique_ptr toLineString( const GeometryFactory* geomFactory); diff --git a/include/geos/coverage/CoverageRingEdges.h b/include/geos/coverage/CoverageRingEdges.h index 5a0f4ac1b9..8378c9c0b6 100644 --- a/include/geos/coverage/CoverageRingEdges.h +++ b/include/geos/coverage/CoverageRingEdges.h @@ -18,6 +18,7 @@ #include #include #include // to materialize CoverageEdge +#include #include #include @@ -72,10 +73,11 @@ class GEOS_DLL CoverageRingEdges { public: - CoverageRingEdges(const std::vector& coverage) + CoverageRingEdges(const std::vector& coverage, + geos::util::ProgressFunction* progressFunction) : m_coverage(coverage) { - build(); + build(progressFunction); }; @@ -96,14 +98,15 @@ class GEOS_DLL CoverageRingEdges { /** * Recreates the polygon coverage from the current edge values. * + * @param progressFunction Progress function or null * @return an array of polygonal geometries representing the coverage */ - std::vector> buildCoverage() const; + std::vector> buildCoverage(geos::util::ProgressFunction* progressFunction) const; private: - void build(); + void build(geos::util::ProgressFunction* progressFunction); void addRingEdges( const LinearRing* ring, @@ -140,10 +143,12 @@ class GEOS_DLL CoverageRingEdges { const CoordinateSequence& ring); Coordinate::UnorderedSet findMultiRingNodes( - const std::vector& coverage); + const std::vector& coverage, + geos::util::ProgressFunction* progressFunction); Coordinate::UnorderedSet findBoundaryNodes( - LineSegment::UnorderedSet& lineSegments); + LineSegment::UnorderedSet& lineSegments, + geos::util::ProgressFunction* progressFunction); std::unique_ptr buildPolygonal( const Geometry* geom) const; diff --git a/include/geos/coverage/VertexRingCounter.h b/include/geos/coverage/VertexRingCounter.h index c0982ad2b4..e67ffc963f 100644 --- a/include/geos/coverage/VertexRingCounter.h +++ b/include/geos/coverage/VertexRingCounter.h @@ -19,6 +19,7 @@ #include #include #include +#include namespace geos { namespace geom { @@ -54,7 +55,8 @@ class VertexRingCounter : public geos::geom::CoordinateSequenceFilter static void count( const std::vector& geoms, - std::map& counts); + std::map& counts, + geos::util::ProgressFunction* progressFunction); private: diff --git a/src/coverage/CoverageBoundarySegmentFinder.cpp b/src/coverage/CoverageBoundarySegmentFinder.cpp index d5759be7d4..4384f17628 100644 --- a/src/coverage/CoverageBoundarySegmentFinder.cpp +++ b/src/coverage/CoverageBoundarySegmentFinder.cpp @@ -28,12 +28,22 @@ namespace coverage { // geos.coverage // public static LineSegment::UnorderedSet CoverageBoundarySegmentFinder::findBoundarySegments( - const std::vector& geoms) + const std::vector& geoms, + geos::util::ProgressFunction* progressFunction) { LineSegment::UnorderedSet segs; CoverageBoundarySegmentFinder finder(segs); - for (const Geometry* geom : geoms) { + const size_t iterCount = geoms.size(); + const size_t notificationInterval = std::max(1, iterCount / 100); + for (size_t i = 0, iNotify = 0; i < iterCount; ++i) { + const Geometry* geom = geoms[i]; geom->apply_ro(finder); + if (progressFunction) { + geos::util::ProgressFunctionIteration(*progressFunction, i, iterCount, iNotify, notificationInterval); + } + } + if (progressFunction) { + (*progressFunction)(1.0, nullptr); } return segs; } diff --git a/src/coverage/CoverageEdge.cpp b/src/coverage/CoverageEdge.cpp index 65971fc3ad..6d1ae89390 100644 --- a/src/coverage/CoverageEdge.cpp +++ b/src/coverage/CoverageEdge.cpp @@ -55,13 +55,23 @@ CoverageEdge::createEdge(const CoordinateSequence& ring, std::unique_ptr CoverageEdge::createLines( const std::vector& edges, - const GeometryFactory* geomFactory) + const GeometryFactory* geomFactory, + geos::util::ProgressFunction* progressFunction) { std::vector> lines; - for (const CoverageEdge* edge : edges) { + const size_t iterCount = edges.size(); + const size_t notificationInterval = std::max(1, iterCount / 100); + for (size_t i = 0, iNotify = 0; i < iterCount; ++i) { + const CoverageEdge* edge = edges[i]; auto cs = edge->getCoordinates()->clone(); auto ls = geomFactory->createLineString(std::move(cs)); lines.push_back(std::move(ls)); + if (progressFunction) { + geos::util::ProgressFunctionIteration(*progressFunction, i, iterCount, iNotify, notificationInterval); + } + } + if (progressFunction) { + (*progressFunction)(1.0, nullptr); } return geomFactory->createMultiLineString(std::move(lines)); } diff --git a/src/coverage/CoverageRingEdges.cpp b/src/coverage/CoverageRingEdges.cpp index 9d5eba07fd..147b5c8b83 100644 --- a/src/coverage/CoverageRingEdges.cpp +++ b/src/coverage/CoverageRingEdges.cpp @@ -61,15 +61,47 @@ CoverageRingEdges::selectEdges(std::size_t ringCount) const /* private */ void -CoverageRingEdges::build() +CoverageRingEdges::build(geos::util::ProgressFunction* progressFunction) { - Coordinate::UnorderedSet nodes = findMultiRingNodes(m_coverage); - LineSegment::UnorderedSet boundarySegs = CoverageBoundarySegmentFinder::findBoundarySegments(m_coverage); - Coordinate::UnorderedSet boundaryNodes = findBoundaryNodes(boundarySegs); + constexpr double RATIO_FIRST_PASS = 0.1; + constexpr double RATIO_SECOND_PASS = 0.2; + constexpr double RATIO_THIRD_PASS = 0.9; + constexpr double RATIO_FOURTH_PASS = 1.0; + geos::util::ProgressFunction subProgress; + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + 0, RATIO_FIRST_PASS, *progressFunction); + } + Coordinate::UnorderedSet nodes = findMultiRingNodes(m_coverage, progressFunction ? &subProgress : nullptr); + + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + RATIO_FIRST_PASS, RATIO_SECOND_PASS, *progressFunction); + } + LineSegment::UnorderedSet boundarySegs = CoverageBoundarySegmentFinder::findBoundarySegments( + m_coverage, progressFunction ? &subProgress : nullptr); + + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + RATIO_SECOND_PASS, RATIO_THIRD_PASS, *progressFunction); + } + Coordinate::UnorderedSet boundaryNodes = findBoundaryNodes( + boundarySegs, progressFunction ? &subProgress : nullptr); nodes.insert(boundaryNodes.begin(), boundaryNodes.end()); + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + RATIO_THIRD_PASS, RATIO_FOURTH_PASS, *progressFunction); + } std::map uniqueEdgeMap; - for (const Geometry* geom : m_coverage) { + const size_t iterCount = m_coverage.size(); + const size_t notificationInterval = std::max(1, iterCount / 100); + for (size_t i = 0, iNotify = 0; i < iterCount; ++i) { + const Geometry* geom = m_coverage[i]; for (std::size_t ipoly = 0; ipoly < geom->getNumGeometries(); ipoly++) { util::ensureNoCurvedComponents(geom->getGeometryN(ipoly)); @@ -91,6 +123,13 @@ CoverageRingEdges::build() addRingEdges(hole, nodes, boundarySegs, uniqueEdgeMap); } } + + if (progressFunction) { + geos::util::ProgressFunctionIteration(subProgress, i, iterCount, iNotify, notificationInterval); + } + } + if (progressFunction) { + (*progressFunction)(1.0, nullptr); } } @@ -254,21 +293,44 @@ CoverageRingEdges::next(std::size_t index, const CoordinateSequence& ring) /* private */ Coordinate::UnorderedSet -CoverageRingEdges::findMultiRingNodes(const std::vector& coverage) +CoverageRingEdges::findMultiRingNodes(const std::vector& coverage, + geos::util::ProgressFunction* progressFunction) { std::map vertexRingCount; - VertexRingCounter::count(coverage, vertexRingCount); + geos::util::ProgressFunction subProgress; + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + 0, 0.5, *progressFunction); + } + + VertexRingCounter::count(coverage, vertexRingCount, progressFunction ? &subProgress : nullptr); Coordinate::UnorderedSet nodes; // for (Coordinate v : vertexCount.keySet()) { // if (vertexCount.get(v) > 2) { // nodes.add(v); // } // } + const size_t iterCount = vertexRingCount.size(); + const size_t notificationInterval = std::max(1, iterCount / 100); + size_t i = 0, iNotify = 0; + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + 0.5, 1.0, *progressFunction); + } for (const auto &mapPair : vertexRingCount) { const Coordinate& v = mapPair.first; std::size_t count = mapPair.second; if (count > 2) nodes.insert(v); + if (progressFunction) { + geos::util::ProgressFunctionIteration(subProgress, i, iterCount, iNotify, notificationInterval); + } + ++i; + } + if (progressFunction) { + (*progressFunction)(1.0, nullptr); } return nodes; } @@ -276,20 +338,51 @@ CoverageRingEdges::findMultiRingNodes(const std::vector& covera /* private */ Coordinate::UnorderedSet -CoverageRingEdges::findBoundaryNodes(LineSegment::UnorderedSet& boundarySegments) +CoverageRingEdges::findBoundaryNodes(LineSegment::UnorderedSet& boundarySegments, + geos::util::ProgressFunction* progressFunction) { std::unordered_map counter; - for (const LineSegment& seg : boundarySegments) { - counter[seg.p0] += 1; - counter[seg.p1] += 1; + geos::util::ProgressFunction subProgress; + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + 0, 0.5, *progressFunction); + } + { + const size_t iterCount = boundarySegments.size(); + const size_t notificationInterval = std::max(1, iterCount / 100); + size_t i = 0, iNotify = 0; + for (const LineSegment& seg : boundarySegments) { + counter[seg.p0] += 1; + counter[seg.p1] += 1; + if (progressFunction) { + geos::util::ProgressFunctionIteration(subProgress, i, iterCount, iNotify, notificationInterval); + } + ++i; + } } + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + 0.5, 1.0, *progressFunction); + } Coordinate::UnorderedSet nodes; + const size_t iterCount = counter.size(); + const size_t notificationInterval = std::max(1, iterCount / 100); + size_t i = 0, iNotify = 0; for (const auto& c : counter) { const Coordinate& v = c.first; std::size_t count = c.second; if (count > 2) nodes.insert(v); + if (progressFunction) { + geos::util::ProgressFunctionIteration(subProgress, i, iterCount, iNotify, notificationInterval); + } + ++i; + } + if (progressFunction) { + (*progressFunction)(1.0, nullptr); } return nodes; } @@ -297,11 +390,20 @@ CoverageRingEdges::findBoundaryNodes(LineSegment::UnorderedSet& boundarySegments /* public */ std::vector> -CoverageRingEdges::buildCoverage() const +CoverageRingEdges::buildCoverage(geos::util::ProgressFunction* progressFunction) const { std::vector> result; - for (const Geometry* geom : m_coverage) { + const size_t iterCount = m_coverage.size(); + const size_t notificationInterval = std::max(1, iterCount / 100); + for (size_t i = 0, iNotify = 0; i < iterCount; ++i) { + const Geometry* geom = m_coverage[i]; result.push_back(buildPolygonal(geom)); + if (progressFunction) { + geos::util::ProgressFunctionIteration(*progressFunction, i, iterCount, iNotify, notificationInterval); + } + } + if (progressFunction) { + (*progressFunction)(1.0, nullptr); } return result; } diff --git a/src/coverage/CoverageSimplifier.cpp b/src/coverage/CoverageSimplifier.cpp index 774125001f..bdbe92a3eb 100644 --- a/src/coverage/CoverageSimplifier.cpp +++ b/src/coverage/CoverageSimplifier.cpp @@ -101,9 +101,25 @@ std::vector> CoverageSimplifier::simplify(double tolerance, ProgressFunction* progressFunction) { - CoverageRingEdges cov(m_input); - simplifyEdges(cov.getEdges(), nullptr, tolerance, progressFunction); - return cov.buildCoverage(); + geos::util::ProgressFunction subProgress; + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + 0, 0.8, *progressFunction); + } + CoverageRingEdges cov(m_input, progressFunction ? &subProgress : nullptr); + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + 0.8, 0.9, *progressFunction); + } + simplifyEdges(cov.getEdges(), nullptr, tolerance, progressFunction ? &subProgress : nullptr); + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + 0.9, 1.0, *progressFunction); + } + return cov.buildCoverage(progressFunction ? &subProgress : nullptr); } /* public */ @@ -111,13 +127,35 @@ std::vector> CoverageSimplifier::simplifyInner(double tolerance, ProgressFunction* progressFunction) { - CoverageRingEdges cov(m_input); + geos::util::ProgressFunction subProgress; + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + 0, 0.7, *progressFunction); + } + CoverageRingEdges cov(m_input, progressFunction ? &subProgress : nullptr); std::vector innerEdges = cov.selectEdges(2); std::vector outerEdges = cov.selectEdges(1); - std::unique_ptr constraintEdges = CoverageEdge::createLines(outerEdges, m_geomFactory); + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + 0.7, 0.8, *progressFunction); + } + std::unique_ptr constraintEdges = CoverageEdge::createLines( + outerEdges, m_geomFactory, progressFunction ? &subProgress : nullptr); - simplifyEdges(innerEdges, constraintEdges.get(), tolerance, progressFunction); - return cov.buildCoverage(); + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + 0.8, 0.9, *progressFunction); + } + simplifyEdges(innerEdges, constraintEdges.get(), tolerance, progressFunction ? &subProgress : nullptr); + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + 0.9, 1.0, *progressFunction); + } + return cov.buildCoverage(progressFunction ? &subProgress : nullptr); } /* private */ @@ -128,9 +166,24 @@ CoverageSimplifier::simplifyEdges( double tolerance, ProgressFunction* progressFunction) { - std::unique_ptr lines = CoverageEdge::createLines(edges, m_geomFactory); + constexpr double RATIO_FIRST_PASS = 0.5; + geos::util::ProgressFunction subProgress; + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + 0, RATIO_FIRST_PASS, *progressFunction); + } + std::unique_ptr lines = CoverageEdge::createLines( + edges, m_geomFactory, progressFunction ? &subProgress : nullptr); std::vector freeRings = getFreeRings(edges); - std::unique_ptr linesSimp = TPVWSimplifier::simplify(lines.get(), freeRings, constraints, tolerance, progressFunction); + if (progressFunction) + { + subProgress = geos::util::CreateScaledProgressFunction( + RATIO_FIRST_PASS, 1, *progressFunction); + } + std::unique_ptr linesSimp = TPVWSimplifier::simplify( + lines.get(), freeRings, constraints, tolerance, + progressFunction ? &subProgress : nullptr); //Assert: mlsSimp.getNumGeometries = edges.length setCoordinates(edges, linesSimp.get()); diff --git a/src/coverage/TPVWSimplifier.cpp b/src/coverage/TPVWSimplifier.cpp index d311697744..c8a9c8ad2c 100644 --- a/src/coverage/TPVWSimplifier.cpp +++ b/src/coverage/TPVWSimplifier.cpp @@ -106,7 +106,7 @@ TPVWSimplifier::simplify(geos::util::ProgressFunction* progressFunction) std::vector emptyList; geos::util::ProgressFunction subProgress; - constexpr double RATIO_FIRST_PASS = 0.4; + constexpr double RATIO_FIRST_PASS = 0.8; const double ratioInputLinesOverInputAndConstraint = RATIO_FIRST_PASS * diff --git a/src/coverage/VertexRingCounter.cpp b/src/coverage/VertexRingCounter.cpp index fe742889f6..e27a5665bf 100644 --- a/src/coverage/VertexRingCounter.cpp +++ b/src/coverage/VertexRingCounter.cpp @@ -34,11 +34,21 @@ namespace coverage { // geos.coverage void VertexRingCounter::count( const std::vector& geoms, - std::map& counts) + std::map& counts, + geos::util::ProgressFunction* progressFunction) { VertexRingCounter vertextCounter(counts); - for (const Geometry* geom : geoms) { + const size_t iterCount = geoms.size(); + const size_t notificationInterval = std::max(1, iterCount / 100); + for (size_t i = 0, iNotify = 0; i < iterCount; ++i) { + const Geometry* geom = geoms[i]; geom->apply_ro(vertextCounter); + if (progressFunction) { + geos::util::ProgressFunctionIteration(*progressFunction, i, iterCount, iNotify, notificationInterval); + } + } + if (progressFunction) { + (*progressFunction)(1.0, nullptr); } } diff --git a/tests/unit/coverage/CoverageRingEdgesTest.cpp b/tests/unit/coverage/CoverageRingEdgesTest.cpp index bf0fdc610e..5aade86ea6 100644 --- a/tests/unit/coverage/CoverageRingEdgesTest.cpp +++ b/tests/unit/coverage/CoverageRingEdgesTest.cpp @@ -32,7 +32,7 @@ struct test_coverageringedges_data { std::unique_ptr geom = r.read(wkt); std::vector polygons = toArray(*geom); - CoverageRingEdges cov(polygons); + CoverageRingEdges cov(polygons, nullptr); std::vector& edges = cov.getEdges(); std::unique_ptr edgeLines = toArray(edges, geom->getFactory()); std::unique_ptr expected = r.read(wktExpected); @@ -44,7 +44,7 @@ struct test_coverageringedges_data { std::unique_ptr geom = r.read(wkt); auto polygons = toArray(*geom); - CoverageRingEdges covEdges(polygons); + CoverageRingEdges covEdges(polygons, nullptr); auto edges = covEdges.selectEdges(ringCount); auto edgeLines = toArray(edges, geom->getFactory()); std::unique_ptr expected = r.read(wktExpected); From d1c514a659248a8d1327ee7bbfa547da51991090 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 13 Jun 2025 14:20:05 +0200 Subject: [PATCH 5/7] Add GEOSUnaryUnionWithProgress_r() --- capi/geos_c.h.in | 7 ++++ capi/geos_ts_c.cpp | 18 ++++++++++ include/geos/geom/Geometry.h | 9 ++++- .../operation/union/CascadedPolygonUnion.h | 20 +++++++---- include/geos/operation/union/UnaryUnionOp.h | 13 ++++--- src/geom/Geometry.cpp | 4 +-- src/geom/util/GeometryFixer.cpp | 3 +- src/operation/overlayng/OverlayNGRobust.cpp | 3 +- src/operation/overlayng/UnaryUnionNG.cpp | 3 +- src/operation/polygonize/BuildArea.cpp | 2 +- src/operation/union/CascadedPolygonUnion.cpp | 36 +++++++++++++------ src/operation/union/UnaryUnionOp.cpp | 4 +-- 12 files changed, 92 insertions(+), 30 deletions(-) diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in index 8519140219..e488af7f6c 100644 --- a/capi/geos_c.h.in +++ b/capi/geos_c.h.in @@ -1101,6 +1101,13 @@ extern GEOSGeometry GEOS_DLL *GEOSUnaryUnion_r( GEOSContextHandle_t handle, const GEOSGeometry* g); +/** \see GEOSUnaryUnion */ +extern GEOSGeometry GEOS_DLL *GEOSUnaryUnionWithProgress_r( + GEOSContextHandle_t handle, + const GEOSGeometry* g, + GEOSProgressCallback_r progressFunc, + void* progressUserData); + /** \see GEOSUnaryUnionPrec */ extern GEOSGeometry GEOS_DLL *GEOSUnaryUnionPrec_r( GEOSContextHandle_t handle, diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp index 608c623726..64f16f9c09 100644 --- a/capi/geos_ts_c.cpp +++ b/capi/geos_ts_c.cpp @@ -1667,6 +1667,24 @@ extern "C" { }); } + Geometry* + GEOSUnaryUnionWithProgress_r(GEOSContextHandle_t extHandle, const Geometry* g, + GEOSProgressCallback_r progressFunc, + void* progressUserData) + { + geos::util::ProgressFunction progressFunction = + [progressFunc, progressUserData](double progress, const char* message) + { + progressFunc(progress, message, progressUserData); + }; + + return execute(extHandle, [&]() { + std::unique_ptr g3(g->Union(&progressFunction)); + g3->setSRID(g->getSRID()); + return g3.release(); + }); + } + Geometry* GEOSUnaryUnionPrec_r(GEOSContextHandle_t extHandle, const Geometry* g1, double gridSize) { diff --git a/include/geos/geom/Geometry.h b/include/geos/geom/Geometry.h index 36e64f0421..4325767a60 100644 --- a/include/geos/geom/Geometry.h +++ b/include/geos/geom/Geometry.h @@ -35,6 +35,7 @@ #include // for Dimension::DimensionType #include // for inheritance #include // to materialize CoordinateSequence +#include #include #include @@ -726,9 +727,15 @@ class GEOS_DLL Geometry { * * @see UnaryUnionOp */ - Ptr Union() const; + Ptr Union(geos::util::ProgressFunction* progressFunction) const; // throw(IllegalArgumentException *, TopologyException *); + Ptr Union() const + { + geos::util::ProgressFunction* progressFunction = nullptr; + return Union(progressFunction); + } + /** * \brief * Returns a Geometry representing the points making up this diff --git a/include/geos/operation/union/CascadedPolygonUnion.h b/include/geos/operation/union/CascadedPolygonUnion.h index 2b1e292670..fcc5ba9a76 100644 --- a/include/geos/operation/union/CascadedPolygonUnion.h +++ b/include/geos/operation/union/CascadedPolygonUnion.h @@ -25,6 +25,7 @@ #include #include +#include // Forward declarations namespace geos { @@ -140,7 +141,7 @@ class GEOS_DLL CascadedPolygonUnion { * ownership of elements *and* vector are left to caller. */ static std::unique_ptr Union(std::vector* polys); - static std::unique_ptr Union(std::vector* polys, UnionStrategy* unionFun); + static std::unique_ptr Union(std::vector* polys, UnionStrategy* unionFun, geos::util::ProgressFunction* progressFunction); /** \brief * Computes the union of a set of polygonal [Geometrys](@ref geom::Geometry). @@ -149,17 +150,18 @@ class GEOS_DLL CascadedPolygonUnion { * @param start start iterator * @param end end iterator * @param unionStrategy strategy to apply + * @param progressFunction progress function */ template static std::unique_ptr - Union(T start, T end, UnionStrategy *unionStrategy) + Union(T start, T end, UnionStrategy *unionStrategy, geos::util::ProgressFunction* progressFunction) { std::vector polys; for(T i = start; i != end; ++i) { const geom::Polygon* p = dynamic_cast(*i); polys.push_back(const_cast(p)); } - return Union(&polys, unionStrategy); + return Union(&polys, unionStrategy, progressFunction); } /** \brief @@ -167,8 +169,9 @@ class GEOS_DLL CascadedPolygonUnion { * * @param polys a collection of polygonal [Geometrys](@ref geom::Geometry). * Ownership of elements *and* vector are left to caller. + * @param progressFunction progress function */ - static std::unique_ptr Union(const geom::MultiPolygon* polys); + static std::unique_ptr Union(const geom::MultiPolygon* polys, geos::util::ProgressFunction* progressFunction); /** \brief * Creates a new instance to union the given collection of @@ -192,10 +195,11 @@ class GEOS_DLL CascadedPolygonUnion { /** \brief * Computes the union of the input geometries. * + * @param progressFunction progress function * @return the union of the input geometries * @return `null` if no input geometries were provided */ - std::unique_ptr Union(); + std::unique_ptr Union(geos::util::ProgressFunction* progressFunction); private: @@ -211,7 +215,11 @@ class GEOS_DLL CascadedPolygonUnion { * @param end the index after the end of the section * @return the union of the list section */ - std::unique_ptr binaryUnion(const std::vector & geoms, std::size_t start, std::size_t end); + std::unique_ptr binaryUnion( + const std::vector & geoms, + std::size_t start, + std::size_t end, + std::function* unitProgress); /** * Computes the union of two geometries, diff --git a/include/geos/operation/union/UnaryUnionOp.h b/include/geos/operation/union/UnaryUnionOp.h index 3fed7e32bc..98b6a9e700 100644 --- a/include/geos/operation/union/UnaryUnionOp.h +++ b/include/geos/operation/union/UnaryUnionOp.h @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -93,7 +94,8 @@ class GEOS_DLL UnaryUnionOp { Union(const T& geoms) { UnaryUnionOp op(geoms); - return op.Union(); + geos::util::ProgressFunction* progressFunction = nullptr; + return op.Union(progressFunction); } template @@ -102,14 +104,15 @@ class GEOS_DLL UnaryUnionOp { geom::GeometryFactory& geomFact) { UnaryUnionOp op(geoms, geomFact); - return op.Union(); + geos::util::ProgressFunction* progressFunction = nullptr; + return op.Union(progressFunction); } static std::unique_ptr - Union(const geom::Geometry& geom) + Union(const geom::Geometry& geom, geos::util::ProgressFunction* progressFunction) { UnaryUnionOp op(geom); - return op.Union(); + return op.Union(progressFunction); } template @@ -150,7 +153,7 @@ class GEOS_DLL UnaryUnionOp { * @return an empty GEOMETRYCOLLECTION if no geometries were provided * in the input */ - std::unique_ptr Union(); + std::unique_ptr Union(geos::util::ProgressFunction* progressFunction); private: diff --git a/src/geom/Geometry.cpp b/src/geom/Geometry.cpp index a34c7bf352..b833a04990 100644 --- a/src/geom/Geometry.cpp +++ b/src/geom/Geometry.cpp @@ -624,10 +624,10 @@ Geometry::Union(const Geometry* other) const /* public */ Geometry::Ptr -Geometry::Union() const +Geometry::Union(geos::util::ProgressFunction* progressFunction) const { using geos::operation::geounion::UnaryUnionOp; - return UnaryUnionOp::Union(*this); + return UnaryUnionOp::Union(*this, progressFunction); } std::unique_ptr diff --git a/src/geom/util/GeometryFixer.cpp b/src/geom/util/GeometryFixer.cpp index 6d73ecbc9f..7523978f46 100644 --- a/src/geom/util/GeometryFixer.cpp +++ b/src/geom/util/GeometryFixer.cpp @@ -341,7 +341,8 @@ GeometryFixer::unionGeometry(std::vector& polys) const } UnaryUnionOp op(polys); - return op.Union(); + geos::util::ProgressFunction* progressFunction = nullptr; + return op.Union(progressFunction); // return OverlayNGRobust::Union(polys); } diff --git a/src/operation/overlayng/OverlayNGRobust.cpp b/src/operation/overlayng/OverlayNGRobust.cpp index e9af20896a..fa12b9340b 100644 --- a/src/operation/overlayng/OverlayNGRobust.cpp +++ b/src/operation/overlayng/OverlayNGRobust.cpp @@ -76,7 +76,8 @@ OverlayNGRobust::Union(const Geometry* a) geounion::UnaryUnionOp op(*a); SRUnionStrategy unionSRFun; op.setUnionFunction(&unionSRFun); - return op.Union(); + geos::util::ProgressFunction* progressFunction = nullptr; + return op.Union(progressFunction); } /*public static*/ diff --git a/src/operation/overlayng/UnaryUnionNG.cpp b/src/operation/overlayng/UnaryUnionNG.cpp index f09b987b93..609fa93ee9 100644 --- a/src/operation/overlayng/UnaryUnionNG.cpp +++ b/src/operation/overlayng/UnaryUnionNG.cpp @@ -37,7 +37,8 @@ UnaryUnionNG::Union(const Geometry* geom, const PrecisionModel& pm) NGUnionStrategy ngUnionStrat(pm); geounion::UnaryUnionOp op(*geom); op.setUnionFunction(&ngUnionStrat); - return op.Union(); + geos::util::ProgressFunction* progressFunction = nullptr; + return op.Union(progressFunction); } /*public static*/ diff --git a/src/operation/polygonize/BuildArea.cpp b/src/operation/polygonize/BuildArea.cpp index 37ccd371eb..9f16ac6476 100644 --- a/src/operation/polygonize/BuildArea.cpp +++ b/src/operation/polygonize/BuildArea.cpp @@ -270,7 +270,7 @@ std::unique_ptr BuildArea::build(const geom::Geometry* geom) { /* Run a single overlay operation to dissolve shared edges */ auto shp = std::unique_ptr( - geos::operation::geounion::CascadedPolygonUnion::CascadedPolygonUnion::Union(tmp.get())); + geos::operation::geounion::CascadedPolygonUnion::CascadedPolygonUnion::Union(tmp.get(), nullptr)); if( shp ) { shp->setSRID(geom->getSRID()); } diff --git a/src/operation/union/CascadedPolygonUnion.cpp b/src/operation/union/CascadedPolygonUnion.cpp index 82cd91bcc7..bc1b12d290 100644 --- a/src/operation/union/CascadedPolygonUnion.cpp +++ b/src/operation/union/CascadedPolygonUnion.cpp @@ -47,18 +47,19 @@ std::unique_ptr CascadedPolygonUnion::Union(std::vector* polys) { CascadedPolygonUnion op(polys); - return op.Union(); + geos::util::ProgressFunction* progressFunction = nullptr; + return op.Union(progressFunction); } std::unique_ptr -CascadedPolygonUnion::Union(std::vector* polys, UnionStrategy* unionFun) +CascadedPolygonUnion::Union(std::vector* polys, UnionStrategy* unionFun, geos::util::ProgressFunction* progressFunction) { CascadedPolygonUnion op(polys, unionFun); - return op.Union(); + return op.Union(progressFunction); } std::unique_ptr -CascadedPolygonUnion::Union(const geom::MultiPolygon* multipoly) +CascadedPolygonUnion::Union(const geom::MultiPolygon* multipoly, geos::util::ProgressFunction* progressFunction) { std::vector polys; @@ -67,11 +68,11 @@ CascadedPolygonUnion::Union(const geom::MultiPolygon* multipoly) } CascadedPolygonUnion op(&polys); - return op.Union(); + return op.Union(progressFunction); } std::unique_ptr -CascadedPolygonUnion::Union() +CascadedPolygonUnion::Union(geos::util::ProgressFunction* progressFunction) { if(inputPolys->empty()) { return nullptr; @@ -94,28 +95,43 @@ CascadedPolygonUnion::Union() // TODO avoid creating this vector and run binaryUnion off the iterators directly std::vector geoms(index.items().begin(), index.items().end()); - return binaryUnion(geoms, 0, geoms.size()); + size_t inc = 0; + std::function UnitProgress = [progressFunction, &inc, &geoms]() + { + ++inc; + (*progressFunction)(static_cast(inc)/static_cast(geoms.size()), ""); + }; + + return binaryUnion(geoms, 0, geoms.size(), progressFunction ? &UnitProgress : nullptr); } std::unique_ptr CascadedPolygonUnion::binaryUnion(const std::vector & geoms, - std::size_t start, std::size_t end) + std::size_t start, std::size_t end, + std::function* unitProgress) { if(end - start == 0) { return nullptr; } else if(end - start == 1) { + if( unitProgress ) + (*unitProgress)(); return unionSafe(geoms[start], nullptr); } else if(end - start == 2) { + if( unitProgress ) + { + (*unitProgress)(); + (*unitProgress)(); + } return unionSafe(geoms[start], geoms[start + 1]); } else { // recurse on both halves of the list std::size_t mid = (end + start) / 2; - std::unique_ptr g0(binaryUnion(geoms, start, mid)); - std::unique_ptr g1(binaryUnion(geoms, mid, end)); + std::unique_ptr g0(binaryUnion(geoms, start, mid, unitProgress)); + std::unique_ptr g1(binaryUnion(geoms, mid, end, unitProgress)); return unionSafe(std::move(g0), std::move(g1)); } } diff --git a/src/operation/union/UnaryUnionOp.cpp b/src/operation/union/UnaryUnionOp.cpp index a801e56582..c894d7cfd3 100644 --- a/src/operation/union/UnaryUnionOp.cpp +++ b/src/operation/union/UnaryUnionOp.cpp @@ -64,7 +64,7 @@ UnaryUnionOp::unionWithNull(std::unique_ptr g0, /*public*/ std::unique_ptr -UnaryUnionOp::Union() +UnaryUnionOp::Union(geos::util::ProgressFunction* progressFunction) { typedef std::unique_ptr GeomPtr; @@ -95,7 +95,7 @@ UnaryUnionOp::Union() GeomPtr unionPolygons; if(!polygons.empty()) { - unionPolygons = CascadedPolygonUnion::Union(polygons.begin(), polygons.end(), unionFunction); + unionPolygons = CascadedPolygonUnion::Union(polygons.begin(), polygons.end(), unionFunction, progressFunction); } /* From 137d1451c243941ac65196faa6f14528e2e75467 Mon Sep 17 00:00:00 2001 From: Dan Baston Date: Tue, 8 Jul 2025 08:00:52 -0400 Subject: [PATCH 6/7] Add Progress class to simplify progress reporting --- include/geos/util/Progress.h | 77 +++++++------ .../CoverageBoundarySegmentFinder.cpp | 17 ++- src/coverage/CoverageEdge.cpp | 20 ++-- src/coverage/CoverageRingEdges.cpp | 102 ++++++++---------- src/coverage/TPVWSimplifier.cpp | 41 ++++--- src/coverage/VertexRingCounter.cpp | 19 ++-- src/util/Progress.cpp | 17 +-- 7 files changed, 128 insertions(+), 165 deletions(-) diff --git a/include/geos/util/Progress.h b/include/geos/util/Progress.h index c581c60acf..57c4ee000b 100644 --- a/include/geos/util/Progress.h +++ b/include/geos/util/Progress.h @@ -18,44 +18,54 @@ #include -namespace geos { -namespace util { // geos::util +namespace geos::util { -/** Signature of a progression function. +/** Signature of a progress function. * - * Such function takes a progression ratio (between 0 and 1), and an optional + * Such function takes a progress ratio (between 0 and 1), and an optional * message. */ -typedef std::function ProgressFunction; +using ProgressFunction = std::function; -/** Do progress function related processing for an iteration loop of iterCount iterations. - * - * This function will invoke (*progressFunction) every notificationInterval - *iteration. - * - * This function will return, or throw a geos::util::InterruptedException. - * - * A typical use is: - * \code - * const size_t notificationInterval = std::max(1, iterCount / 100); - * for (size_t i = 0, iNotify = 0; i < iterCount; ++i) { - * // do something useful - * if (progressFunction) { - * geos::util::ProgressFunctionIteration(*progressFunction, i, iterCount, iNotify, notificationInterval); - * } - * } - * if (progressFunction) { - * (*progressFunction)(1.0, nullptr); - * } - * \endcode - * - * @param progressFunction Progress function - * @param i Current index of loop iteration. - * @param iterCount Total number of loop iteration. - * @param iNotify Notification counter, updated by this function. Must be set by the caller (before the loop) to 0. - * @param notificationInterval Notification interval (e.g. iterCount / 100). - */ -void ProgressFunctionIteration(ProgressFunction& progressFunction, size_t i, size_t iterCount, size_t& iNotify, size_t notificationInterval); +class GEOS_DLL Progress { + +public: + Progress(const ProgressFunction* pCallback, size_t pIterCount) : + callback(pCallback), + i(0), + iterCount(pIterCount), + notificationInterval(std::max(1, iterCount / 100)), + iNotify(0) {} + + void setResolution(double res) { + notificationInterval = std::max(1, static_cast(static_cast(iterCount) * res)); + } + + void update() { + if (callback) { + if (iNotify + 1 == notificationInterval) { + (*callback)(static_cast(i + 1)/static_cast(iterCount), nullptr); + iNotify = 0; + } + else { + ++iNotify; + } + } + } + + void finish() const { + if (callback) { + (*callback)(1.0, nullptr); + } + } + +private: + const ProgressFunction* callback; + std::size_t i; + std::size_t iterCount; + std::size_t notificationInterval; + std::size_t iNotify; +}; /** Create a scaled progress function. * @@ -80,5 +90,4 @@ ProgressFunction CreateScaledProgressFunction(double ratioMin, double ratioMax, } // namespace geos::util -} // namespace geos diff --git a/src/coverage/CoverageBoundarySegmentFinder.cpp b/src/coverage/CoverageBoundarySegmentFinder.cpp index 4384f17628..6064620b3a 100644 --- a/src/coverage/CoverageBoundarySegmentFinder.cpp +++ b/src/coverage/CoverageBoundarySegmentFinder.cpp @@ -29,21 +29,16 @@ namespace coverage { // geos.coverage LineSegment::UnorderedSet CoverageBoundarySegmentFinder::findBoundarySegments( const std::vector& geoms, - geos::util::ProgressFunction* progressFunction) + util::ProgressFunction* progressFunction) { LineSegment::UnorderedSet segs; CoverageBoundarySegmentFinder finder(segs); - const size_t iterCount = geoms.size(); - const size_t notificationInterval = std::max(1, iterCount / 100); - for (size_t i = 0, iNotify = 0; i < iterCount; ++i) { - const Geometry* geom = geoms[i]; + + util::Progress progress(progressFunction, geoms.size()); + + for (const auto& geom : geoms) { geom->apply_ro(finder); - if (progressFunction) { - geos::util::ProgressFunctionIteration(*progressFunction, i, iterCount, iNotify, notificationInterval); - } - } - if (progressFunction) { - (*progressFunction)(1.0, nullptr); + progress.update(); } return segs; } diff --git a/src/coverage/CoverageEdge.cpp b/src/coverage/CoverageEdge.cpp index 6d1ae89390..ad53b03951 100644 --- a/src/coverage/CoverageEdge.cpp +++ b/src/coverage/CoverageEdge.cpp @@ -56,23 +56,21 @@ std::unique_ptr CoverageEdge::createLines( const std::vector& edges, const GeometryFactory* geomFactory, - geos::util::ProgressFunction* progressFunction) + util::ProgressFunction* progressFunction) { std::vector> lines; - const size_t iterCount = edges.size(); - const size_t notificationInterval = std::max(1, iterCount / 100); - for (size_t i = 0, iNotify = 0; i < iterCount; ++i) { - const CoverageEdge* edge = edges[i]; + + util::Progress progress(progressFunction, edges.size()); + + for (const CoverageEdge* edge : edges) { auto cs = edge->getCoordinates()->clone(); auto ls = geomFactory->createLineString(std::move(cs)); lines.push_back(std::move(ls)); - if (progressFunction) { - geos::util::ProgressFunctionIteration(*progressFunction, i, iterCount, iNotify, notificationInterval); - } - } - if (progressFunction) { - (*progressFunction)(1.0, nullptr); + progress.update(); } + + progress.finish(); + return geomFactory->createMultiLineString(std::move(lines)); } diff --git a/src/coverage/CoverageRingEdges.cpp b/src/coverage/CoverageRingEdges.cpp index 147b5c8b83..c8a607dcf7 100644 --- a/src/coverage/CoverageRingEdges.cpp +++ b/src/coverage/CoverageRingEdges.cpp @@ -61,23 +61,23 @@ CoverageRingEdges::selectEdges(std::size_t ringCount) const /* private */ void -CoverageRingEdges::build(geos::util::ProgressFunction* progressFunction) +CoverageRingEdges::build(util::ProgressFunction* progressFunction) { constexpr double RATIO_FIRST_PASS = 0.1; constexpr double RATIO_SECOND_PASS = 0.2; constexpr double RATIO_THIRD_PASS = 0.9; constexpr double RATIO_FOURTH_PASS = 1.0; - geos::util::ProgressFunction subProgress; + util::ProgressFunction subProgress; if (progressFunction) { - subProgress = geos::util::CreateScaledProgressFunction( + subProgress = util::CreateScaledProgressFunction( 0, RATIO_FIRST_PASS, *progressFunction); } Coordinate::UnorderedSet nodes = findMultiRingNodes(m_coverage, progressFunction ? &subProgress : nullptr); if (progressFunction) { - subProgress = geos::util::CreateScaledProgressFunction( + subProgress = util::CreateScaledProgressFunction( RATIO_FIRST_PASS, RATIO_SECOND_PASS, *progressFunction); } LineSegment::UnorderedSet boundarySegs = CoverageBoundarySegmentFinder::findBoundarySegments( @@ -85,7 +85,7 @@ CoverageRingEdges::build(geos::util::ProgressFunction* progressFunction) if (progressFunction) { - subProgress = geos::util::CreateScaledProgressFunction( + subProgress = util::CreateScaledProgressFunction( RATIO_SECOND_PASS, RATIO_THIRD_PASS, *progressFunction); } Coordinate::UnorderedSet boundaryNodes = findBoundaryNodes( @@ -94,14 +94,14 @@ CoverageRingEdges::build(geos::util::ProgressFunction* progressFunction) if (progressFunction) { - subProgress = geos::util::CreateScaledProgressFunction( + subProgress = util::CreateScaledProgressFunction( RATIO_THIRD_PASS, RATIO_FOURTH_PASS, *progressFunction); } std::map uniqueEdgeMap; - const size_t iterCount = m_coverage.size(); - const size_t notificationInterval = std::max(1, iterCount / 100); - for (size_t i = 0, iNotify = 0; i < iterCount; ++i) { - const Geometry* geom = m_coverage[i]; + + util::Progress progress(progressFunction ? &subProgress : nullptr, m_coverage.size()); + + for (const Geometry* geom : m_coverage) { for (std::size_t ipoly = 0; ipoly < geom->getNumGeometries(); ipoly++) { util::ensureNoCurvedComponents(geom->getGeometryN(ipoly)); @@ -124,13 +124,10 @@ CoverageRingEdges::build(geos::util::ProgressFunction* progressFunction) } } - if (progressFunction) { - geos::util::ProgressFunctionIteration(subProgress, i, iterCount, iNotify, notificationInterval); - } - } - if (progressFunction) { - (*progressFunction)(1.0, nullptr); + progress.update(); } + + progress.finish(); } /* private */ @@ -297,10 +294,10 @@ CoverageRingEdges::findMultiRingNodes(const std::vector& covera geos::util::ProgressFunction* progressFunction) { std::map vertexRingCount; - geos::util::ProgressFunction subProgress; + util::ProgressFunction subProgress; if (progressFunction) { - subProgress = geos::util::CreateScaledProgressFunction( + subProgress = util::CreateScaledProgressFunction( 0, 0.5, *progressFunction); } @@ -311,27 +308,23 @@ CoverageRingEdges::findMultiRingNodes(const std::vector& covera // nodes.add(v); // } // } - const size_t iterCount = vertexRingCount.size(); - const size_t notificationInterval = std::max(1, iterCount / 100); - size_t i = 0, iNotify = 0; if (progressFunction) { - subProgress = geos::util::CreateScaledProgressFunction( + subProgress = util::CreateScaledProgressFunction( 0.5, 1.0, *progressFunction); } + + util::Progress progress(progressFunction ? &subProgress : nullptr, vertexRingCount.size()); + for (const auto &mapPair : vertexRingCount) { const Coordinate& v = mapPair.first; std::size_t count = mapPair.second; if (count > 2) nodes.insert(v); - if (progressFunction) { - geos::util::ProgressFunctionIteration(subProgress, i, iterCount, iNotify, notificationInterval); - } - ++i; - } - if (progressFunction) { - (*progressFunction)(1.0, nullptr); + + progress.update(); } + progress.finish(); return nodes; } @@ -339,26 +332,23 @@ CoverageRingEdges::findMultiRingNodes(const std::vector& covera /* private */ Coordinate::UnorderedSet CoverageRingEdges::findBoundaryNodes(LineSegment::UnorderedSet& boundarySegments, - geos::util::ProgressFunction* progressFunction) + util::ProgressFunction* progressFunction) { std::unordered_map counter; - geos::util::ProgressFunction subProgress; + util::ProgressFunction subProgress; if (progressFunction) { subProgress = geos::util::CreateScaledProgressFunction( 0, 0.5, *progressFunction); } + + util::Progress progress(progressFunction ? &subProgress : nullptr, boundarySegments.size()); + { - const size_t iterCount = boundarySegments.size(); - const size_t notificationInterval = std::max(1, iterCount / 100); - size_t i = 0, iNotify = 0; for (const LineSegment& seg : boundarySegments) { counter[seg.p0] += 1; counter[seg.p1] += 1; - if (progressFunction) { - geos::util::ProgressFunctionIteration(subProgress, i, iterCount, iNotify, notificationInterval); - } - ++i; + progress.update(); } } @@ -366,45 +356,39 @@ CoverageRingEdges::findBoundaryNodes(LineSegment::UnorderedSet& boundarySegments { subProgress = geos::util::CreateScaledProgressFunction( 0.5, 1.0, *progressFunction); + progress = util::Progress(&subProgress, counter.size()); } Coordinate::UnorderedSet nodes; - const size_t iterCount = counter.size(); - const size_t notificationInterval = std::max(1, iterCount / 100); - size_t i = 0, iNotify = 0; for (const auto& c : counter) { const Coordinate& v = c.first; std::size_t count = c.second; if (count > 2) nodes.insert(v); - if (progressFunction) { - geos::util::ProgressFunctionIteration(subProgress, i, iterCount, iNotify, notificationInterval); - } - ++i; - } - if (progressFunction) { - (*progressFunction)(1.0, nullptr); + + progress.update(); } + + progress.finish(); + return nodes; } /* public */ std::vector> -CoverageRingEdges::buildCoverage(geos::util::ProgressFunction* progressFunction) const +CoverageRingEdges::buildCoverage(util::ProgressFunction* progressFunction) const { std::vector> result; - const size_t iterCount = m_coverage.size(); - const size_t notificationInterval = std::max(1, iterCount / 100); - for (size_t i = 0, iNotify = 0; i < iterCount; ++i) { - const Geometry* geom = m_coverage[i]; + + util::Progress progress(progressFunction, m_coverage.size()); + + for (const Geometry* geom : m_coverage) { result.push_back(buildPolygonal(geom)); - if (progressFunction) { - geos::util::ProgressFunctionIteration(*progressFunction, i, iterCount, iNotify, notificationInterval); - } - } - if (progressFunction) { - (*progressFunction)(1.0, nullptr); + progress.update(); } + + progress.finish(); + return result; } diff --git a/src/coverage/TPVWSimplifier.cpp b/src/coverage/TPVWSimplifier.cpp index c8a9c8ad2c..29fff16579 100644 --- a/src/coverage/TPVWSimplifier.cpp +++ b/src/coverage/TPVWSimplifier.cpp @@ -104,7 +104,7 @@ std::unique_ptr TPVWSimplifier::simplify(geos::util::ProgressFunction* progressFunction) { std::vector emptyList; - geos::util::ProgressFunction subProgress; + util::ProgressFunction subProgress; constexpr double RATIO_FIRST_PASS = 0.8; @@ -117,7 +117,7 @@ TPVWSimplifier::simplify(geos::util::ProgressFunction* progressFunction) if (progressFunction) { - subProgress = geos::util::CreateScaledProgressFunction( + subProgress = util::CreateScaledProgressFunction( 0, ratioInputLinesOverInputAndConstraint, *progressFunction); } std::vector edges = createEdges(inputLines, isFreeRing, @@ -125,7 +125,7 @@ TPVWSimplifier::simplify(geos::util::ProgressFunction* progressFunction) if (progressFunction) { - subProgress = geos::util::CreateScaledProgressFunction( + subProgress = util::CreateScaledProgressFunction( ratioInputLinesOverInputAndConstraint, RATIO_FIRST_PASS, *progressFunction); } std::vector constraintEdges = createEdges( @@ -137,25 +137,22 @@ TPVWSimplifier::simplify(geos::util::ProgressFunction* progressFunction) edgeIndex.add(constraintEdges); std::vector> result; - const size_t iterCount = edges.size(); - const size_t notificationInterval = std::max(1, iterCount / 100); if (progressFunction) { - subProgress = geos::util::CreateScaledProgressFunction( + subProgress = util::CreateScaledProgressFunction( RATIO_FIRST_PASS, 1.0, *progressFunction); } - for (size_t i = 0, iNotify = 0; i < iterCount; ++i) { - auto& edge = edges[i]; + + util::Progress progress(progressFunction ? &subProgress : nullptr, edges.size()); + + for (auto& edge : edges) { std::unique_ptr ptsSimp = edge.simplify(edgeIndex); auto ls = geomFactory->createLineString(std::move(ptsSimp)); result.emplace_back(ls.release()); - if (progressFunction) { - geos::util::ProgressFunctionIteration(subProgress, i, iterCount, iNotify, notificationInterval); - } - } - if (progressFunction) { - (*progressFunction)(1.0, nullptr); + progress.update(); } + + progress.finish(); return geomFactory->createMultiLineString(std::move(result)); } @@ -164,25 +161,23 @@ std::vector TPVWSimplifier::createEdges( const MultiLineString* lines, std::vector& freeRing, - geos::util::ProgressFunction* progressFunction) + util::ProgressFunction* progressFunction) { std::vector edges; if (lines == nullptr) return edges; const size_t iterCount = lines->getNumGeometries(); - const size_t notificationInterval = std::max(1, iterCount / 100); - for (size_t i = 0, iNotify = 0; i < iterCount; ++i) { + + util::Progress progress(progressFunction, lines->getNumGeometries()); + + for (size_t i = 0; i < iterCount; ++i) { const LineString* line = lines->getGeometryN(i); bool isFree = freeRing.empty() ? false : freeRing[i]; edges.emplace_back(line, isFree, areaTolerance); - if (progressFunction) { - geos::util::ProgressFunctionIteration(*progressFunction, i, iterCount, iNotify, notificationInterval); - } - } - if (progressFunction) { - (*progressFunction)(1.0, nullptr); + progress.update(); } + progress.finish(); return edges; } diff --git a/src/coverage/VertexRingCounter.cpp b/src/coverage/VertexRingCounter.cpp index e27a5665bf..5bffd81c61 100644 --- a/src/coverage/VertexRingCounter.cpp +++ b/src/coverage/VertexRingCounter.cpp @@ -35,21 +35,18 @@ void VertexRingCounter::count( const std::vector& geoms, std::map& counts, - geos::util::ProgressFunction* progressFunction) + util::ProgressFunction* progressFunction) { VertexRingCounter vertextCounter(counts); - const size_t iterCount = geoms.size(); - const size_t notificationInterval = std::max(1, iterCount / 100); - for (size_t i = 0, iNotify = 0; i < iterCount; ++i) { - const Geometry* geom = geoms[i]; + + util::Progress progress(progressFunction, geoms.size()); + + for (const Geometry* geom : geoms) { geom->apply_ro(vertextCounter); - if (progressFunction) { - geos::util::ProgressFunctionIteration(*progressFunction, i, iterCount, iNotify, notificationInterval); - } - } - if (progressFunction) { - (*progressFunction)(1.0, nullptr); + progress.update(); } + + progress.finish(); } diff --git a/src/util/Progress.cpp b/src/util/Progress.cpp index ab4041e683..887892b1b4 100644 --- a/src/util/Progress.cpp +++ b/src/util/Progress.cpp @@ -14,20 +14,7 @@ #include -namespace geos { -namespace util { // geos::util - -void ProgressFunctionIteration(ProgressFunction& progressFunction, size_t i, - size_t iterCount, size_t& iNotify, - size_t notificationInterval) { - if (iNotify + 1 == notificationInterval) { - progressFunction(static_cast(i + 1)/static_cast(iterCount), nullptr); - iNotify = 0; - } - else { - ++iNotify; - } -} +namespace geos::util { ProgressFunction CreateScaledProgressFunction(double ratioMin, double ratioMax, ProgressFunction& progressFunction) @@ -39,5 +26,3 @@ ProgressFunction CreateScaledProgressFunction(double ratioMin, double ratioMax, } } // namespace geos::util -} // namespace geos - From c3d5a9ed4248b2e3c0774224a24740b034514932 Mon Sep 17 00:00:00 2001 From: Dan Baston Date: Tue, 8 Jul 2025 14:28:33 -0400 Subject: [PATCH 7/7] ProgressFunction: add subProgress method --- capi/geos_ts_c.cpp | 12 +-- .../coverage/CoverageBoundarySegmentFinder.h | 3 +- include/geos/coverage/CoverageEdge.h | 2 +- include/geos/coverage/CoverageRingEdges.h | 10 +- include/geos/coverage/CoverageSimplifier.h | 22 ++--- include/geos/coverage/TPVWSimplifier.h | 12 +-- include/geos/coverage/VertexRingCounter.h | 2 +- include/geos/util/Progress.h | 98 ++++++++++++------- .../CoverageBoundarySegmentFinder.cpp | 4 +- src/coverage/CoverageEdge.cpp | 4 +- src/coverage/CoverageRingEdges.cpp | 77 ++++----------- src/coverage/CoverageSimplifier.cpp | 82 ++++------------ src/coverage/TPVWSimplifier.cpp | 32 ++---- src/coverage/VertexRingCounter.cpp | 4 +- src/util/Progress.cpp | 19 ++-- tests/unit/coverage/CoverageRingEdgesTest.cpp | 4 +- .../unit/coverage/CoverageSimplifierTest.cpp | 15 ++- tests/unit/coverage/TPVWSimplifierTest.cpp | 6 +- util/geosop/GeometryOp.cpp | 2 +- 19 files changed, 169 insertions(+), 241 deletions(-) diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp index 64f16f9c09..06131a6cce 100644 --- a/capi/geos_ts_c.cpp +++ b/capi/geos_ts_c.cpp @@ -1672,11 +1672,11 @@ extern "C" { GEOSProgressCallback_r progressFunc, void* progressUserData) { - geos::util::ProgressFunction progressFunction = + geos::util::ProgressFunction progressFunction( [progressFunc, progressUserData](double progress, const char* message) { progressFunc(progress, message, progressUserData); - }; + }); return execute(extHandle, [&]() { std::unique_ptr g3(g->Union(&progressFunction)); @@ -4554,11 +4554,11 @@ extern "C" { GEOSProgressCallback_r progressFunc, void* progressUserData) { - geos::util::ProgressFunction progressFunction = + geos::util::ProgressFunction progressFunction( [progressFunc, progressUserData](double progress, const char* message) { progressFunc(progress, message, progressUserData); - }; + }); using geos::coverage::CoverageSimplifier; @@ -4574,10 +4574,10 @@ extern "C" { CoverageSimplifier cov(coverage); std::vector> simple; if (preserveBoundary == 1) { - simple = cov.simplifyInner(tolerance, progressFunc ? &progressFunction : nullptr); + simple = cov.simplifyInner(tolerance, progressFunc ? progressFunction : geos::util::defaultProgress); } else if (preserveBoundary == 0) { - simple = cov.simplify(tolerance, progressFunc ? &progressFunction : nullptr); + simple = cov.simplify(tolerance, progressFunc ? progressFunction : geos::util::defaultProgress); } else return nullptr; diff --git a/include/geos/coverage/CoverageBoundarySegmentFinder.h b/include/geos/coverage/CoverageBoundarySegmentFinder.h index fb0945ca4a..3639561100 100644 --- a/include/geos/coverage/CoverageBoundarySegmentFinder.h +++ b/include/geos/coverage/CoverageBoundarySegmentFinder.h @@ -59,7 +59,8 @@ class CoverageBoundarySegmentFinder : public geos::geom::CoordinateSequenceFilte static LineSegment::UnorderedSet findBoundarySegments(const std::vector& geoms, - geos::util::ProgressFunction* progressFunction); + const util::ProgressFunction& progressFunction = util::defaultProgress); + static bool isBoundarySegment( const LineSegment::UnorderedSet& boundarySegs, diff --git a/include/geos/coverage/CoverageEdge.h b/include/geos/coverage/CoverageEdge.h index 910c2310e5..b695648396 100644 --- a/include/geos/coverage/CoverageEdge.h +++ b/include/geos/coverage/CoverageEdge.h @@ -118,7 +118,7 @@ class GEOS_DLL CoverageEdge { static std::unique_ptr createLines( const std::vector& edges, const GeometryFactory* geomFactory, - geos::util::ProgressFunction* progressFunction); + const util::ProgressFunction& progressFunction = util::defaultProgress); std::unique_ptr toLineString( const GeometryFactory* geomFactory); diff --git a/include/geos/coverage/CoverageRingEdges.h b/include/geos/coverage/CoverageRingEdges.h index 8378c9c0b6..6709672a22 100644 --- a/include/geos/coverage/CoverageRingEdges.h +++ b/include/geos/coverage/CoverageRingEdges.h @@ -74,7 +74,7 @@ class GEOS_DLL CoverageRingEdges { public: CoverageRingEdges(const std::vector& coverage, - geos::util::ProgressFunction* progressFunction) + const util::ProgressFunction& progressFunction = util::defaultProgress) : m_coverage(coverage) { build(progressFunction); @@ -101,12 +101,12 @@ class GEOS_DLL CoverageRingEdges { * @param progressFunction Progress function or null * @return an array of polygonal geometries representing the coverage */ - std::vector> buildCoverage(geos::util::ProgressFunction* progressFunction) const; + std::vector> buildCoverage(const util::ProgressFunction& progressFunction = util::defaultProgress) const; private: - void build(geos::util::ProgressFunction* progressFunction); + void build(const util::ProgressFunction& progressFunction = util::defaultProgress); void addRingEdges( const LinearRing* ring, @@ -144,11 +144,11 @@ class GEOS_DLL CoverageRingEdges { Coordinate::UnorderedSet findMultiRingNodes( const std::vector& coverage, - geos::util::ProgressFunction* progressFunction); + const util::ProgressFunction& progressFunction = util::defaultProgress); Coordinate::UnorderedSet findBoundaryNodes( LineSegment::UnorderedSet& lineSegments, - geos::util::ProgressFunction* progressFunction); + const util::ProgressFunction& progressFunction = util::defaultProgress); std::unique_ptr buildPolygonal( const Geometry* geom) const; diff --git a/include/geos/coverage/CoverageSimplifier.h b/include/geos/coverage/CoverageSimplifier.h index f3c1c21c42..bf788f283f 100644 --- a/include/geos/coverage/CoverageSimplifier.h +++ b/include/geos/coverage/CoverageSimplifier.h @@ -88,18 +88,18 @@ class GEOS_DLL CoverageSimplifier { * * @param coverage a set of polygonal geometries forming a coverage * @param tolerance the simplification tolerance - * @param progressFunction Progress function, or nullptr. + * @param progressFunction Progress function * @return the simplified polygons */ static std::vector> simplify( std::vector& coverage, double tolerance, - geos::util::ProgressFunction* progressFunction); + const util::ProgressFunction& progressFunction = util::defaultProgress); static std::vector> simplify( const std::vector>& coverage, double tolerance, - geos::util::ProgressFunction* progressFunction); + const util::ProgressFunction& progressFunction = util::defaultProgress); /** * Simplifies the inner boundaries of a set of polygonal geometries forming a coverage, @@ -108,28 +108,28 @@ class GEOS_DLL CoverageSimplifier { * * @param coverage a set of polygonal geometries forming a coverage * @param tolerance the simplification tolerance - * @param progressFunction Progress function, or nullptr. + * @param progressFunction Progress function * @return the simplified polygons */ static std::vector> simplifyInner( std::vector& coverage, double tolerance, - geos::util::ProgressFunction* progressFunction); + const util::ProgressFunction& progressFunction = util::defaultProgress); static std::vector> simplifyInner( const std::vector>& coverage, double tolerance, - geos::util::ProgressFunction* progressFunction); + const util::ProgressFunction& progressFunction = util::defaultProgress); /** * Computes the simplified coverage, preserving the coverage topology. * * @param tolerance the simplification tolerance - * @param progressFunction Progress function, or nullptr. + * @param progressFunction Progress function * @return the simplified polygons */ std::vector> simplify( - double tolerance, geos::util::ProgressFunction* progressFunction); + double tolerance, const util::ProgressFunction& progressFunction = util::defaultProgress); /** * Computes the inner-boundary simplified coverage, @@ -137,11 +137,11 @@ class GEOS_DLL CoverageSimplifier { * and leaving outer boundary edges unchanged. * * @param tolerance the simplification tolerance - * @param progressFunction Progress function, or nullptr. + * @param progressFunction Progress function * @return the simplified polygons */ std::vector> simplifyInner( - double tolerance, geos::util::ProgressFunction* progressFunction); + double tolerance, const util::ProgressFunction& progressFunction = util::defaultProgress); private: @@ -155,7 +155,7 @@ class GEOS_DLL CoverageSimplifier { std::vector edges, const MultiLineString* constraints, double tolerance, - geos::util::ProgressFunction* progressFunction); + const util::ProgressFunction& progressFunction); void setCoordinates( std::vector& edges, diff --git a/include/geos/coverage/TPVWSimplifier.h b/include/geos/coverage/TPVWSimplifier.h index ec2bce993f..fbe3cc5095 100644 --- a/include/geos/coverage/TPVWSimplifier.h +++ b/include/geos/coverage/TPVWSimplifier.h @@ -165,13 +165,13 @@ class GEOS_DLL TPVWSimplifier * * @param lines the lines to simplify * @param distanceTolerance the simplification tolerance - * @param progressFunction Progress function, or nullptr. + * @param progressFunction Progress function * @return the simplified lines */ static std::unique_ptr simplify( const MultiLineString* lines, double distanceTolerance, - geos::util::ProgressFunction* progressFunction); + const util::ProgressFunction& progressFunction = util::defaultProgress); /** * Simplifies a set of lines, preserving the topology of the lines between @@ -184,7 +184,7 @@ class GEOS_DLL TPVWSimplifier * @param freeRings flags indicating which ring edges do not have node endpoints * @param constraintLines the linear constraints * @param distanceTolerance the simplification tolerance - * @param progressFunction Progress function, or nullptr. + * @param progressFunction Progress function * @return the simplified lines */ static std::unique_ptr simplify( @@ -192,7 +192,7 @@ class GEOS_DLL TPVWSimplifier std::vector& freeRings, const MultiLineString* constraintLines, double distanceTolerance, - geos::util::ProgressFunction* progressFunction); + const util::ProgressFunction& progressFunction = util::defaultProgress); // Constructor TPVWSimplifier(const MultiLineString* lines, @@ -214,12 +214,12 @@ class GEOS_DLL TPVWSimplifier void setConstraints(const MultiLineString* constraints); - std::unique_ptr simplify(geos::util::ProgressFunction* progressFunction); + std::unique_ptr simplify(const util::ProgressFunction& progressFunction = util::defaultProgress); std::vector createEdges( const MultiLineString* lines, std::vector& freeRing, - geos::util::ProgressFunction* progressFunction); + const util::ProgressFunction& progressFunction = util::defaultProgress); }; // TPVWSimplifier diff --git a/include/geos/coverage/VertexRingCounter.h b/include/geos/coverage/VertexRingCounter.h index e67ffc963f..5cc242ca70 100644 --- a/include/geos/coverage/VertexRingCounter.h +++ b/include/geos/coverage/VertexRingCounter.h @@ -56,7 +56,7 @@ class VertexRingCounter : public geos::geom::CoordinateSequenceFilter static void count( const std::vector& geoms, std::map& counts, - geos::util::ProgressFunction* progressFunction); + const util::ProgressFunction& progressFunction = util::defaultProgress); private: diff --git a/include/geos/util/Progress.h b/include/geos/util/Progress.h index 57c4ee000b..34ff622d04 100644 --- a/include/geos/util/Progress.h +++ b/include/geos/util/Progress.h @@ -15,36 +15,88 @@ #pragma once #include +#include #include namespace geos::util { -/** Signature of a progress function. - * - * Such function takes a progress ratio (between 0 and 1), and an optional - * message. +/** A ProgressFunction is a wrapper around an optional user-defined callback, + * taking a progress ratio (between 0 and 1) and an optional message. If not + * provided with a callback at construction, the ProgressFunction will do nothing + * when invoked. */ -using ProgressFunction = std::function; +class GEOS_DLL ProgressFunction { +public: + ProgressFunction() : m_function(std::nullopt) {} + + ProgressFunction(std::function f) : m_function(f) {} + + void operator()(double percentage, const char* message) const { + if (m_function.has_value()) { + m_function.value()(percentage, message); + } + } + + /** Create a ProgressFunction to manage a subset of the work reported + * by this ProgressFunction. + * + * Sometimes when an operation wants to report progress, it actually + * invokes several subprocesses which also take a ProgressFunction, + * and it is desirable to map the progress of each sub operation into + * a portion of 0.0 to 1.0 progress of the overall process. The scaled + * progress function can be used for this. + * + * For each subsection a scaled progress function is created and + * instead of passing the overall progress func down to the sub functions, + * the scaled progress function is passed instead. + * + * @param from the completion fraction (0 to 1) to which 0.0 in the sub operation is mapped. + * @param to the completion fraction (0 to 1) to which 1.0 is the sub operation is mapped. + * + * @return scaled progress function. + */ + ProgressFunction subProgress(double from, double to) const; + + bool isSpecified() const { + return m_function.has_value(); + } + +private: + std::optional> m_function; +}; + +static const ProgressFunction defaultProgress; -class GEOS_DLL Progress { +/** A ProgressContext manages the invocation of a ProgressFunction at a specified frequency. + */ +class GEOS_DLL ProgressContext { public: - Progress(const ProgressFunction* pCallback, size_t pIterCount) : + /** Create a ProgressContext + * + * @param pCallback the function to call with progress updates + * @param pIterCount the total number of expected iterations + */ + ProgressContext(ProgressFunction pCallback, size_t pIterCount) : callback(pCallback), i(0), iterCount(pIterCount), notificationInterval(std::max(1, iterCount / 100)), iNotify(0) {} + /** Set the resolution of the progress reporting as a fraction from 0 to 1. By default, + * the progress function will be called for each 1% change in progress. + */ void setResolution(double res) { notificationInterval = std::max(1, static_cast(static_cast(iterCount) * res)); } + /** Update the progress. This method should be called once per iteration. */ void update() { - if (callback) { + if (callback.isSpecified()) { if (iNotify + 1 == notificationInterval) { - (*callback)(static_cast(i + 1)/static_cast(iterCount), nullptr); + callback(static_cast(i + 1)/static_cast(iterCount), nullptr); iNotify = 0; } else { @@ -54,40 +106,16 @@ class GEOS_DLL Progress { } void finish() const { - if (callback) { - (*callback)(1.0, nullptr); - } + callback(1.0, nullptr); } private: - const ProgressFunction* callback; + const ProgressFunction callback; std::size_t i; std::size_t iterCount; std::size_t notificationInterval; std::size_t iNotify; }; -/** Create a scaled progress function. - * - * Sometimes when an operation wants to report progress, it actually - * invokes several subprocesses which also take a ProgressFunction, - * and it is desirable to map the progress of each sub operation into - * a portion of 0.0 to 1.0 progress of the overall process. The scaled - * progress function can be used for this. - * - * For each subsection a scaled progress function is created and - * instead of passing the overall progress func down to the sub functions, - * the scale progress function is passed instead. - * - * @param ratioMin the value to which 0.0 in the sub operation is mapped. - * @param ratioMax the value to which 1.0 is the sub operation is mapped. - * @param progressFunction the overall progress function. - * - * @return scaled progress function. - */ -ProgressFunction CreateScaledProgressFunction(double ratioMin, double ratioMax, - ProgressFunction& progressFunction); - - } // namespace geos::util diff --git a/src/coverage/CoverageBoundarySegmentFinder.cpp b/src/coverage/CoverageBoundarySegmentFinder.cpp index 6064620b3a..c1fd187346 100644 --- a/src/coverage/CoverageBoundarySegmentFinder.cpp +++ b/src/coverage/CoverageBoundarySegmentFinder.cpp @@ -29,12 +29,12 @@ namespace coverage { // geos.coverage LineSegment::UnorderedSet CoverageBoundarySegmentFinder::findBoundarySegments( const std::vector& geoms, - util::ProgressFunction* progressFunction) + const util::ProgressFunction& progressFunction) { LineSegment::UnorderedSet segs; CoverageBoundarySegmentFinder finder(segs); - util::Progress progress(progressFunction, geoms.size()); + util::ProgressContext progress(progressFunction, geoms.size()); for (const auto& geom : geoms) { geom->apply_ro(finder); diff --git a/src/coverage/CoverageEdge.cpp b/src/coverage/CoverageEdge.cpp index ad53b03951..dd5bfe1eff 100644 --- a/src/coverage/CoverageEdge.cpp +++ b/src/coverage/CoverageEdge.cpp @@ -56,11 +56,11 @@ std::unique_ptr CoverageEdge::createLines( const std::vector& edges, const GeometryFactory* geomFactory, - util::ProgressFunction* progressFunction) + const util::ProgressFunction& progressFunction) { std::vector> lines; - util::Progress progress(progressFunction, edges.size()); + util::ProgressContext progress(progressFunction, edges.size()); for (const CoverageEdge* edge : edges) { auto cs = edge->getCoordinates()->clone(); diff --git a/src/coverage/CoverageRingEdges.cpp b/src/coverage/CoverageRingEdges.cpp index c8a607dcf7..9801f381e5 100644 --- a/src/coverage/CoverageRingEdges.cpp +++ b/src/coverage/CoverageRingEdges.cpp @@ -61,45 +61,25 @@ CoverageRingEdges::selectEdges(std::size_t ringCount) const /* private */ void -CoverageRingEdges::build(util::ProgressFunction* progressFunction) +CoverageRingEdges::build(const util::ProgressFunction& progressFunction) { constexpr double RATIO_FIRST_PASS = 0.1; constexpr double RATIO_SECOND_PASS = 0.2; constexpr double RATIO_THIRD_PASS = 0.9; constexpr double RATIO_FOURTH_PASS = 1.0; - util::ProgressFunction subProgress; - if (progressFunction) - { - subProgress = util::CreateScaledProgressFunction( - 0, RATIO_FIRST_PASS, *progressFunction); - } - Coordinate::UnorderedSet nodes = findMultiRingNodes(m_coverage, progressFunction ? &subProgress : nullptr); - if (progressFunction) - { - subProgress = util::CreateScaledProgressFunction( - RATIO_FIRST_PASS, RATIO_SECOND_PASS, *progressFunction); - } + Coordinate::UnorderedSet nodes = findMultiRingNodes(m_coverage, progressFunction.subProgress(0, RATIO_FIRST_PASS)); + LineSegment::UnorderedSet boundarySegs = CoverageBoundarySegmentFinder::findBoundarySegments( - m_coverage, progressFunction ? &subProgress : nullptr); + m_coverage, progressFunction.subProgress(RATIO_FIRST_PASS, RATIO_SECOND_PASS)); - if (progressFunction) - { - subProgress = util::CreateScaledProgressFunction( - RATIO_SECOND_PASS, RATIO_THIRD_PASS, *progressFunction); - } Coordinate::UnorderedSet boundaryNodes = findBoundaryNodes( - boundarySegs, progressFunction ? &subProgress : nullptr); + boundarySegs, progressFunction.subProgress(RATIO_SECOND_PASS, RATIO_THIRD_PASS)); nodes.insert(boundaryNodes.begin(), boundaryNodes.end()); - if (progressFunction) - { - subProgress = util::CreateScaledProgressFunction( - RATIO_THIRD_PASS, RATIO_FOURTH_PASS, *progressFunction); - } std::map uniqueEdgeMap; - util::Progress progress(progressFunction ? &subProgress : nullptr, m_coverage.size()); + util::ProgressContext progress(progressFunction.subProgress(RATIO_THIRD_PASS, RATIO_FOURTH_PASS), m_coverage.size()); for (const Geometry* geom : m_coverage) { for (std::size_t ipoly = 0; ipoly < geom->getNumGeometries(); ipoly++) { @@ -291,30 +271,19 @@ CoverageRingEdges::next(std::size_t index, const CoordinateSequence& ring) /* private */ Coordinate::UnorderedSet CoverageRingEdges::findMultiRingNodes(const std::vector& coverage, - geos::util::ProgressFunction* progressFunction) + const util::ProgressFunction& progressFunction) { std::map vertexRingCount; - util::ProgressFunction subProgress; - if (progressFunction) - { - subProgress = util::CreateScaledProgressFunction( - 0, 0.5, *progressFunction); - } - VertexRingCounter::count(coverage, vertexRingCount, progressFunction ? &subProgress : nullptr); + VertexRingCounter::count(coverage, vertexRingCount, progressFunction.subProgress(0, 0.5)); Coordinate::UnorderedSet nodes; // for (Coordinate v : vertexCount.keySet()) { // if (vertexCount.get(v) > 2) { // nodes.add(v); // } // } - if (progressFunction) - { - subProgress = util::CreateScaledProgressFunction( - 0.5, 1.0, *progressFunction); - } - util::Progress progress(progressFunction ? &subProgress : nullptr, vertexRingCount.size()); + util::ProgressContext progress(progressFunction.subProgress(0.5, 1.0), vertexRingCount.size()); for (const auto &mapPair : vertexRingCount) { const Coordinate& v = mapPair.first; @@ -332,32 +301,22 @@ CoverageRingEdges::findMultiRingNodes(const std::vector& covera /* private */ Coordinate::UnorderedSet CoverageRingEdges::findBoundaryNodes(LineSegment::UnorderedSet& boundarySegments, - util::ProgressFunction* progressFunction) + const util::ProgressFunction& progressFunction) { std::unordered_map counter; - util::ProgressFunction subProgress; - if (progressFunction) - { - subProgress = geos::util::CreateScaledProgressFunction( - 0, 0.5, *progressFunction); - } - util::Progress progress(progressFunction ? &subProgress : nullptr, boundarySegments.size()); + util::ProgressContext progress1(progressFunction.subProgress(0, 0.5), boundarySegments.size()); { for (const LineSegment& seg : boundarySegments) { counter[seg.p0] += 1; counter[seg.p1] += 1; - progress.update(); + progress1.update(); } } - if (progressFunction) - { - subProgress = geos::util::CreateScaledProgressFunction( - 0.5, 1.0, *progressFunction); - progress = util::Progress(&subProgress, counter.size()); - } + util::ProgressContext progress2(progressFunction.subProgress(0.5, 1.0), counter.size()); + Coordinate::UnorderedSet nodes; for (const auto& c : counter) { const Coordinate& v = c.first; @@ -365,10 +324,10 @@ CoverageRingEdges::findBoundaryNodes(LineSegment::UnorderedSet& boundarySegments if (count > 2) nodes.insert(v); - progress.update(); + progress2.update(); } - progress.finish(); + progress2.finish(); return nodes; } @@ -376,11 +335,11 @@ CoverageRingEdges::findBoundaryNodes(LineSegment::UnorderedSet& boundarySegments /* public */ std::vector> -CoverageRingEdges::buildCoverage(util::ProgressFunction* progressFunction) const +CoverageRingEdges::buildCoverage(const util::ProgressFunction& progressFunction) const { std::vector> result; - util::Progress progress(progressFunction, m_coverage.size()); + util::ProgressContext progress(progressFunction, m_coverage.size()); for (const Geometry* geom : m_coverage) { result.push_back(buildPolygonal(geom)); diff --git a/src/coverage/CoverageSimplifier.cpp b/src/coverage/CoverageSimplifier.cpp index bdbe92a3eb..e80f49c960 100644 --- a/src/coverage/CoverageSimplifier.cpp +++ b/src/coverage/CoverageSimplifier.cpp @@ -36,7 +36,7 @@ std::vector> CoverageSimplifier::simplify( std::vector& coverage, double tolerance, - ProgressFunction* progressFunction) + const ProgressFunction& progressFunction) { CoverageSimplifier simplifier(coverage); return simplifier.simplify(tolerance, progressFunction); @@ -47,7 +47,7 @@ std::vector> CoverageSimplifier::simplify( const std::vector>& coverage, double tolerance, - ProgressFunction* progressFunction) + const ProgressFunction& progressFunction) { std::vector geoms; for (auto& geom : coverage) { @@ -62,7 +62,7 @@ std::vector> CoverageSimplifier::simplifyInner( std::vector& coverage, double tolerance, - ProgressFunction* progressFunction) + const ProgressFunction& progressFunction) { CoverageSimplifier simplifier(coverage); return simplifier.simplifyInner(tolerance, progressFunction); @@ -74,7 +74,7 @@ std::vector> CoverageSimplifier::simplifyInner( const std::vector>& coverage, double tolerance, - ProgressFunction* progressFunction) + const ProgressFunction& progressFunction) { std::vector geoms; for (auto& geom : coverage) { @@ -99,63 +99,27 @@ CoverageSimplifier::CoverageSimplifier(const std::vector& cover /* public */ std::vector> CoverageSimplifier::simplify(double tolerance, - ProgressFunction* progressFunction) + const ProgressFunction& progressFunction) { - geos::util::ProgressFunction subProgress; - if (progressFunction) - { - subProgress = geos::util::CreateScaledProgressFunction( - 0, 0.8, *progressFunction); - } - CoverageRingEdges cov(m_input, progressFunction ? &subProgress : nullptr); - if (progressFunction) - { - subProgress = geos::util::CreateScaledProgressFunction( - 0.8, 0.9, *progressFunction); - } - simplifyEdges(cov.getEdges(), nullptr, tolerance, progressFunction ? &subProgress : nullptr); - if (progressFunction) - { - subProgress = geos::util::CreateScaledProgressFunction( - 0.9, 1.0, *progressFunction); - } - return cov.buildCoverage(progressFunction ? &subProgress : nullptr); + CoverageRingEdges cov(m_input, progressFunction.subProgress(0, 0.8)); + simplifyEdges(cov.getEdges(), nullptr, tolerance, progressFunction.subProgress(0.8, 0.9)); + return cov.buildCoverage(progressFunction.subProgress(0.9, 1.0)); } /* public */ std::vector> CoverageSimplifier::simplifyInner(double tolerance, - ProgressFunction* progressFunction) + const ProgressFunction& progressFunction) { - geos::util::ProgressFunction subProgress; - if (progressFunction) - { - subProgress = geos::util::CreateScaledProgressFunction( - 0, 0.7, *progressFunction); - } - CoverageRingEdges cov(m_input, progressFunction ? &subProgress : nullptr); + CoverageRingEdges cov(m_input, progressFunction.subProgress(0, 0.7)); std::vector innerEdges = cov.selectEdges(2); std::vector outerEdges = cov.selectEdges(1); - if (progressFunction) - { - subProgress = geos::util::CreateScaledProgressFunction( - 0.7, 0.8, *progressFunction); - } + std::unique_ptr constraintEdges = CoverageEdge::createLines( - outerEdges, m_geomFactory, progressFunction ? &subProgress : nullptr); + outerEdges, m_geomFactory, progressFunction.subProgress(0.7, 0.8)); - if (progressFunction) - { - subProgress = geos::util::CreateScaledProgressFunction( - 0.8, 0.9, *progressFunction); - } - simplifyEdges(innerEdges, constraintEdges.get(), tolerance, progressFunction ? &subProgress : nullptr); - if (progressFunction) - { - subProgress = geos::util::CreateScaledProgressFunction( - 0.9, 1.0, *progressFunction); - } - return cov.buildCoverage(progressFunction ? &subProgress : nullptr); + simplifyEdges(innerEdges, constraintEdges.get(), tolerance, progressFunction.subProgress(0.8, 0.9)); + return cov.buildCoverage(progressFunction.subProgress(0.9, 1.0)); } /* private */ @@ -164,26 +128,16 @@ CoverageSimplifier::simplifyEdges( std::vector edges, const MultiLineString* constraints, double tolerance, - ProgressFunction* progressFunction) + const ProgressFunction& progressFunction) { constexpr double RATIO_FIRST_PASS = 0.5; - geos::util::ProgressFunction subProgress; - if (progressFunction) - { - subProgress = geos::util::CreateScaledProgressFunction( - 0, RATIO_FIRST_PASS, *progressFunction); - } + std::unique_ptr lines = CoverageEdge::createLines( - edges, m_geomFactory, progressFunction ? &subProgress : nullptr); + edges, m_geomFactory, progressFunction.subProgress(0, RATIO_FIRST_PASS)); std::vector freeRings = getFreeRings(edges); - if (progressFunction) - { - subProgress = geos::util::CreateScaledProgressFunction( - RATIO_FIRST_PASS, 1, *progressFunction); - } std::unique_ptr linesSimp = TPVWSimplifier::simplify( lines.get(), freeRings, constraints, tolerance, - progressFunction ? &subProgress : nullptr); + progressFunction.subProgress(RATIO_FIRST_PASS, 1.0)); //Assert: mlsSimp.getNumGeometries = edges.length setCoordinates(edges, linesSimp.get()); diff --git a/src/coverage/TPVWSimplifier.cpp b/src/coverage/TPVWSimplifier.cpp index 29fff16579..a6087c4227 100644 --- a/src/coverage/TPVWSimplifier.cpp +++ b/src/coverage/TPVWSimplifier.cpp @@ -48,7 +48,7 @@ std::unique_ptr TPVWSimplifier::simplify( const MultiLineString* lines, double distanceTolerance, - geos::util::ProgressFunction* progressFunction) + const util::ProgressFunction& progressFunction) { TPVWSimplifier simp(lines, distanceTolerance); std::unique_ptr result = simp.simplify(progressFunction); @@ -63,7 +63,7 @@ TPVWSimplifier::simplify( std::vector& p_freeRings, const MultiLineString* p_constraintLines, double distanceTolerance, - geos::util::ProgressFunction* progressFunction) + const util::ProgressFunction& progressFunction) { TPVWSimplifier simp(p_lines, distanceTolerance); simp.setFreeRingIndices(p_freeRings); @@ -101,10 +101,9 @@ TPVWSimplifier::setFreeRingIndices(std::vector& freeRing) /* private */ std::unique_ptr -TPVWSimplifier::simplify(geos::util::ProgressFunction* progressFunction) +TPVWSimplifier::simplify(const util::ProgressFunction& progressFunction) { std::vector emptyList; - util::ProgressFunction subProgress; constexpr double RATIO_FIRST_PASS = 0.8; @@ -115,35 +114,20 @@ TPVWSimplifier::simplify(geos::util::ProgressFunction* progressFunction) (inputLines ? inputLines->getNumGeometries() : 0) + (constraintLines ? constraintLines->getNumGeometries() : 0))); - if (progressFunction) - { - subProgress = util::CreateScaledProgressFunction( - 0, ratioInputLinesOverInputAndConstraint, *progressFunction); - } std::vector edges = createEdges(inputLines, isFreeRing, - progressFunction ? &subProgress : nullptr); + progressFunction.subProgress(0, ratioInputLinesOverInputAndConstraint)); - if (progressFunction) - { - subProgress = util::CreateScaledProgressFunction( - ratioInputLinesOverInputAndConstraint, RATIO_FIRST_PASS, *progressFunction); - } std::vector constraintEdges = createEdges( constraintLines, emptyList, - progressFunction ? &subProgress : nullptr); + progressFunction.subProgress(ratioInputLinesOverInputAndConstraint, RATIO_FIRST_PASS)); EdgeIndex edgeIndex; edgeIndex.add(edges); edgeIndex.add(constraintEdges); std::vector> result; - if (progressFunction) - { - subProgress = util::CreateScaledProgressFunction( - RATIO_FIRST_PASS, 1.0, *progressFunction); - } - util::Progress progress(progressFunction ? &subProgress : nullptr, edges.size()); + util::ProgressContext progress(progressFunction.subProgress(RATIO_FIRST_PASS, 1.0), edges.size()); for (auto& edge : edges) { std::unique_ptr ptsSimp = edge.simplify(edgeIndex); @@ -161,7 +145,7 @@ std::vector TPVWSimplifier::createEdges( const MultiLineString* lines, std::vector& freeRing, - util::ProgressFunction* progressFunction) + const util::ProgressFunction& progressFunction) { std::vector edges; @@ -169,7 +153,7 @@ TPVWSimplifier::createEdges( return edges; const size_t iterCount = lines->getNumGeometries(); - util::Progress progress(progressFunction, lines->getNumGeometries()); + util::ProgressContext progress(progressFunction, lines->getNumGeometries()); for (size_t i = 0; i < iterCount; ++i) { const LineString* line = lines->getGeometryN(i); diff --git a/src/coverage/VertexRingCounter.cpp b/src/coverage/VertexRingCounter.cpp index 5bffd81c61..bccfde3383 100644 --- a/src/coverage/VertexRingCounter.cpp +++ b/src/coverage/VertexRingCounter.cpp @@ -35,11 +35,11 @@ void VertexRingCounter::count( const std::vector& geoms, std::map& counts, - util::ProgressFunction* progressFunction) + const util::ProgressFunction& progressFunction) { VertexRingCounter vertextCounter(counts); - util::Progress progress(progressFunction, geoms.size()); + util::ProgressContext progress(progressFunction, geoms.size()); for (const Geometry* geom : geoms) { geom->apply_ro(vertextCounter); diff --git a/src/util/Progress.cpp b/src/util/Progress.cpp index 887892b1b4..4a89c62d4f 100644 --- a/src/util/Progress.cpp +++ b/src/util/Progress.cpp @@ -16,13 +16,16 @@ namespace geos::util { -ProgressFunction CreateScaledProgressFunction(double ratioMin, double ratioMax, - ProgressFunction& progressFunction) -{ - return [ratioMin, ratioMax, &progressFunction](double ratio, const char* msg) - { - progressFunction(ratioMin + (ratioMax - ratioMin) * ratio, msg); - }; +ProgressFunction +ProgressFunction::subProgress(double from, double to) const { + if (m_function.has_value()) { + return ProgressFunction([from, to, this](double ratio, const char* msg) + { + m_function.value()(from + (to - from) * ratio, msg); + }); + } + + return ProgressFunction(); } -} // namespace geos::util +} \ No newline at end of file diff --git a/tests/unit/coverage/CoverageRingEdgesTest.cpp b/tests/unit/coverage/CoverageRingEdgesTest.cpp index 5aade86ea6..bf0fdc610e 100644 --- a/tests/unit/coverage/CoverageRingEdgesTest.cpp +++ b/tests/unit/coverage/CoverageRingEdgesTest.cpp @@ -32,7 +32,7 @@ struct test_coverageringedges_data { std::unique_ptr geom = r.read(wkt); std::vector polygons = toArray(*geom); - CoverageRingEdges cov(polygons, nullptr); + CoverageRingEdges cov(polygons); std::vector& edges = cov.getEdges(); std::unique_ptr edgeLines = toArray(edges, geom->getFactory()); std::unique_ptr expected = r.read(wktExpected); @@ -44,7 +44,7 @@ struct test_coverageringedges_data { std::unique_ptr geom = r.read(wkt); auto polygons = toArray(*geom); - CoverageRingEdges covEdges(polygons, nullptr); + CoverageRingEdges covEdges(polygons); auto edges = covEdges.selectEdges(ringCount); auto edgeLines = toArray(edges, geom->getFactory()); std::unique_ptr expected = r.read(wktExpected); diff --git a/tests/unit/coverage/CoverageSimplifierTest.cpp b/tests/unit/coverage/CoverageSimplifierTest.cpp index 187ed7e74b..725ddcdd89 100644 --- a/tests/unit/coverage/CoverageSimplifierTest.cpp +++ b/tests/unit/coverage/CoverageSimplifierTest.cpp @@ -27,7 +27,7 @@ struct test_coveragesimplifier_data { void checkNoop( const std::vector>& input) { - std::vector> actual = CoverageSimplifier::simplify(input, 0, nullptr); + std::vector> actual = CoverageSimplifier::simplify(input, 0); // std::cout << w.write(*input[0]) << std::endl; // std::cout << w.write(*input[1]) << std::endl; @@ -44,13 +44,12 @@ struct test_coveragesimplifier_data { const std::vector>& expected) { double lastRatio = 0.0; - geos::util::ProgressFunction myProgress = [&lastRatio](double ratio, const char*) + geos::util::ProgressFunction myProgress([&lastRatio](double ratio, const char*) { ensure("ratio >= lastRatio", ratio >= lastRatio); lastRatio = ratio; - return true; - }; - std::vector> actual = CoverageSimplifier::simplify(input, tolerance, &myProgress); + }); + std::vector> actual = CoverageSimplifier::simplify(input, tolerance, myProgress); checkArrayEqual(expected, actual); ensure("lastRatio == 1.0", lastRatio == 1.0); } @@ -60,7 +59,7 @@ struct test_coveragesimplifier_data { double tolerance, const std::vector>& expected) { - std::vector> actual = CoverageSimplifier::simplifyInner(input, tolerance, nullptr); + std::vector> actual = CoverageSimplifier::simplifyInner(input, tolerance); checkArrayEqual(expected, actual); } @@ -481,7 +480,7 @@ void object::test<30> () }); try { std::vector> result = - CoverageSimplifier::simplify(input, 10, nullptr); + CoverageSimplifier::simplify(input, 10); } catch (geos::util::IllegalArgumentException&) { ensure("caught IllegalArgumentException", true); @@ -501,7 +500,7 @@ void object::test<31> () }); try { std::vector> result = - CoverageSimplifier::simplify(input, 10, nullptr); + CoverageSimplifier::simplify(input, 10); } catch (geos::util::IllegalArgumentException&) { ensure("caught IllegalArgumentException", true); diff --git a/tests/unit/coverage/TPVWSimplifierTest.cpp b/tests/unit/coverage/TPVWSimplifierTest.cpp index e8762ce426..ce55c99656 100644 --- a/tests/unit/coverage/TPVWSimplifierTest.cpp +++ b/tests/unit/coverage/TPVWSimplifierTest.cpp @@ -31,7 +31,7 @@ struct test_tpvwsimplifier_data { { std::unique_ptr geom = r.read(wkt); const MultiLineString* mls = static_cast(geom.get()); - std::unique_ptr actual = TPVWSimplifier::simplify(mls, tolerance, nullptr); + std::unique_ptr actual = TPVWSimplifier::simplify(mls, tolerance); ensure_equals_geometry(actual.get(), geom.get()); } @@ -42,7 +42,7 @@ struct test_tpvwsimplifier_data { const std::string& wktExpected) { auto mls = r.read(wkt); - std::unique_ptr actual = TPVWSimplifier::simplify(mls.get(), tolerance, nullptr); + std::unique_ptr actual = TPVWSimplifier::simplify(mls.get(), tolerance); std::unique_ptr expected = r.read(wktExpected); ensure_equals_geometry(actual.get(), expected.get()); } @@ -76,7 +76,7 @@ struct test_tpvwsimplifier_data { constraints = r.read(wktConstraints); } - std::unique_ptr actual = TPVWSimplifier::simplify(lines.get(), freeRings, constraints.get(), tolerance, nullptr); + std::unique_ptr actual = TPVWSimplifier::simplify(lines.get(), freeRings, constraints.get(), tolerance); std::unique_ptr expected = r.read(wktExpected); // std::cout << "-- actual" << std::endl; diff --git a/util/geosop/GeometryOp.cpp b/util/geosop/GeometryOp.cpp index 4c1d7f8208..986d1856a4 100644 --- a/util/geosop/GeometryOp.cpp +++ b/util/geosop/GeometryOp.cpp @@ -968,7 +968,7 @@ std::vector opRegistry { [](const Geometry& geom, double d) { std::vector coverage = toList(geom); std::vector> result - = geos::coverage::CoverageSimplifier::simplify(coverage, d, nullptr); + = geos::coverage::CoverageSimplifier::simplify(coverage, d); //-- convert list type (be nice to avoid this) std::vector> resultList; for (std::size_t i = 0; i < result.size(); i++) {