From git at osgeo.org Wed Jul 1 07:26:06 2026 From: git at osgeo.org (git at osgeo.org) Date: Wed, 1 Jul 2026 07:26:06 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. 5ffc04e4be506fe32ef237851ccc60ff400449de Message-ID: <20260701142606.DFFB11AF089@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via 5ffc04e4be506fe32ef237851ccc60ff400449de (commit) from b54d1466e1c699f418770a4176ceb12d3cf7c6c6 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 5ffc04e4be506fe32ef237851ccc60ff400449de Author: Daniel Baston Date: Wed Jul 1 10:25:45 2026 -0400 geos_c.h: Remove prototype for unimplemented GEOS_interruptThread() (#1461) diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in index 56b0abc6c..8f706e543 100644 --- a/capi/geos_c.h.in +++ b/capi/geos_c.h.in @@ -420,12 +420,6 @@ extern GEOSContextInterruptCallback GEOS_DLL *GEOSContext_setInterruptCallback_r */ extern void GEOS_DLL GEOS_interruptRequest(void); -/** -* Interrupt the current thread. -* \since 3.14 -*/ -extern void GEOS_DLL GEOS_interruptThread(void); - /** * Cancel a pending interruption request * \since 3.4 ----------------------------------------------------------------------- Summary of changes: capi/geos_c.h.in | 6 ------ 1 file changed, 6 deletions(-) hooks/post-receive -- GEOS From git at osgeo.org Wed Jul 1 08:51:31 2026 From: git at osgeo.org (git at osgeo.org) Date: Wed, 1 Jul 2026 08:51:31 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch dependabot/github_actions/all-actions-75c878a17d created. 75a7aee174a6fcdefaf38f03e7f051e4fbc249c6 Message-ID: <20260701155131.2E7E31AFEA0@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, dependabot/github_actions/all-actions-75c878a17d has been created at 75a7aee174a6fcdefaf38f03e7f051e4fbc249c6 (commit) - Log ----------------------------------------------------------------- commit 75a7aee174a6fcdefaf38f03e7f051e4fbc249c6 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed Jul 1 15:51:08 2026 +0000 Bump the all-actions group with 6 updates Bumps the all-actions group with 6 updates: | Package | From | To | | --- | --- | --- | | [actions/checkout](https://github.com/actions/checkout) | `6.0.2` | `7.0.0` | | [actions/cache](https://github.com/actions/cache) | `5` | `6.1.0` | | [codecov/codecov-action](https://github.com/codecov/codecov-action) | `6` | `7` | | [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) | `1.4.5` | `1.4.8` | | [github/codeql-action](https://github.com/github/codeql-action) | `4.36.0` | `4.36.2` | | [docker/build-push-action](https://github.com/docker/build-push-action) | `7` | `7.2.0` | Updates `actions/checkout` from 6.0.2 to 7.0.0 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v6.0.2...v7.0.0) Updates `actions/cache` from 5 to 6.1.0 - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v5...v6.1.0) Updates `codecov/codecov-action` from 6 to 7 - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v6...v7) Updates `vmactions/freebsd-vm` from 1.4.5 to 1.4.8 - [Release notes](https://github.com/vmactions/freebsd-vm/releases) - [Commits](https://github.com/vmactions/freebsd-vm/compare/v1.4.5...v1.4.8) Updates `github/codeql-action` from 4.36.0 to 4.36.2 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v4.36.0...v4.36.2) Updates `docker/build-push-action` from 7 to 7.2.0 - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v7...v7.2.0) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: all-actions - dependency-name: actions/cache dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: all-actions - dependency-name: codecov/codecov-action dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major dependency-group: all-actions - dependency-name: vmactions/freebsd-vm dependency-version: 1.4.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: github/codeql-action dependency-version: 4.36.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: docker/build-push-action dependency-version: 7.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions ... Signed-off-by: dependabot[bot] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f7a5e237..c493e6f3b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -197,10 +197,10 @@ jobs: python3 -m pip install --disable-pip-version-check --user cmake==${{ matrix.ci.cmake }} echo "$(python3 -m site --user-base)/bin" >> $GITHUB_PATH - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 - name: Retrieve build cache - uses: actions/cache/restore at v5 + uses: actions/cache/restore at v6.1.0 id: restore-cache with: path: .ccache @@ -227,7 +227,7 @@ jobs: ccache --show-stats - name: Save build cache - uses: actions/cache/save at v5 + uses: actions/cache/save at v6.1.0 with: path: .ccache key: ${{ steps.restore-cache.outputs.cache-primary-key }} @@ -263,7 +263,7 @@ jobs: - name: Upload Coverage to Codecov if: ${{ matrix.ci.build_type == 'Coverage' && !cancelled() }} - uses: codecov/codecov-action at v6 + uses: codecov/codecov-action at v7 with: fail_ci_if_error: false files: build/coverage.info @@ -281,7 +281,7 @@ jobs: build_type: ['Debug', 'Release'] steps: - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 - name: 'Setup' uses: msys2/setup-msys2 at v2 @@ -295,7 +295,7 @@ jobs: mingw-w64-x86_64-gcc - name: Retrieve build cache - uses: actions/cache/restore at v5 + uses: actions/cache/restore at v6.1.0 id: restore-cache with: path: .ccache @@ -316,7 +316,7 @@ jobs: ccache --show-stats --verbose - name: Save build cache - uses: actions/cache/save at v5 + uses: actions/cache/save at v6.1.0 with: path: .ccache key: ${{ steps.restore-cache.outputs.cache-primary-key }} @@ -337,7 +337,7 @@ jobs: arch: arm64 steps: - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 # ccache not supported for this generator and/or Debug @@ -378,10 +378,10 @@ jobs: runs-on: ${{ matrix.ci.os }} steps: - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 - name: Retrieve build cache - uses: actions/cache/restore at v5 + uses: actions/cache/restore at v6.1.0 id: restore-cache with: path: .ccache @@ -415,7 +415,7 @@ jobs: ccache --show-stats --verbose - name: Save build cache - uses: actions/cache/save at v5 + uses: actions/cache/save at v6.1.0 with: path: .ccache key: ${{ steps.restore-cache.outputs.cache-primary-key }} @@ -459,10 +459,10 @@ jobs: which clang++ clang++ --version - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 - name: Retrieve build cache - uses: actions/cache/restore at v5 + uses: actions/cache/restore at v6.1.0 id: restore-cache with: path: .ccache @@ -482,7 +482,7 @@ jobs: ccache --show-stats - name: Save build cache - uses: actions/cache/save at v5 + uses: actions/cache/save at v6.1.0 with: path: .ccache key: ${{ steps.restore-cache.outputs.cache-primary-key }} @@ -495,10 +495,10 @@ jobs: name: FreeBSD runs-on: ubuntu-latest steps: - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 - name: Retrieve build cache - uses: actions/cache/restore at v5 + uses: actions/cache/restore at v6.1.0 id: restore-cache with: path: .ccache @@ -506,7 +506,7 @@ jobs: restore-keys: freebsd - name: Build and test - uses: vmactions/freebsd-vm at v1.4.5 + uses: vmactions/freebsd-vm at v1.4.8 with: release: "15.0" envs: CCACHE_COMPRESS CCACHE_COMPRESSLEVEL CCACHE_MAXSIZE @@ -524,7 +524,7 @@ jobs: rm -rf build - name: Save build cache - uses: actions/cache/save at v5 + uses: actions/cache/save at v6.1.0 with: path: .ccache key: ${{ steps.restore-cache.outputs.cache-primary-key }} @@ -540,10 +540,10 @@ jobs: sudo -E apt-get autopurge -y needrestart sudo -E apt-get -yq --no-install-suggests --no-install-recommends install make cmake emscripten ccache - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 - name: Retrieve build cache - uses: actions/cache/restore at v5 + uses: actions/cache/restore at v6.1.0 id: restore-cache with: path: .ccache @@ -562,7 +562,7 @@ jobs: ccache --show-stats - name: Save build cache - uses: actions/cache/save at v5 + uses: actions/cache/save at v6.1.0 with: path: .ccache key: ${{ steps.restore-cache.outputs.cache-primary-key }} @@ -582,7 +582,7 @@ jobs: sudo -E apt-get -yq --no-install-suggests --no-install-recommends install cppcheck python3 -m pip install --disable-pip-version-check --user codespell - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 - name: 'cppcheck' run: ./tools/cppcheck.sh @@ -605,7 +605,7 @@ jobs: python3 -m pip install --disable-pip-version-check --user cmake==3.15.* echo "$(python3 -m site --user-base)/bin" >> $GITHUB_PATH - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 with: path: geos diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 39126ac0d..3b2190fcf 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -36,11 +36,11 @@ jobs: # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init at v4.36.0 + uses: github/codeql-action/init at v4.36.2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -63,6 +63,6 @@ jobs: make -j$(nproc) - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze at v4.36.0 + uses: github/codeql-action/analyze at v4.36.2 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index d9168c715..af7b5e652 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -24,7 +24,7 @@ jobs: steps: - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 - name: Docker meta id: meta @@ -54,7 +54,7 @@ jobs: cp tools/ci/geosop/Dockerfile . - name: Build and Push - uses: docker/build-push-action at v7 + uses: docker/build-push-action at v7.2.0 with: context: . push: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2920c83ae..2931ed4ae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,7 +32,7 @@ jobs: uname -a sudo -E apt-get -yq --no-install-suggests --no-install-recommends install make doxygen - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 - name: Get tag id: tag diff --git a/.github/workflows/web.yml b/.github/workflows/web.yml index cdd46c346..152377359 100644 --- a/.github/workflows/web.yml +++ b/.github/workflows/web.yml @@ -27,7 +27,7 @@ jobs: sudo -E apt-get -yq --no-install-suggests --no-install-recommends install make doxygen - name: 'Check Out ?' - uses: actions/checkout at v6.0.2 + uses: actions/checkout at v7.0.0 - name: 'Hugo Build ?' # ...or replace 'master' with a full version tag, such as: v0.64.1 ----------------------------------------------------------------------- hooks/post-receive -- GEOS From git at osgeo.org Wed Jul 1 15:08:04 2026 From: git at osgeo.org (git at osgeo.org) Date: Wed, 1 Jul 2026 15:08:04 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch dependabot/github_actions/all-actions-75c878a17d updated. 70d903915980f99db1a95162e5370ebe89c11073 Message-ID: <20260701220805.081501B3C82@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, dependabot/github_actions/all-actions-75c878a17d has been updated via 70d903915980f99db1a95162e5370ebe89c11073 (commit) via d98040d84bacaf91003d74ae17a2cb0ec282da1e (commit) via c01f1a3249b02510d039e826d077bc3393fad93d (commit) from 75a7aee174a6fcdefaf38f03e7f051e4fbc249c6 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 70d903915980f99db1a95162e5370ebe89c11073 Author: Mike Taves Date: Thu Jul 2 10:02:14 2026 +1200 Updates `vmactions/freebsd-vm` from 1.4.8 to 1.5.0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44547f7d9..84ff56502 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -506,7 +506,7 @@ jobs: restore-keys: freebsd - name: Build and test - uses: vmactions/freebsd-vm at v1.4.8 + uses: vmactions/freebsd-vm at v1.5.0 with: release: "15.0" envs: CCACHE_COMPRESS CCACHE_COMPRESSLEVEL CCACHE_MAXSIZE commit d98040d84bacaf91003d74ae17a2cb0ec282da1e Author: Mike Taves Date: Thu Jul 2 10:00:56 2026 +1200 Updates `lammy/msvc-dev-cmd` from 1 to 1.13.0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c493e6f3b..44547f7d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -392,7 +392,7 @@ jobs: run: choco install ccache ninja - name: Activate MSVC - uses: ilammy/msvc-dev-cmd at v1 + uses: ilammy/msvc-dev-cmd at v1.13.0 with: arch: ${{ matrix.ci.arch }} commit c01f1a3249b02510d039e826d077bc3393fad93d Author: Mike Taves Date: Thu Jul 2 09:57:59 2026 +1200 Updates `docker/build-push-action` from 7.3.0 diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index af7b5e652..8689d26ad 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -54,7 +54,7 @@ jobs: cp tools/ci/geosop/Dockerfile . - name: Build and Push - uses: docker/build-push-action at v7.2.0 + uses: docker/build-push-action at v7.3.0 with: context: . push: true ----------------------------------------------------------------------- Summary of changes: .github/workflows/ci.yml | 4 ++-- .github/workflows/container.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) hooks/post-receive -- GEOS From git at osgeo.org Wed Jul 1 15:43:03 2026 From: git at osgeo.org (git at osgeo.org) Date: Wed, 1 Jul 2026 15:43:03 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch dependabot/github_actions/all-actions-75c878a17d updated. e0dc9983bfeeedbcf96afbd8911d3d28321c31c7 Message-ID: <20260701224303.2F25F1B3D4B@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, dependabot/github_actions/all-actions-75c878a17d has been updated via e0dc9983bfeeedbcf96afbd8911d3d28321c31c7 (commit) from 70d903915980f99db1a95162e5370ebe89c11073 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit e0dc9983bfeeedbcf96afbd8911d3d28321c31c7 Author: Mike Taves Date: Thu Jul 2 10:42:25 2026 +1200 Switch GHA from ilammy/msvc-dev-cmd to TheMrMilchmann/setup-msvc-dev diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84ff56502..c3547f00c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -373,7 +373,7 @@ jobs: arch: x64 - os: windows-11-arm cxxstd: 20 - arch: arm64 + arch: x86_arm64 runs-on: ${{ matrix.ci.os }} steps: @@ -392,7 +392,7 @@ jobs: run: choco install ccache ninja - name: Activate MSVC - uses: ilammy/msvc-dev-cmd at v1.13.0 + uses: TheMrMilchmann/setup-msvc-dev at v4 with: arch: ${{ matrix.ci.arch }} ----------------------------------------------------------------------- Summary of changes: .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) hooks/post-receive -- GEOS From git at osgeo.org Wed Jul 1 15:56:28 2026 From: git at osgeo.org (git at osgeo.org) Date: Wed, 1 Jul 2026 15:56:28 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch dependabot/github_actions/all-actions-75c878a17d updated. ec9eeb90c8cfda2ed8f608cd98b6e9754c7c320d Message-ID: <20260701225628.756A01B3FD3@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, dependabot/github_actions/all-actions-75c878a17d has been updated via ec9eeb90c8cfda2ed8f608cd98b6e9754c7c320d (commit) from e0dc9983bfeeedbcf96afbd8911d3d28321c31c7 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit ec9eeb90c8cfda2ed8f608cd98b6e9754c7c320d Author: Mike Taves Date: Thu Jul 2 10:55:59 2026 +1200 Try again with arch: amd64_arm64 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3547f00c..7432ec8a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -373,7 +373,7 @@ jobs: arch: x64 - os: windows-11-arm cxxstd: 20 - arch: x86_arm64 + arch: amd64_arm64 runs-on: ${{ matrix.ci.os }} steps: ----------------------------------------------------------------------- Summary of changes: .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) hooks/post-receive -- GEOS From git at osgeo.org Wed Jul 1 16:09:11 2026 From: git at osgeo.org (git at osgeo.org) Date: Wed, 1 Jul 2026 16:09:11 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch dependabot/github_actions/all-actions-75c878a17d updated. e324d3a58637c7dbfd90a36544dbe8f083b2f415 Message-ID: <20260701230911.B57F41B449D@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, dependabot/github_actions/all-actions-75c878a17d has been updated via e324d3a58637c7dbfd90a36544dbe8f083b2f415 (commit) from ec9eeb90c8cfda2ed8f608cd98b6e9754c7c320d (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit e324d3a58637c7dbfd90a36544dbe8f083b2f415 Author: Mike Taves Date: Thu Jul 2 11:05:08 2026 +1200 Try again with arch: arm64 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7432ec8a1..6df0cef55 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -373,7 +373,7 @@ jobs: arch: x64 - os: windows-11-arm cxxstd: 20 - arch: amd64_arm64 + arch: arm64 runs-on: ${{ matrix.ci.os }} steps: ----------------------------------------------------------------------- Summary of changes: .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) hooks/post-receive -- GEOS From git at osgeo.org Wed Jul 1 16:43:42 2026 From: git at osgeo.org (git at osgeo.org) Date: Wed, 1 Jul 2026 16:43:42 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch dependabot/github_actions/all-actions-75c878a17d deleted. e324d3a58637c7dbfd90a36544dbe8f083b2f415 Message-ID: <20260701234343.0507A1B4A04@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, dependabot/github_actions/all-actions-75c878a17d has been deleted was e324d3a58637c7dbfd90a36544dbe8f083b2f415 - Log ----------------------------------------------------------------- e324d3a58637c7dbfd90a36544dbe8f083b2f415 Try again with arch: arm64 ----------------------------------------------------------------------- hooks/post-receive -- GEOS From git at osgeo.org Wed Jul 1 16:43:43 2026 From: git at osgeo.org (git at osgeo.org) Date: Wed, 1 Jul 2026 16:43:43 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. 8385781ea869e09719a92f0d7240c4ba800501e8 Message-ID: <20260701234343.272CE1B481C@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via 8385781ea869e09719a92f0d7240c4ba800501e8 (commit) from 5ffc04e4be506fe32ef237851ccc60ff400449de (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 8385781ea869e09719a92f0d7240c4ba800501e8 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu Jul 2 11:43:13 2026 +1200 Bump the all-actions group with 6 updates (#1463) * Bump the all-actions group with 6 updates - Updates `actions/checkout` from 6.0.2 to 7.0.0 - Updates `actions/cache` from 5 to 6.1.0 - Updates `codecov/codecov-action` from 6 to 7 - Updates `vmactions/freebsd-vm` from 1.4.5 to 1.4.8 - Updates `github/codeql-action` from 4.36.0 to 4.36.2 - Updates `docker/build-push-action` from 7 to 7.3.0 * Updates `lammy/msvc-dev-cmd` from 1 to 1.13.0 * Updates `vmactions/freebsd-vm` from 1.4.8 to 1.5.0 * Switch GHA from ilammy/msvc-dev-cmd to TheMrMilchmann/setup-msvc-dev --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mike Taves diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f7a5e237..6df0cef55 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -197,10 +197,10 @@ jobs: python3 -m pip install --disable-pip-version-check --user cmake==${{ matrix.ci.cmake }} echo "$(python3 -m site --user-base)/bin" >> $GITHUB_PATH - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 - name: Retrieve build cache - uses: actions/cache/restore at v5 + uses: actions/cache/restore at v6.1.0 id: restore-cache with: path: .ccache @@ -227,7 +227,7 @@ jobs: ccache --show-stats - name: Save build cache - uses: actions/cache/save at v5 + uses: actions/cache/save at v6.1.0 with: path: .ccache key: ${{ steps.restore-cache.outputs.cache-primary-key }} @@ -263,7 +263,7 @@ jobs: - name: Upload Coverage to Codecov if: ${{ matrix.ci.build_type == 'Coverage' && !cancelled() }} - uses: codecov/codecov-action at v6 + uses: codecov/codecov-action at v7 with: fail_ci_if_error: false files: build/coverage.info @@ -281,7 +281,7 @@ jobs: build_type: ['Debug', 'Release'] steps: - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 - name: 'Setup' uses: msys2/setup-msys2 at v2 @@ -295,7 +295,7 @@ jobs: mingw-w64-x86_64-gcc - name: Retrieve build cache - uses: actions/cache/restore at v5 + uses: actions/cache/restore at v6.1.0 id: restore-cache with: path: .ccache @@ -316,7 +316,7 @@ jobs: ccache --show-stats --verbose - name: Save build cache - uses: actions/cache/save at v5 + uses: actions/cache/save at v6.1.0 with: path: .ccache key: ${{ steps.restore-cache.outputs.cache-primary-key }} @@ -337,7 +337,7 @@ jobs: arch: arm64 steps: - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 # ccache not supported for this generator and/or Debug @@ -378,10 +378,10 @@ jobs: runs-on: ${{ matrix.ci.os }} steps: - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 - name: Retrieve build cache - uses: actions/cache/restore at v5 + uses: actions/cache/restore at v6.1.0 id: restore-cache with: path: .ccache @@ -392,7 +392,7 @@ jobs: run: choco install ccache ninja - name: Activate MSVC - uses: ilammy/msvc-dev-cmd at v1 + uses: TheMrMilchmann/setup-msvc-dev at v4 with: arch: ${{ matrix.ci.arch }} @@ -415,7 +415,7 @@ jobs: ccache --show-stats --verbose - name: Save build cache - uses: actions/cache/save at v5 + uses: actions/cache/save at v6.1.0 with: path: .ccache key: ${{ steps.restore-cache.outputs.cache-primary-key }} @@ -459,10 +459,10 @@ jobs: which clang++ clang++ --version - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 - name: Retrieve build cache - uses: actions/cache/restore at v5 + uses: actions/cache/restore at v6.1.0 id: restore-cache with: path: .ccache @@ -482,7 +482,7 @@ jobs: ccache --show-stats - name: Save build cache - uses: actions/cache/save at v5 + uses: actions/cache/save at v6.1.0 with: path: .ccache key: ${{ steps.restore-cache.outputs.cache-primary-key }} @@ -495,10 +495,10 @@ jobs: name: FreeBSD runs-on: ubuntu-latest steps: - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 - name: Retrieve build cache - uses: actions/cache/restore at v5 + uses: actions/cache/restore at v6.1.0 id: restore-cache with: path: .ccache @@ -506,7 +506,7 @@ jobs: restore-keys: freebsd - name: Build and test - uses: vmactions/freebsd-vm at v1.4.5 + uses: vmactions/freebsd-vm at v1.5.0 with: release: "15.0" envs: CCACHE_COMPRESS CCACHE_COMPRESSLEVEL CCACHE_MAXSIZE @@ -524,7 +524,7 @@ jobs: rm -rf build - name: Save build cache - uses: actions/cache/save at v5 + uses: actions/cache/save at v6.1.0 with: path: .ccache key: ${{ steps.restore-cache.outputs.cache-primary-key }} @@ -540,10 +540,10 @@ jobs: sudo -E apt-get autopurge -y needrestart sudo -E apt-get -yq --no-install-suggests --no-install-recommends install make cmake emscripten ccache - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 - name: Retrieve build cache - uses: actions/cache/restore at v5 + uses: actions/cache/restore at v6.1.0 id: restore-cache with: path: .ccache @@ -562,7 +562,7 @@ jobs: ccache --show-stats - name: Save build cache - uses: actions/cache/save at v5 + uses: actions/cache/save at v6.1.0 with: path: .ccache key: ${{ steps.restore-cache.outputs.cache-primary-key }} @@ -582,7 +582,7 @@ jobs: sudo -E apt-get -yq --no-install-suggests --no-install-recommends install cppcheck python3 -m pip install --disable-pip-version-check --user codespell - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 - name: 'cppcheck' run: ./tools/cppcheck.sh @@ -605,7 +605,7 @@ jobs: python3 -m pip install --disable-pip-version-check --user cmake==3.15.* echo "$(python3 -m site --user-base)/bin" >> $GITHUB_PATH - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 with: path: geos diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 39126ac0d..3b2190fcf 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -36,11 +36,11 @@ jobs: # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init at v4.36.0 + uses: github/codeql-action/init at v4.36.2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -63,6 +63,6 @@ jobs: make -j$(nproc) - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze at v4.36.0 + uses: github/codeql-action/analyze at v4.36.2 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index d9168c715..8689d26ad 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -24,7 +24,7 @@ jobs: steps: - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 - name: Docker meta id: meta @@ -54,7 +54,7 @@ jobs: cp tools/ci/geosop/Dockerfile . - name: Build and Push - uses: docker/build-push-action at v7 + uses: docker/build-push-action at v7.3.0 with: context: . push: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2920c83ae..2931ed4ae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,7 +32,7 @@ jobs: uname -a sudo -E apt-get -yq --no-install-suggests --no-install-recommends install make doxygen - - uses: actions/checkout at v6.0.2 + - uses: actions/checkout at v7.0.0 - name: Get tag id: tag diff --git a/.github/workflows/web.yml b/.github/workflows/web.yml index cdd46c346..152377359 100644 --- a/.github/workflows/web.yml +++ b/.github/workflows/web.yml @@ -27,7 +27,7 @@ jobs: sudo -E apt-get -yq --no-install-suggests --no-install-recommends install make doxygen - name: 'Check Out ?' - uses: actions/checkout at v6.0.2 + uses: actions/checkout at v7.0.0 - name: 'Hugo Build ?' # ...or replace 'master' with a full version tag, such as: v0.64.1 ----------------------------------------------------------------------- Summary of changes: .github/workflows/ci.yml | 48 ++++++++++++++++++++--------------------- .github/workflows/codeql.yml | 6 +++--- .github/workflows/container.yml | 4 ++-- .github/workflows/release.yml | 2 +- .github/workflows/web.yml | 2 +- 5 files changed, 31 insertions(+), 31 deletions(-) hooks/post-receive -- GEOS From git at osgeo.org Wed Jul 1 18:10:05 2026 From: git at osgeo.org (git at osgeo.org) Date: Wed, 1 Jul 2026 18:10:05 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. 134a07c31971413b2f0f3eca9ee4561375a85285 Message-ID: <20260702011005.A25DA1B509E@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via 134a07c31971413b2f0f3eca9ee4561375a85285 (commit) from 8385781ea869e09719a92f0d7240c4ba800501e8 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 134a07c31971413b2f0f3eca9ee4561375a85285 Author: Daniel Baston Date: Wed Jul 1 21:09:41 2026 -0400 Update NEWS diff --git a/NEWS.md b/NEWS.md index a77954ff6..2113bbb67 100644 --- a/NEWS.md +++ b/NEWS.md @@ -16,6 +16,7 @@ - Normalize (GH-1397, Dan Baston) - OverlayNG (GH-1427, Dan Baston) - Polygonizer (GH-1420, Dan Baston) + - Add GEOSLineToCurve, GEOSCurveToLine (GH-1382, Dan Baston) - Add GeometrySplitter (GH-1424, Dan Baston) - Breaking Changes: ----------------------------------------------------------------------- Summary of changes: NEWS.md | 1 + 1 file changed, 1 insertion(+) hooks/post-receive -- GEOS From git at osgeo.org Mon Jul 6 13:15:52 2026 From: git at osgeo.org (git at osgeo.org) Date: Mon, 6 Jul 2026 13:15:52 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. 28726481ede802eee10a9585f9df781873242a71 Message-ID: <20260706201552.9418535C1B@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via 28726481ede802eee10a9585f9df781873242a71 (commit) from 134a07c31971413b2f0f3eca9ee4561375a85285 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 28726481ede802eee10a9585f9df781873242a71 Author: Paul Ramsey Date: Mon Jul 6 19:34:29 2026 +0000 Pass non-null dummy parameter (closes #1464) diff --git a/util/geosop/GeosOp.cpp b/util/geosop/GeosOp.cpp index b6c0d0a9f..3e91c1d3b 100644 --- a/util/geosop/GeosOp.cpp +++ b/util/geosop/GeosOp.cpp @@ -538,7 +538,7 @@ Result* GeosOp::executeOp(GeometryOp * op, geos::util::Profile sw( "op" ); sw.start(); - Result* result = op->execute( *gA, *gB, opArgs.arg1, opArgs.arg2 ); + Result* result = op->execute( *gA, gB ? *gB : *gA, opArgs.arg1, opArgs.arg2 ); sw.stop(); double time = sw.getTot(); totalTime += time; ----------------------------------------------------------------------- Summary of changes: util/geosop/GeosOp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) hooks/post-receive -- GEOS From git at osgeo.org Mon Jul 6 13:16:46 2026 From: git at osgeo.org (git at osgeo.org) Date: Mon, 6 Jul 2026 13:16:46 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch 3.13 updated. fbe9180f7e7b027b00b29846ebc79ede1bf80a59 Message-ID: <20260706201646.A721735DD1@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, 3.13 has been updated via fbe9180f7e7b027b00b29846ebc79ede1bf80a59 (commit) from 4a31a2b4d3d17e01c028fe0539f2d0235bbd27f7 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit fbe9180f7e7b027b00b29846ebc79ede1bf80a59 Author: Paul Ramsey Date: Mon Jul 6 19:34:29 2026 +0000 Pass non-null dummy parameter (closes #1464) diff --git a/util/geosop/GeosOp.cpp b/util/geosop/GeosOp.cpp index fceb29e2e..7ffbaf4ac 100644 --- a/util/geosop/GeosOp.cpp +++ b/util/geosop/GeosOp.cpp @@ -494,7 +494,7 @@ Result* GeosOp::executeOp(GeometryOp * op, geos::util::Profile sw( "op" ); sw.start(); - Result* result = op->execute( *gA, *gB, opArgs.arg1, opArgs.arg2 ); + Result* result = op->execute( *gA, gB ? *gB : *gA, opArgs.arg1, opArgs.arg2 ); sw.stop(); double time = sw.getTot(); totalTime += time; ----------------------------------------------------------------------- Summary of changes: util/geosop/GeosOp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) hooks/post-receive -- GEOS From git at osgeo.org Mon Jul 6 13:17:07 2026 From: git at osgeo.org (git at osgeo.org) Date: Mon, 6 Jul 2026 13:17:07 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch 3.12 updated. 69e0d518a60521a4bbc6b72f98718ea18d2b0fd7 Message-ID: <20260706201707.9BE6234EC4@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, 3.12 has been updated via 69e0d518a60521a4bbc6b72f98718ea18d2b0fd7 (commit) from ef1e94327bfb9f9f1b175d8a85f8220f72497f41 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 69e0d518a60521a4bbc6b72f98718ea18d2b0fd7 Author: Paul Ramsey Date: Mon Jul 6 19:34:29 2026 +0000 Pass non-null dummy parameter (closes #1464) diff --git a/util/geosop/GeosOp.cpp b/util/geosop/GeosOp.cpp index deb70b7d1..07268787a 100644 --- a/util/geosop/GeosOp.cpp +++ b/util/geosop/GeosOp.cpp @@ -488,7 +488,7 @@ Result* GeosOp::executeOp(GeometryOp * op, geos::util::Profile sw( "op" ); sw.start(); - Result* result = op->execute( *gA, *gB, opArgs.arg1, opArgs.arg2 ); + Result* result = op->execute( *gA, gB ? *gB : *gA, opArgs.arg1, opArgs.arg2 ); sw.stop(); double time = sw.getTot(); totalTime += time; ----------------------------------------------------------------------- Summary of changes: util/geosop/GeosOp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) hooks/post-receive -- GEOS From git at osgeo.org Tue Jul 7 08:33:59 2026 From: git at osgeo.org (git at osgeo.org) Date: Tue, 7 Jul 2026 08:33:59 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. 40b2404209e65edd044386b11c4989d8226aeecb Message-ID: <20260707153400.1DC7C196AFF@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via 40b2404209e65edd044386b11c4989d8226aeecb (commit) from 28726481ede802eee10a9585f9df781873242a71 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 40b2404209e65edd044386b11c4989d8226aeecb Author: Daniel Baston Date: Tue Jul 7 11:33:37 2026 -0400 Add progress reporting mechanism (#1466) Co-authored-by: Even Rouault diff --git a/capi/geos_c.cpp b/capi/geos_c.cpp index a421eabc0..568f1c0b4 100644 --- a/capi/geos_c.cpp +++ b/capi/geos_c.cpp @@ -2194,5 +2194,4 @@ extern "C" { return GEOSCoverageSimplifyVW_r(handle, input, tolerance, preserveBoundary); } - } /* extern "C" */ diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in index 8f706e543..cb91502fd 100644 --- a/capi/geos_c.h.in +++ b/capi/geos_c.h.in @@ -117,6 +117,21 @@ typedef void (*GEOSMessageHandler)(GEOS_PRINTF_FORMAT const char *fmt, ...) */ typedef void (*GEOSMessageHandler_r)(const char *message, void *userdata); + +/** + * A GEOS progress callback function. + * + * Such function takes a progress ratio (0 to 1), 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. + */ +typedef void (GEOSProgressCallback)(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 @@ -426,6 +441,23 @@ extern void GEOS_DLL GEOS_interruptRequest(void); */ extern void GEOS_DLL GEOS_interruptCancel(void); +/* ========== Progress monitoring ========== */ + +/** +* Register a function to be called with progress updates +* +* \param extHandle the context returned by \ref GEOS_init_r. +* \param cb Callback function to invoke +* \param userData optional data to be pe provided as argument to callback +* \return the previously registered callback, or NULL +* \see GEOSProgressCallback +* \since 3.15 +*/ +extern GEOSProgressCallback GEOS_DLL *GEOSContext_setProgressCallback_r( + GEOSContextHandle_t extHandle, + GEOSProgressCallback* cb, + void* userData); + /* ========== Initialization and Cleanup ========== */ /** diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp index 7b06105cf..32254f9e3 100644 --- a/capi/geos_ts_c.cpp +++ b/capi/geos_ts_c.cpp @@ -84,6 +84,7 @@ #include #include #include +#include #include #include #include @@ -122,6 +123,7 @@ #include #include #include +#include #include // This should go away @@ -255,6 +257,8 @@ typedef struct GEOSContextHandle_HS { GEOSMessageHandler_r errorMessageNew; GEOSContextInterruptCallback* interrupt_cb; void* interrupt_cb_data; + GEOSProgressCallback* progress_cb; + void* progress_cb_data; void* errorData; uint8_t WKBOutputDims; int WKBByteOrder; @@ -262,6 +266,7 @@ typedef struct GEOSContextHandle_HS { std::unique_ptr point2d; std::optional lineToCurveParams; std::optional curveToLineParams; + geos::util::ProgressFunction progressFunction; GEOSContextHandle_HS() : @@ -273,6 +278,8 @@ typedef struct GEOSContextHandle_HS { errorMessageNew(nullptr), interrupt_cb(nullptr), interrupt_cb_data(nullptr), + progress_cb(nullptr), + progress_cb_data(nullptr), errorData(nullptr), point2d(nullptr) { @@ -284,6 +291,13 @@ typedef struct GEOSContextHandle_HS { setNoticeHandler(nullptr); setErrorHandler(nullptr); initialized = 1; + + progressFunction = [this](double progress, const char* message) + { + if (progress_cb) { + (*progress_cb)(progress, message, progress_cb_data); + } + }; } GEOSMessageHandler @@ -339,6 +353,15 @@ typedef struct GEOSContextHandle_HS { return old; } + GEOSProgressCallback* + setProgressCallback(GEOSProgressCallback* cb, void* userData) + { + auto old = progress_cb; + progress_cb = cb; + progress_cb_data = userData; + return old; + } + void NOTICE_MESSAGE(GEOS_PRINTF_FORMAT const char *fmt, ...) GEOS_PRINTF_FORMAT_ATTR(2, 3) { @@ -548,17 +571,49 @@ struct InterruptManager { }; struct NotInterruptible { - NotInterruptible(GEOSContextHandle_t handle) { + explicit NotInterruptible(GEOSContextHandle_t handle) { (void) handle; } }; +using Interruptible = InterruptManager; + +// RAII construct that ensures a progress callback is called at completion of an operation, +// even if that operation does not support progress reporting. +struct DefaultProgress { + explicit DefaultProgress(GEOSContextHandle_t handle) + : cb(handle->progress_cb) + , cb_data(handle->progress_cb_data) + {} + + ~DefaultProgress() { + if (cb) { + (*cb)(1.0, "", cb_data); + } + } + + GEOSProgressCallback* cb; + void* cb_data; +}; + +// Construct that avoids automatic progress reporting. +// Used for functions that have progress reporting implemented. +struct ReportsProgress { + explicit ReportsProgress(GEOSContextHandle_t handle) { + (void) handle; + } +}; + +// Construct for trivial functions that avoids automatic progress +// reporting. +using NoProgress = ReportsProgress; + } // namespace anonymous // Execute a lambda, using the given context handle to process errors. // Return errval on error. // Errval should be of the type returned by f, unless f returns a bool in which case we promote to char. -template +template inline auto execute( GEOSContextHandle_t extHandle, typename std::conditional()()),bool>::value, @@ -575,6 +630,7 @@ inline auto execute( } InterruptManagerType ic(handle); + ProgressManagerType pc(handle); try { return f(); @@ -589,7 +645,7 @@ inline auto execute( // Execute a lambda, using the given context handle to process errors. // Return nullptr on error. -template()())>::value, std::nullptr_t>::type = nullptr> +template()())>::value, std::nullptr_t>::type = nullptr> inline auto execute(GEOSContextHandle_t extHandle, F&& f) -> decltype(f()) { if (extHandle == nullptr) { throw std::runtime_error("context handle is uninitialized, call initGEOS"); @@ -601,6 +657,7 @@ inline auto execute(GEOSContextHandle_t extHandle, F&& f) -> decltype(f()) { } InterruptManagerType ic(handle); + ProgressManagerType pc(handle); try { return f(); @@ -615,7 +672,7 @@ inline auto execute(GEOSContextHandle_t extHandle, F&& f) -> decltype(f()) { // Execute a lambda, using the given context handle to process errors. // No return value. -template()())>::value, std::nullptr_t>::type = nullptr> +template()())>::value, std::nullptr_t>::type = nullptr> inline void execute(GEOSContextHandle_t extHandle, F&& f) { GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); @@ -653,10 +710,10 @@ Geometry* convertCurvesAndExecute(GEOSContextHandle_t extHandle, const Geometry* }); } -template +template Geometry* convertCurvesAndExecute(GEOSContextHandle_t extHandle, const Geometry* g1, F&& f) { - return execute(extHandle, [&]() { + return execute(extHandle, [&]() { const auto geom1 = convertToLineIfNeeded(extHandle, g1); auto g3 = f(geom1.get()); @@ -750,6 +807,16 @@ extern "C" { return handle->setInterruptHandler(cb, userData); } + GEOSProgressCallback* + GEOSContext_setProgressCallback_r(GEOSContextHandle_t extHandle, GEOSProgressCallback* cb, void* userData) + { + if(0 == extHandle->initialized) { + return nullptr; + } + + return extHandle->setProgressCallback(cb, userData); + } + void GEOSContext_setCurveToLineParams_r(GEOSContextHandle_t extHandle, const GEOSCurveToLineParams* params) { if (params) { @@ -1241,7 +1308,7 @@ extern "C" { int GEOSArea_r(GEOSContextHandle_t extHandle, const Geometry* g, double* area) { - return execute(extHandle, 0, [&]() { + return execute(extHandle, 0, [&]() { *area = g->getArea(); return 1; }); @@ -1250,7 +1317,7 @@ extern "C" { int GEOSLength_r(GEOSContextHandle_t extHandle, const Geometry* g, double* length) { - return execute(extHandle, 0, [&]() { + return execute(extHandle, 0, [&]() { *length = g->getLength(); return 1; }); @@ -2006,7 +2073,8 @@ extern "C" { Geometry* GEOSUnaryUnion_r(GEOSContextHandle_t extHandle, const Geometry* g) { - return convertCurvesAndExecute(extHandle, g, [&](const Geometry* input1) { + return convertCurvesAndExecute(extHandle, g, [&](const Geometry* input1) { + return geos::operation::geounion::UnaryUnionOp::Union(*input1, &extHandle->progressFunction); return input1->Union(); }); } @@ -2014,7 +2082,7 @@ extern "C" { Geometry* GEOSUnaryUnionPrec_r(GEOSContextHandle_t extHandle, const Geometry* g1, double gridSize) { - return execute(extHandle, [&]() { + return execute(extHandle, [&]() { std::unique_ptr pm; if(gridSize != 0) { @@ -2276,7 +2344,7 @@ extern "C" { int GEOSGetNumInteriorRings_r(GEOSContextHandle_t extHandle, const Geometry* g1) { - return execute(extHandle, -1, [&]() { + return execute(extHandle, -1, [&]() { const Surface* p = dynamic_cast(g1); if(!p) { throw IllegalArgumentException("Argument is not a Surface"); @@ -2290,7 +2358,7 @@ extern "C" { int GEOSGetNumGeometries_r(GEOSContextHandle_t extHandle, const Geometry* g1) { - return execute(extHandle, -1, [&]() { + return execute(extHandle, -1, [&]() { return static_cast(g1->getNumGeometries()); }); } @@ -2303,7 +2371,7 @@ extern "C" { const Geometry* GEOSGetGeometryN_r(GEOSContextHandle_t extHandle, const Geometry* g1, int n) { - return execute(extHandle, [&]() { + return execute(extHandle, [&]() { if(n < 0) { throw IllegalArgumentException("Index must be non-negative."); } @@ -2314,7 +2382,7 @@ extern "C" { int GEOSGetNumCurves_r(GEOSContextHandle_t extHandle, const Geometry* g1) { - return execute(extHandle, -1, [&]() { + return execute(extHandle, -1, [&]() { const Curve* curve = dynamic_cast(g1); if (curve) { return static_cast(curve->getNumCurves()); @@ -2327,7 +2395,7 @@ extern "C" { const GEOSGeometry* GEOSGetCurveN_r(GEOSContextHandle_t extHandle, const Geometry* g, int n) { - return execute(extHandle, [&]() -> const Geometry* { + return execute(extHandle, [&]() -> const Geometry* { if(n < 0) { throw IllegalArgumentException("Index must be non-negative."); } @@ -2528,7 +2596,7 @@ extern "C" { const Geometry* GEOSGetExteriorRing_r(GEOSContextHandle_t extHandle, const Geometry* g1) { - return execute(extHandle, [&]() { + return execute(extHandle, [&]() { const Surface* p = dynamic_cast(g1); if(!p) { throw IllegalArgumentException("Invalid argument (must be a Surface)"); @@ -2544,7 +2612,7 @@ extern "C" { const Geometry* GEOSGetInteriorRingN_r(GEOSContextHandle_t extHandle, const Geometry* g1, int n) { - return execute(extHandle, [&]() { + return execute(extHandle, [&]() { const Surface* p = dynamic_cast(g1); if(!p) { throw IllegalArgumentException("Invalid argument (must be a Surface)"); @@ -3374,7 +3442,7 @@ extern "C" { GEOSCoordSeq_setOrdinate_r(GEOSContextHandle_t extHandle, CoordinateSequence* cs, unsigned int idx, unsigned int dim, double val) { - return execute(extHandle, 0, [&]() { + return execute(extHandle, 0, [&]() { cs->setOrdinate(idx, dim, val); return 1; }); @@ -3407,7 +3475,7 @@ extern "C" { int GEOSCoordSeq_setXY_r(GEOSContextHandle_t extHandle, CoordinateSequence* cs, unsigned int idx, double x, double y) { - return execute(extHandle, 0, [&]() { + return execute(extHandle, 0, [&]() { cs->setAt(CoordinateXY{x, y}, idx); return 1; }); @@ -3416,7 +3484,7 @@ extern "C" { int GEOSCoordSeq_setXYZ_r(GEOSContextHandle_t extHandle, CoordinateSequence* cs, unsigned int idx, double x, double y, double z) { - return execute(extHandle, 0, [&]() { + return execute(extHandle, 0, [&]() { cs->setAt(Coordinate{x, y, z}, idx); return 1; }); @@ -3434,7 +3502,7 @@ extern "C" { GEOSCoordSeq_getOrdinate_r(GEOSContextHandle_t extHandle, const CoordinateSequence* cs, unsigned int idx, unsigned int dim, double* val) { - return execute(extHandle, 0, [&]() { + return execute(extHandle, 0, [&]() { *val = cs->getOrdinate(idx, dim); return 1; }); @@ -3467,7 +3535,7 @@ extern "C" { int GEOSCoordSeq_getXY_r(GEOSContextHandle_t extHandle, const CoordinateSequence* cs, unsigned int idx, double* x, double* y) { - return execute(extHandle, 0, [&]() { + return execute(extHandle, 0, [&]() { auto& c = cs->getAt(idx); *x = c.x; *y = c.y; @@ -3478,7 +3546,7 @@ extern "C" { int GEOSCoordSeq_getXYZ_r(GEOSContextHandle_t extHandle, const CoordinateSequence* cs, unsigned int idx, double* x, double* y, double* z) { - return execute(extHandle, 0, [&]() { + return execute(extHandle, 0, [&]() { auto& c = cs->getAt(idx); *x = c.x; *y = c.y; @@ -3490,7 +3558,7 @@ extern "C" { int GEOSCoordSeq_getSize_r(GEOSContextHandle_t extHandle, const CoordinateSequence* cs, unsigned int* size) { - return execute(extHandle, 0, [&]() { + return execute(extHandle, 0, [&]() { const std::size_t sz = cs->getSize(); *size = static_cast(sz); return 1; @@ -3500,7 +3568,7 @@ extern "C" { int GEOSCoordSeq_getDimensions_r(GEOSContextHandle_t extHandle, const CoordinateSequence* cs, unsigned int* dims) { - return execute(extHandle, 0, [&]() { + return execute(extHandle, 0, [&]() { const std::size_t dim = cs->getDimension(); *dims = static_cast(dim); @@ -5100,11 +5168,11 @@ extern "C" { GEOSCoverageSimplifyVW_r(GEOSContextHandle_t extHandle, const Geometry* input, double tolerance, - int preserveBoundary) - { + int preserveBoundary) { + using geos::coverage::CoverageSimplifier; - return convertCurvesAndExecute(extHandle, input, [&](const Geometry* linearized) -> std::unique_ptr { + return convertCurvesAndExecute(extHandle, input, [&](const Geometry* linearized) -> std::unique_ptr { const GeometryCollection* col = dynamic_cast(linearized); if (!col) return nullptr; @@ -5116,10 +5184,10 @@ extern "C" { CoverageSimplifier cov(coverage); std::vector> simple; if (preserveBoundary == 1) { - simple = cov.simplifyInner(tolerance); + simple = cov.simplifyInner(tolerance, &extHandle->progressFunction); } else if (preserveBoundary == 0) { - simple = cov.simplify(tolerance); + simple = cov.simplify(tolerance, &extHandle->progressFunction); } else return nullptr; diff --git a/include/geos/coverage/CoverageSimplifier.h b/include/geos/coverage/CoverageSimplifier.h index 74df234dd..4c87e12eb 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 @@ public: * * @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 = nullptr); static std::vector> simplify( const std::vector>& coverage, - double tolerance); + double tolerance, + geos::util::ProgressFunction* progressFunction = nullptr); /** * Simplifies the inner boundaries of a set of polygonal geometries forming a coverage, @@ -104,24 +108,28 @@ public: * * @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 @@ public: * 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 @@ private: 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 0b904c8a8..ec2bce993 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 @@ public: * * @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 @@ public: * @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,11 +214,12 @@ private: void setConstraints(const MultiLineString* constraints); - std::unique_ptr simplify(); + std::unique_ptr simplify(geos::util::ProgressFunction* progressFunction); std::vector createEdges( const MultiLineString* lines, - std::vector& freeRing); + std::vector& freeRing, + geos::util::ProgressFunction* progressFunction); }; // TPVWSimplifier diff --git a/include/geos/geom/Geometry.h b/include/geos/geom/Geometry.h index 8fb442832..5a4a4fa24 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 @@ -748,9 +749,15 @@ public: * * @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 2b1e29267..fcc5ba9a7 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 @@ public: * 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 @@ public: * @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 @@ public: * * @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 @@ public: /** \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 @@ private: * @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 3fed7e32b..98b6a9e70 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 @@ public: Union(const T& geoms) { UnaryUnionOp op(geoms); - return op.Union(); + geos::util::ProgressFunction* progressFunction = nullptr; + return op.Union(progressFunction); } template @@ -102,14 +104,15 @@ public: 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 @@ public: * @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/include/geos/util/Progress.h b/include/geos/util/Progress.h new file mode 100644 index 000000000..c581c60ac --- /dev/null +++ b/include/geos/util/Progress.h @@ -0,0 +1,84 @@ +/********************************************************************** + * + * 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 function. + * + * Such function takes a progression ratio (between 0 and 1), and an optional + * message. + */ +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); + +/** 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/CoverageSimplifier.cpp b/src/coverage/CoverageSimplifier.cpp index 0623cedce..fd52f0e84 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 0bf36ac30..d31169774 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,21 +101,60 @@ 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); - 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); 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); + 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(subProgress, i, iterCount, iNotify, notificationInterval); + } + } + if (progressFunction) { + (*progressFunction)(1.0, nullptr); } return geomFactory->createMultiLineString(std::move(result)); } @@ -122,17 +163,25 @@ TPVWSimplifier::simplify() 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/geom/Geometry.cpp b/src/geom/Geometry.cpp index a34c7bf35..b833a0499 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 6d73ecbc9..7523978f4 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 258a7ecda..3e8da6f9d 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 f09b987b9..609fa93ee 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 37ccd371e..9f16ac647 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 35a93b0e9..aac120c80 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 09073f59a..7b6601c14 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); } /* @@ -122,6 +122,10 @@ UnaryUnionOp::Union() ret = geomFact->createGeometryCollection(); } + if (polygons.empty() && progressFunction) { + (*progressFunction)(1.0, ""); + } + return ret; } diff --git a/src/util/Progress.cpp b/src/util/Progress.cpp new file mode 100644 index 000000000..ab4041e68 --- /dev/null +++ b/src/util/Progress.cpp @@ -0,0 +1,43 @@ +/********************************************************************** + * + * 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 + +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; + } +} + +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 + diff --git a/tests/unit/capi/GEOSCoverageSimplifyTest.cpp b/tests/unit/capi/GEOSCoverageSimplifyTest.cpp index d22ae9b79..ea4ff4353 100644 --- a/tests/unit/capi/GEOSCoverageSimplifyTest.cpp +++ b/tests/unit/capi/GEOSCoverageSimplifyTest.cpp @@ -84,10 +84,27 @@ 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 void Func(double progressRatio, const char* /* msg */, void* userdata) + { + Cbk* self = static_cast(userdata); + self->lastRatio = progressRatio; + } + }; + + Cbk cbk; + auto context = initGEOS_r(nullptr, nullptr); + GEOSContext_setProgressCallback_r(context, &Cbk::Func, &cbk); + result_ = GEOSCoverageSimplifyVW_r(context, input_, 1.0, 0); + finishGEOS_r(context); + + ensure_equals("cbk.lastRatio == 1.0", cbk.lastRatio, 1.0); ensure( result_ != nullptr ); - ensure( GEOSGeomTypeId(result_) == GEOS_GEOMETRYCOLLECTION ); + ensure_equals( GEOSGeomTypeId(result_), GEOS_GEOMETRYCOLLECTION ); const char* expectedWKT = "GEOMETRYCOLLECTION(POLYGON((0 0,10 0,10 10,0 10,0 0)),POLYGON((10 0,20 0,20 10,10 10,10 0)))"; diff --git a/tests/unit/capi/GEOSDifferenceTest.cpp b/tests/unit/capi/GEOSDifferenceTest.cpp index ee1e8c823..d38f28c1d 100644 --- a/tests/unit/capi/GEOSDifferenceTest.cpp +++ b/tests/unit/capi/GEOSDifferenceTest.cpp @@ -78,5 +78,31 @@ void object::test<3>() ensure_geometry_equals(result_, expected_, 1e-8); } +template<> +template<> +void object::test<4>() +{ + set_test_name("progress reporting"); + // GEOSDifference does not actually implement progress reporting, but our callback will automatically be + // called at 100% by the C API "DefaultProgress" handler. + + useContext(); + + geom1_ = fromWKT("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))"); + geom2_ = fromWKT("POLYGON ((5 5, 5 7, 7 7, 7 5, 5 5))"); + + std::vector fracs; + + auto progress = [](double f, const char*, void* userData) { + static_cast*>(userData)->push_back(f); + }; + + GEOSContext_setProgressCallback_r(ctxt_, progress, &fracs); + result_ = GEOSDifference_r(ctxt_, geom1_, geom2_); + + ensure_equals(fracs.size(), 1u); + ensure_equals(fracs[0], 1.0); +} + } // namespace tut diff --git a/tests/unit/capi/GEOSUnaryUnionTest.cpp b/tests/unit/capi/GEOSUnaryUnionTest.cpp index dea0c8941..24f99fcb9 100644 --- a/tests/unit/capi/GEOSUnaryUnionTest.cpp +++ b/tests/unit/capi/GEOSUnaryUnionTest.cpp @@ -5,6 +5,7 @@ // geos #include // std +#include #include #include "capi_test_utils.h" @@ -252,6 +253,60 @@ void object::test<12>() ensure_equals("length does not match", result_length, input_length, 1e-5); } +template<> +template<> +void object::test<13>() +{ + set_test_name("progress callback invoked (cascaded union)"); + useContext(); + + std::vector geoms; + for (int i = 0; i < 10; i++) { + GEOSGeometry* pt = GEOSGeom_createPointFromXY_r(ctxt_, static_cast(i), static_cast(i)); + GEOSGeometry* circle = GEOSBuffer_r(ctxt_, pt, 3, 8); + geoms.push_back(circle); + GEOSGeom_destroy_r(ctxt_, pt); + } + input_ = GEOSGeom_createCollection_r(ctxt_, GEOS_GEOMETRYCOLLECTION, geoms.data(), static_cast(geoms.size())); + + std::vector fracs; + + auto progress = [](double f, const char*, void* userData) { + static_cast*>(userData)->push_back(f); + }; + + GEOSContext_setProgressCallback_r(ctxt_, progress, &fracs); + result_ = GEOSUnaryUnion_r(ctxt_, input_); + + ensure(fracs.size() > 3); // arbitrary + + // progress reported in-order + ensure(std::is_sorted(fracs.begin(), fracs.end())); + + // no duplicate progress values + ensure(std::adjacent_find(fracs.begin(), fracs.end()) == fracs.end()); +} + +template<> +template<> +void object::test<14>() { + set_test_name("progress callback invoked (input already unioned)"); + useContext(); + + input_ = fromWKT("POINT (1 1)"); + + std::vector fracs; + + auto progress = [](double f, const char*, void* userData) { + static_cast*>(userData)->push_back(f); + }; + + GEOSContext_setProgressCallback_r(ctxt_, progress, &fracs); + result_ = GEOSUnaryUnion_r(ctxt_, input_); + + ensure_equals(fracs.size(), 1u); + ensure_equals(fracs[0], 1.0); +} } // namespace tut diff --git a/tests/unit/coverage/CoverageSimplifierTest.cpp b/tests/unit/coverage/CoverageSimplifierTest.cpp index be8b1938c..926436409 100644 --- a/tests/unit/coverage/CoverageSimplifierTest.cpp +++ b/tests/unit/coverage/CoverageSimplifierTest.cpp @@ -31,7 +31,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; @@ -47,8 +47,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( @@ -56,7 +64,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); } @@ -477,7 +485,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); @@ -497,7 +505,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 ce55c9965..e8762ce42 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 f10e22e84..7ef4c4613 100644 --- a/util/geosop/GeometryOp.cpp +++ b/util/geosop/GeometryOp.cpp @@ -1009,7 +1009,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++) { ----------------------------------------------------------------------- Summary of changes: capi/geos_c.cpp | 1 - capi/geos_c.h.in | 32 ++++++ capi/geos_ts_c.cpp | 128 ++++++++++++++++----- include/geos/coverage/CoverageSimplifier.h | 24 ++-- include/geos/coverage/TPVWSimplifier.h | 14 ++- include/geos/geom/Geometry.h | 9 +- .../geos/operation/union/CascadedPolygonUnion.h | 20 +++- include/geos/operation/union/UnaryUnionOp.h | 13 ++- include/geos/util/Progress.h | 84 ++++++++++++++ src/coverage/CoverageSimplifier.cpp | 37 +++--- src/coverage/TPVWSimplifier.cpp | 71 ++++++++++-- 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 | 8 +- src/util/Progress.cpp | 43 +++++++ tests/unit/capi/GEOSCoverageSimplifyTest.cpp | 21 +++- tests/unit/capi/GEOSDifferenceTest.cpp | 26 +++++ tests/unit/capi/GEOSUnaryUnionTest.cpp | 55 +++++++++ tests/unit/coverage/CoverageSimplifierTest.cpp | 18 ++- tests/unit/coverage/TPVWSimplifierTest.cpp | 6 +- util/geosop/GeometryOp.cpp | 2 +- 25 files changed, 554 insertions(+), 109 deletions(-) create mode 100644 include/geos/util/Progress.h create mode 100644 src/util/Progress.cpp hooks/post-receive -- GEOS From git at osgeo.org Tue Jul 7 08:36:46 2026 From: git at osgeo.org (git at osgeo.org) Date: Tue, 7 Jul 2026 08:36:46 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. d6809e45fdb9d6705ab9e55dcda3fdee07f6eddd Message-ID: <20260707153646.6206E19743A@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via d6809e45fdb9d6705ab9e55dcda3fdee07f6eddd (commit) from 40b2404209e65edd044386b11c4989d8226aeecb (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit d6809e45fdb9d6705ab9e55dcda3fdee07f6eddd Author: Daniel Baston Date: Tue Jul 7 11:36:27 2026 -0400 OverlayNG: Merge linear outputs (#1459) * OverlayNG: Merge linear outputs * OverlayNG: Update XML tests for merged output * split.xml: split with GeometrySplitter instead of Overlay * Update NEWS diff --git a/NEWS.md b/NEWS.md index 2113bbb67..9f0ae69ad 100644 --- a/NEWS.md +++ b/NEWS.md @@ -22,6 +22,8 @@ - Breaking Changes: - BufferOp returns POLYGON EMPTY when fed Inf/Nan coords (GH-1332) - Return Inf when calculating distance to an empty geometry (GH-1345, Even Rouault) + - Overlay operations now produce a LineString geometry in cases that would previously + produce a MultiLineString with contiguous sub-geometries. (GH-1459, Dan Baston) - Fixes/Improvements: diff --git a/src/operation/overlayng/LineBuilder.cpp b/src/operation/overlayng/LineBuilder.cpp index 4d1a25877..27c8bf1a1 100644 --- a/src/operation/overlayng/LineBuilder.cpp +++ b/src/operation/overlayng/LineBuilder.cpp @@ -35,7 +35,7 @@ std::vector> LineBuilder::getLines() { markResultLines(); - addResultLines(); + addResultLinesMerged(); // Transfer ownership of all the lines to the // caller @@ -252,10 +252,10 @@ LineBuilder::buildLine(OverlayEdge* node) const const bool constructZ = node->getCoordinatesRO()->hasZ(); const bool constructM = node->getCoordinatesRO()->hasM(); geom::util::CurveBuilder cb(*geometryFactory, constructZ, constructM); + cb.setOutputLinearRing(false); // assert: edgeStart degree = 1 // assert: edgeStart direction = forward - cb.add(*node->getCoordinatesRO(), node->isCurved()); bool isNodeForward = node->isForward(); diff --git a/tests/unit/capi/GEOSDifferenceTest.cpp b/tests/unit/capi/GEOSDifferenceTest.cpp index d38f28c1d..c5f70196b 100644 --- a/tests/unit/capi/GEOSDifferenceTest.cpp +++ b/tests/unit/capi/GEOSDifferenceTest.cpp @@ -73,7 +73,7 @@ void object::test<3>() result_ = GEOSDifference(geom1_, geom2_); ensure(result_); - expected_ = fromWKT("MULTICURVE (CIRCULARSTRING (1.7071067812 0.7071067812, 1.9238795325 0.3826834324, 2 0), CIRCULARSTRING (0 0, 0.6173165676 0.9238795325, 1.7071067812 0.7071067812))"); + expected_ = fromWKT("CIRCULARSTRING (0 0, 0.6173165676 0.9238795325, 1.7071067812 0.7071067812, 1.9238795325 0.3826834324, 2 0)"); ensure_geometry_equals(result_, expected_, 1e-8); } diff --git a/tests/unit/operation/geounion/UnaryUnionOpTest.cpp b/tests/unit/operation/geounion/UnaryUnionOpTest.cpp index 780907382..9f2023e39 100644 --- a/tests/unit/operation/geounion/UnaryUnionOpTest.cpp +++ b/tests/unit/operation/geounion/UnaryUnionOpTest.cpp @@ -78,6 +78,7 @@ struct test_unaryuniontest_data { GeomPtr b2 = normalize(b); bool eq = a2->equalsExact(b2.get()); if(! eq) { + cout << "EXPECTED: " << wktwriter.write(a2.get()) << endl; cout << "OBTAINED: " << wktwriter.write(b2.get()) << endl; } return eq; @@ -184,6 +185,8 @@ template<> void object::test<6> () { + set_test_name("non-simple LineString"); + static char const* const geoms[] = { "LINESTRING (0 0, 10 0, 5 -5, 5 5)", nullptr diff --git a/tests/unit/operation/overlayng/CoverageUnionNGTest.cpp b/tests/unit/operation/overlayng/CoverageUnionNGTest.cpp index 01095d0f5..49db51c2f 100644 --- a/tests/unit/operation/overlayng/CoverageUnionNGTest.cpp +++ b/tests/unit/operation/overlayng/CoverageUnionNGTest.cpp @@ -157,7 +157,7 @@ void object::test<9> () { checkUnion( "MULTILINESTRING ((1 1, 5 1), (9 1, 5 1))", - "MULTILINESTRING ((1 1, 5 1), (5 1, 9 1))"); + "LINESTRING (1 1, 5 1, 9 1)"); } /** @@ -169,7 +169,7 @@ void object::test<10> () { checkUnion( "MULTILINESTRING ((1 1, 2 1, 3 1), (4 1, 3 1, 2 1))", - "MULTILINESTRING ((1 1, 2 1), (2 1, 3 1), (3 1, 4 1))"); + "LINESTRING (1 1, 2 1, 3 1, 4 1)"); } /** @@ -181,27 +181,29 @@ void object::test<11> () { checkUnion( "MULTILINESTRING ((1 9, 3.1 8, 5 7, 7 8, 9 9), (5 7, 5 3, 4 3, 2 3), (9 5, 7 4, 5 3, 8 1))", - "MULTILINESTRING ((1 9, 3.1 8), (2 3, 4 3), (3.1 8, 5 7), (4 3, 5 3), (5 3, 5 7), (5 3, 7 4), (5 3, 8 1), (5 7, 7 8), (7 4, 9 5), (7 8, 9 9))"); + "MULTILINESTRING ((1 9, 3.1 8, 5 7), (2 3, 4 3, 5 3), (5 3, 5 7), (5 3, 7 4, 9 5), (5 3, 8 1), (5 7, 7 8, 9 9))"); } -// Z values preserved in linear inpuuts +// Z values preserved in linear inputs template<> template<> void object::test<12> () { + // TODO: Should Z values be averaged? checkUnion( "MULTILINESTRING Z ((1 1 8, 5 1 9), (9 1 6, 5 1 2))", - "MULTILINESTRING Z ((1 1 8, 5 1 9), (5 1 2, 9 1 6))"); + "LINESTRING Z (1 1 8, 5 1 9, 9 1 6)"); } -// M values preserved in linear inpuuts +// M values preserved in linear inputs template<> template<> void object::test<13> () { + // TODO: Should M values be averaged? checkUnion( "MULTILINESTRING M ((1 1 8, 5 1 9), (9 1 6, 5 1 2))", - "MULTILINESTRING M ((1 1 8, 5 1 9), (5 1 2, 9 1 6))"); + "LINESTRING M (1 1 8, 5 1 9, 9 1 6)"); } // Mixed Z/M values handled in linear inputs @@ -210,8 +212,9 @@ template<> template<> void object::test<14>() { + // TODO: Should M values be coalesced? checkUnion("GEOMETRYCOLLECTION (LINESTRING Z(1 1 8, 5 1 9), LINESTRING M(9 1 6, 5 1 2))", - "MULTILINESTRING ZM ((1 1 8 NaN, 5 1 9 NaN), (5 1 9 2, 9 1 8.5 6))"); + "LINESTRING ZM (1 1 8 NaN, 5 1 9 NaN, 9 1 8.5 6)"); } // Z values preserved in polygonal inputs diff --git a/tests/unit/operation/overlayng/OverlayNGGeometryCollectionTest.cpp b/tests/unit/operation/overlayng/OverlayNGGeometryCollectionTest.cpp index 3edf40cce..9eb04234d 100644 --- a/tests/unit/operation/overlayng/OverlayNGGeometryCollectionTest.cpp +++ b/tests/unit/operation/overlayng/OverlayNGGeometryCollectionTest.cpp @@ -131,10 +131,12 @@ template<> template<> void object::test<6> () { + set_test_name("testSimpleL_AA"); + std::string a = "LINESTRING (0 0, 10 10)"; std::string b = "GEOMETRYCOLLECTION ( POLYGON ((1 1, 1 5, 5 5, 5 1, 1 1)), POLYGON ((9 9, 9 5, 5 5, 5 9, 9 9)) )"; testIntersection(a, b, - "MULTILINESTRING ((1 1, 5 5), (5 5, 9 9))"); + "LINESTRING (1 1, 5 5, 9 9)"); testUnion(a, b, "GEOMETRYCOLLECTION (LINESTRING (0 0, 1 1), LINESTRING (9 9, 10 10), POLYGON ((1 1, 1 5, 5 5, 5 1, 1 1)), POLYGON ((5 5, 5 9, 9 9, 9 5, 5 5)))"); } diff --git a/tests/unit/operation/overlayng/OverlayNGTest.cpp b/tests/unit/operation/overlayng/OverlayNGTest.cpp index 2da3cbcca..0240fa457 100644 --- a/tests/unit/operation/overlayng/OverlayNGTest.cpp +++ b/tests/unit/operation/overlayng/OverlayNGTest.cpp @@ -234,10 +234,12 @@ template<> template<> void object::test<13> () { + set_test_name("testAreaLineIntersection"); + std::string a = "POLYGON ((360 200, 220 200, 220 180, 300 180, 300 160, 300 140, 360 200))"; std::string b = "MULTIPOLYGON (((280 180, 280 160, 300 160, 300 180, 280 180)), ((220 230, 240 230, 240 180, 220 180, 220 230)))"; // std::string exp = "POLYGON ((220 200, 240 200, 240 180, 220 180, 220 200))"; - std::string exp = "GEOMETRYCOLLECTION (LINESTRING (280 180, 300 180), LINESTRING (300 160, 300 180), POLYGON ((220 180, 220 200, 240 200, 240 180, 220 180)))"; + std::string exp = "GEOMETRYCOLLECTION (LINESTRING (280 180, 300 180, 300 160), POLYGON ((220 180, 220 200, 240 200, 240 180, 220 180)))"; testOverlay(a, b, exp, OverlayNG::INTERSECTION, 1); } @@ -500,9 +502,11 @@ template<> template<> void object::test<37> () { + set_test_name("testCollapseTriBoxUnion"); + std::string a = "POLYGON ((1 3.3, 1.3 1.4, 3.1 1.4, 3.1 0.9, 1.3 0.9, 1 -0.2, 0.8 1.3, 1 3.3))"; std::string b = "POLYGON ((1 2.9, 2.9 2.9, 2.9 1.3, 1.7 1, 1.3 0.9, 1 0.4, 1 2.9))"; - std::string exp = "MULTILINESTRING ((1 1, 1 0), (1 3, 1 1), (1 1, 2 1), (2 1, 3 1))"; + std::string exp = "MULTILINESTRING ((1 1, 1 0), (1 3, 1 1), (1 1, 2 1, 3 1))"; testOverlay(a, b, exp, OverlayNG::INTERSECTION, 1); } @@ -808,9 +812,7 @@ void object::test<62>() std::string a = "CIRCULARSTRING (0 0, 1 1, 2 0, 3 -1, 4 0)"; - // Noding causes the CircularString to split at (2, 0) - // OverlayNG does not merge output lines, so we get a MultiCurve. - std::string exp = "MULTICURVE (CIRCULARSTRING (0 0, 1 1, 2 0), CIRCULARSTRING (2 0, 3 -1, 4 0))"; + std::string exp = "CIRCULARSTRING (0 0, 1 1, 2 0, 3 -1, 4 0)"; testOverlay(a, a, exp, OverlayNG::UNION, 0); } diff --git a/tests/unit/operation/overlayng/OverlayNGZTest.cpp b/tests/unit/operation/overlayng/OverlayNGZTest.cpp index d8625d3cb..7eb79756c 100644 --- a/tests/unit/operation/overlayng/OverlayNGZTest.cpp +++ b/tests/unit/operation/overlayng/OverlayNGZTest.cpp @@ -129,11 +129,13 @@ template<> template<> void object::test<5> () { + set_test_name("testLineOverlapUnion"); + checkUnion("LINESTRING (0 0 0, 10 10 10)", "LINESTRING (5 5 990, 15 15 999)", - "MULTILINESTRING Z((0 0 0, 5 5 990), (5 5 990, 10 10 10), (10 10 10, 15 15 999))"); + "LINESTRING Z(0 0 0, 5 5 990, 10 10 10, 15 15 999)"); checkUnion("LINESTRING M (0 0 0, 10 10 10)", "LINESTRING M (5 5 990, 15 15 999)", - "MULTILINESTRING M((0 0 0, 5 5 990), (5 5 990, 10 10 10), (10 10 10, 15 15 999))"); + "LINESTRING M(0 0 0, 5 5 990, 10 10 10, 15 15 999)"); } // testLineLineXYDifferenceLineInterpolated diff --git a/tests/unit/operation/overlayng/PrecisionReducerTest.cpp b/tests/unit/operation/overlayng/PrecisionReducerTest.cpp index 7d9dc6ea0..8953a9dd2 100644 --- a/tests/unit/operation/overlayng/PrecisionReducerTest.cpp +++ b/tests/unit/operation/overlayng/PrecisionReducerTest.cpp @@ -159,8 +159,10 @@ template<> template<> void object::test<13> () { + set_test_name("testCollapsedNodedLine"); + checkReduce("LINESTRING(1 1, 3 3, 9 9, 5.1 5, 2.1 2)", - 1, "MULTILINESTRING ((1 1, 2 2), (2 2, 3 3), (3 3, 5 5), (5 5, 9 9))"); + 1, "LINESTRING (1 1, 2 2, 3 3, 5 5, 9 9)"); } // diff --git a/tests/unit/operation/sharedpaths/SharedPathsOpTest.cpp b/tests/unit/operation/sharedpaths/SharedPathsOpTest.cpp index ded1c83fc..78f6e9936 100644 --- a/tests/unit/operation/sharedpaths/SharedPathsOpTest.cpp +++ b/tests/unit/operation/sharedpaths/SharedPathsOpTest.cpp @@ -324,9 +324,8 @@ void object::test<16> forwDir.clear(); backDir.clear(); SharedPathsOp::sharedPathsOp(*g0, *g1, forwDir, backDir); - ensure_equals(forwDir.size(), 2u); - ensure_equals(wktwriter.write(forwDir[0]), "LINESTRING (0 0, 5 10)"); - ensure_equals(wktwriter.write(forwDir[1]), "LINESTRING (5 10, 10 10)"); + ensure_equals(forwDir.size(), 1u); + ensure_equals(wktwriter.write(forwDir[0]), "LINESTRING (0 0, 5 10, 10 10)"); SharedPathsOp::clearEdges(forwDir); ensure(backDir.empty()); @@ -343,9 +342,8 @@ void object::test<17> forwDir.clear(); backDir.clear(); SharedPathsOp::sharedPathsOp(*g0, *g1, forwDir, backDir); - ensure_equals(backDir.size(), 2u); - ensure_equals(wktwriter.write(backDir[0]), "LINESTRING (0 0, 5 10)"); - ensure_equals(wktwriter.write(backDir[1]), "LINESTRING (5 10, 10 10)"); + ensure_equals(backDir.size(), 1u); + ensure_equals(wktwriter.write(backDir[0]), "LINESTRING (0 0, 5 10, 10 10)"); SharedPathsOp::clearEdges(backDir); ensure(forwDir.empty()); diff --git a/tests/xmltester/XMLTester.cpp b/tests/xmltester/XMLTester.cpp index be5445813..01803404b 100644 --- a/tests/xmltester/XMLTester.cpp +++ b/tests/xmltester/XMLTester.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -1395,6 +1396,10 @@ void Test::executeOp(Geometry* gA, Geometry* gB) double tolerance = std::atof(opArg2.c_str()); checkResult( *geos::simplify::TopologyPreservingSimplifier::simplify(gA, tolerance) ); } + else if (opName == "split") + { + checkResult(*geos::operation::split::GeometrySplitter::split(*gA, *gB)); + } else { //TODO: error out here? std::cerr << tester.testcaseRef() << " - " << opName; diff --git a/tests/xmltester/tests/general/TestNGOverlayA.xml b/tests/xmltester/tests/general/TestNGOverlayA.xml index 3d981d481..7c9089c67 100644 --- a/tests/xmltester/tests/general/TestNGOverlayA.xml +++ b/tests/xmltester/tests/general/TestNGOverlayA.xml @@ -259,7 +259,7 @@ Uses a floating precision model. - MULTILINESTRING ((27 27, 27 13), (27 13, 13 13), (13 27, 27 27), (13 13, 13 27)) + LINESTRING (13 13, 13 27, 27 27, 27 13, 13 13) @@ -482,8 +482,7 @@ POLYGON ((13 27, 27 27, 27 13, 13 13, 13 27), (15 25, 20 25, 20 20, 15 20, 15 25 GEOMETRYCOLLECTION (POLYGON ((22 22, 27 22, 27 20, 20 20, 20 25, 20 27, 22 27, 22 22)), POLYGON ((15 20, 20 20, 20 13, 15 13, 15 15, 13 15, 13 20, 15 20)), - LINESTRING (22 27, 27 27), - LINESTRING (27 27, 27 22)) + LINESTRING (22 27, 27 27, 27 22)) diff --git a/tests/xmltester/tests/general/TestNGOverlayAPrec.xml b/tests/xmltester/tests/general/TestNGOverlayAPrec.xml index e20a8edd1..0eb8934f1 100644 --- a/tests/xmltester/tests/general/TestNGOverlayAPrec.xml +++ b/tests/xmltester/tests/general/TestNGOverlayAPrec.xml @@ -67,10 +67,9 @@ POLYGON ((1 3.3, 1.3 1.4, 3.1 1.4, 3.1 0.9, 1.3 0.9, 1 -0.2, 0.8 1.3, 1 3.3)) POLYGON ((1 2.9, 2.9 2.9, 2.9 1.3, 1.7 1, 1.3 0.9, 1 0.4, 1 2.9)) -MULTILINESTRING ((1 1, 2 1), +MULTILINESTRING ((1 1, 2 1, 3 1), (1 1, 1 0), - (1 3, 1 1), - (2 1, 3 1)) + (1 3, 1 1)) GEOMETRYCOLLECTION (POLYGON ((1 1, 1 3, 3 3, 3 1, 2 1, 1 1)), @@ -307,10 +306,10 @@ POLYGON ((1 1, 1 4, 3 4, 3 1.3, 7 1, 1 1)) POLYGON ((8 4, 9 4, 9 1, 4 1, 8 1.3, 8 4)) -MULTILINESTRING ((6 1, 7 1), (4 1, 6 1)) +LINESTRING (4 1, 6 1, 7 1) -GEOMETRYCOLLECTION (POLYGON ((9 4, 9 1, 8 1, 8 4, 9 4)), POLYGON ((1 4, 3 4, 3 1, 1 1, 1 4)), LINESTRING (8 1, 7 1), LINESTRING (6 1, 7 1), LINESTRING (3 1, 4 1), LINESTRING (4 1, 6 1)) +GEOMETRYCOLLECTION (POLYGON ((9 4, 9 1, 8 1, 8 4, 9 4)), POLYGON ((1 4, 3 4, 3 1, 1 1, 1 4)), LINESTRING (3 1, 4 1, 6 1, 7 1, 8 1)) GEOMETRYCOLLECTION (POLYGON ((1 4, 3 4, 3 1, 1 1, 1 4)), LINESTRING (3 1, 4 1)) @@ -323,4 +322,4 @@ GEOMETRYCOLLECTION (POLYGON ((9 4, 9 1, 8 1, 8 4, 9 4)), POLYGON ((1 4, 3 4, 3 1 - \ No newline at end of file + diff --git a/tests/xmltester/tests/general/TestNGOverlayGC.xml b/tests/xmltester/tests/general/TestNGOverlayGC.xml index d50eb44c7..1dd39b37d 100644 --- a/tests/xmltester/tests/general/TestNGOverlayGC.xml +++ b/tests/xmltester/tests/general/TestNGOverlayGC.xml @@ -74,7 +74,7 @@ Uses a floating precision model. - MULTILINESTRING ((1 1, 5 5), (5 5, 9 9)) + LINESTRING (1 1, 5 5, 9 9) diff --git a/tests/xmltester/tests/general/TestNGOverlayL.xml b/tests/xmltester/tests/general/TestNGOverlayL.xml index 4ec2faba4..1db4001d4 100644 --- a/tests/xmltester/tests/general/TestNGOverlayL.xml +++ b/tests/xmltester/tests/general/TestNGOverlayL.xml @@ -44,10 +44,10 @@ POINT (15 15) MULTILINESTRING ((10 10, 15 15), (15 15, 20 20), (10 20, 15 15), (15 15, 20 10)) -MULTILINESTRING ((10 10, 15 15), (15 15, 20 20)) +LINESTRING (10 10, 15 15, 20 20) -MULTILINESTRING ((10 20, 15 15), (15 15, 20 10)) +LINESTRING (10 20, 15 15, 20 10) MULTILINESTRING ((10 10, 15 15), (15 15, 20 20), (10 20, 15 15), (15 15, 20 10)) @@ -75,7 +75,7 @@ LINESTRING (10 10, 20 20) LINESTRING (20 20, 30 30) -MULTILINESTRING ((20 20, 30 30), (10 10, 20 20)) +LINESTRING (10 10, 20 20, 30 30) @@ -150,7 +150,7 @@ MULTILINESTRING ((0 10, 10 10, 20 20), (30 30, 40 30)) MULTILINESTRING ((20 0, 20 20), (30 30, 30 40)) -MULTILINESTRING ((0 10, 10 10, 20 20), (20 0, 20 20), (30 30, 30 40), (30 30, 40 30)) +MULTILINESTRING ((0 10, 10 10, 20 20, 20 0), (40 30, 30 30, 30 40)) @@ -169,13 +169,13 @@ GEOMETRYCOLLECTION (LINESTRING (0 0, 3 3), POINT (5 5)) MULTILINESTRING ((5 5, 1 9), (0 0, 3 3), (5 5, 10 10), (3 3, 8 2, 5 5), (3 3, 5 5)) -MULTILINESTRING ((5 5, 10 10), (3 3, 5 5)) +LINESTRING (3 3, 5 5, 10 10) -MULTILINESTRING ((5 5, 1 9), (3 3, 8 2, 5 5)) +LINESTRING (3 3, 8 2, 5 5, 1 9) -MULTILINESTRING ((5 5, 1 9), (5 5, 10 10), (3 3, 8 2, 5 5), (3 3, 5 5)) +MULTILINESTRING ((5 5, 1 9), (5 5, 10 10), (5 5, 3 3, 8 2, 5 5)) @@ -194,10 +194,10 @@ POINT (150 150) MULTILINESTRING ((150 150, 200 200, 300 300, 400 200), (100 100, 150 150), (190 110, 150 150), (150 150, 120 180)) -MULTILINESTRING ((150 150, 200 200, 300 300, 400 200), (100 100, 150 150)) +LINESTRING (100 100, 150 150, 200 200, 300 300, 400 200) -MULTILINESTRING ((190 110, 150 150), (150 150, 120 180)) +LINESTRING (190 110, 150 150, 120 180) MULTILINESTRING ((150 150, 200 200, 300 300, 400 200), (100 100, 150 150), (190 110, 150 150), (150 150, 120 180)) diff --git a/tests/xmltester/tests/general/TestNGOverlayLPrec.xml b/tests/xmltester/tests/general/TestNGOverlayLPrec.xml index 99ec16f03..70dabbf80 100644 --- a/tests/xmltester/tests/general/TestNGOverlayLPrec.xml +++ b/tests/xmltester/tests/general/TestNGOverlayLPrec.xml @@ -22,10 +22,10 @@ MULTIPOINT ((2 2), (3 3)) MULTILINESTRING ((1 1, 2 2), (2 2, 3 3), (3 3, 4 4), (2 2, 4 2), (3 1, 2 2), (3 4, 3 3), (3 3, 1 3)) -MULTILINESTRING ((1 1, 2 2), (2 2, 3 3), (3 3, 4 4)) +LINESTRING (1 1, 2 2, 3 3, 4 4) -MULTILINESTRING ((2 2, 4 2), (3 1, 2 2), (3 4, 3 3), (3 3, 1 3)) +MULTILINESTRING ((3 1, 2 2, 4 2), (3 4, 3 3, 1 3)) MULTILINESTRING ((1 1, 2 2), (2 2, 3 3), (3 3, 4 4), (2 2, 4 2), (3 1, 2 2), (3 4, 3 3), (3 3, 1 3)) @@ -41,10 +41,10 @@ LINESTRING (1.1 1.3, 2.6 2.8, 3.8 3.3) LINESTRING (0.3 2.8, 1.4 1.9, 2.7 2.6, 2.9 3.8) -MULTILINESTRING ((1 2, 2 2), (2 2, 3 3)) +LINESTRING (1 2, 2 2, 3 3) -MULTILINESTRING ((1 1, 1 2), (1 2, 2 2), (2 2, 3 3), (3 3, 4 3), (0 3, 1 2), (3 3, 3 4)) +MULTILINESTRING ((1 1, 1 2), (1 2, 2 2, 3 3), (3 3, 4 3), (0 3, 1 2), (3 3, 3 4)) MULTILINESTRING ((1 1, 1 2), (3 3, 4 3)) @@ -53,7 +53,7 @@ MULTILINESTRING ((1 1, 1 2), (3 3, 4 3)) MULTILINESTRING ((0 3, 1 2), (3 3, 3 4)) -MULTILINESTRING ((1 1, 1 2), (3 3, 4 3), (0 3, 1 2), (3 3, 3 4)) +MULTILINESTRING ((1 1, 1 2, 0 3), (3 4, 3 3, 4 3)) @@ -66,10 +66,10 @@ LINESTRING (0 0, 1.8 2.3, 1.1 1.1, 3 3) LINESTRING (0.7 0.7, 2.8 2.6) -MULTILINESTRING ((1 1, 2 2), (2 2, 3 3)) +LINESTRING (1 1, 2 2, 3 3) -MULTILINESTRING ((0 0, 1 1), (1 1, 2 2), (2 2, 3 3)) +LINESTRING (0 0, 1 1, 2 2, 3 3) LINESTRING (0 0, 1 1) @@ -91,10 +91,10 @@ LINESTRING (0 1, 0.9 1.1, 1.8 1.1, 3.2 0.9, 5 0.7, 6.1 0.7, 7.3 0.6) LINESTRING (0 2, 1.1 1.7, 3.7 1.2, 5.4 1.6, 6.1 2.1, 7.2 2.2) -MULTILINESTRING ((4 1, 5 1), (2 1, 3 1), (3 1, 4 1)) +LINESTRING (2 1, 3 1, 4 1, 5 1) -MULTILINESTRING ((5 1, 6 1, 7 1), (0 1, 1 1, 2 1), (0 2, 1 2, 2 1), (4 1, 5 1), (2 1, 3 1), (3 1, 4 1), (5 1, 5 2, 6 2, 7 2)) +MULTILINESTRING ((5 1, 6 1, 7 1), (0 1, 1 1, 2 1), (0 2, 1 2, 2 1), (2 1, 3 1, 4 1, 5 1), (5 1, 5 2, 6 2, 7 2)) MULTILINESTRING ((5 1, 6 1, 7 1), (0 1, 1 1, 2 1)) @@ -103,8 +103,8 @@ MULTILINESTRING ((5 1, 6 1, 7 1), (0 1, 1 1, 2 1)) MULTILINESTRING ((0 2, 1 2, 2 1), (5 1, 5 2, 6 2, 7 2)) -MULTILINESTRING ((5 1, 6 1, 7 1), (0 1, 1 1, 2 1), (0 2, 1 2, 2 1), (5 1, 5 2, 6 2, 7 2)) +MULTILINESTRING ((7 2, 6 2, 5 2, 5 1, 6 1, 7 1), (0 1, 1 1, 2 1, 1 2, 0 2)) - \ No newline at end of file + diff --git a/tests/xmltester/tests/general/TestOverlayAA.xml b/tests/xmltester/tests/general/TestOverlayAA.xml index 2f4a5d92e..6a93130aa 100644 --- a/tests/xmltester/tests/general/TestOverlayAA.xml +++ b/tests/xmltester/tests/general/TestOverlayAA.xml @@ -357,9 +357,7 @@ GEOMETRYCOLLECTION( POINT(200 240), LINESTRING(300 200, 220 200), - LINESTRING(280 180, 300 180), - LINESTRING(300 180, 300 160), - LINESTRING(300 160, 280 160), + LINESTRING(280 180, 300 180, 300 160, 280 160), LINESTRING(220 140, 240 140), LINESTRING(240 120, 220 120), POLYGON( @@ -451,22 +449,10 @@ POINT(260 160), POINT(280 220), POINT(300 200), - LINESTRING(100 200, 100 180), - LINESTRING(100 180, 120 180), - LINESTRING(120 180, 120 200), - LINESTRING(120 200, 100 200), - LINESTRING(220 140, 220 160), - LINESTRING(220 160, 160 160), - LINESTRING(160 160, 160 180), - LINESTRING(160 180, 200 180), - LINESTRING(200 200, 160 200), - LINESTRING(160 200, 160 220), - LINESTRING(160 220, 220 220), - LINESTRING(220 220, 220 240), - LINESTRING(80 220, 80 160), - LINESTRING(80 160, 140 160), - LINESTRING(140 160, 140 220), - LINESTRING(140 220, 80 220)) + LINESTRING(100 200, 100 180, 120 180, 120 200, 100 200), + LINESTRING(220 140, 220 160, 160 160, 160 180, 200 180), + LINESTRING(200 200, 160 200, 160 220, 220 220, 220 240), + LINESTRING(80 220, 80 160, 140 160, 140 220, 80 220)) diff --git a/tests/xmltester/tests/general/TestOverlayAAPrec.xml b/tests/xmltester/tests/general/TestOverlayAAPrec.xml index f3b8dc0ae..c87ce14a2 100644 --- a/tests/xmltester/tests/general/TestOverlayAAPrec.xml +++ b/tests/xmltester/tests/general/TestOverlayAAPrec.xml @@ -115,9 +115,7 @@ - MULTILINESTRING( - (10 30, 10 10), - (10 10, 30 10)) + LINESTRING (10 30, 10 10, 30 10) @@ -273,9 +271,7 @@ - MULTILINESTRING( - (10 30, 10 10), - (10 10, 30 10)) + LINESTRING (10 30, 10 10, 30 10) @@ -317,9 +313,7 @@ - MULTILINESTRING( - (10 30, 10 10), - (10 10, 30 10)) + LINESTRING(10 30, 10 10, 30 10) @@ -361,9 +355,7 @@ - MULTILINESTRING( - (10 30, 10 10), - (10 10, 30 10)) + LINESTRING(10 30, 10 10, 30 10) @@ -606,9 +598,7 @@ GEOMETRYCOLLECTION( - MULTILINESTRING( - (160 140, 160 120), - (160 120, 160 100)) + LINESTRING(160 140, 160 120, 160 100) @@ -719,8 +709,7 @@ GEOMETRYCOLLECTION( GEOMETRYCOLLECTION( - LINESTRING(78 39, 83 39), - LINESTRING(83 33, 83 39), + LINESTRING(78 39, 83 39, 83 33), POLYGON( (83 39, 62 402, 68 402, 83 39)), POLYGON( diff --git a/tests/xmltester/tests/general/TestOverlayLA.xml b/tests/xmltester/tests/general/TestOverlayLA.xml index f9444fbf9..97cab3b82 100644 --- a/tests/xmltester/tests/general/TestOverlayLA.xml +++ b/tests/xmltester/tests/general/TestOverlayLA.xml @@ -20,8 +20,7 @@ GEOMETRYCOLLECTION( LINESTRING(100 240, 100 180, 160 180, 160 120, 220 120), - LINESTRING(40 360, 40 60), - LINESTRING(40 60, 340 60, 40 360), + LINESTRING(40 60, 340 60, 40 360, 40 60), LINESTRING(40 60, 40 20), LINESTRING(120 120, 120 140, 100 140, 100 120, 120 120), LINESTRING(120 120, 140 120), @@ -87,20 +86,16 @@ (220 260, 140 260), (140 260, 120 260), (120 260, 60 260), - (200 240, 140 240), + (220 240, 200 240, 140 240), (140 240, 120 240), - (120 240, 80 240), (180 220, 140 220), (140 220, 120 220), (120 220, 100 220), - (120 200, 120 180), - (220 240, 200 240), - (80 240, 60 240), + (120 220, 120 200, 120 180, 120 160), + (120 240, 80 240, 60 240), (60 220, 80 220), (200 220, 220 220), (120 260, 120 240), - (120 220, 120 200), - (120 180, 120 160), (120 140, 120 120), (140 120, 140 140), (140 160, 140 180), @@ -160,17 +155,14 @@ LINESTRING(80 220, 100 220), LINESTRING(180 220, 200 220), LINESTRING(220 220, 240 220), - LINESTRING(120 300, 120 260), + LINESTRING(120 320, 120 300, 120 260), LINESTRING(120 240, 120 220), LINESTRING(120 160, 120 140), - LINESTRING(120 120, 120 80), - LINESTRING(120 80, 140 80), - LINESTRING(140 80, 140 120), + LINESTRING(120 120, 120 80, 140 80, 140 120), LINESTRING(140 140, 140 160), LINESTRING(140 180, 140 200), LINESTRING(140 220, 140 240), LINESTRING(140 260, 140 300), - LINESTRING(120 300, 120 320), POLYGON( (60 240, 60 260, 120 260, 140 260, 220 260, 220 240, 220 220, 220 120, 140 120, 120 120, 60 120, 60 220, 60 240), @@ -205,8 +197,7 @@ GEOMETRYCOLLECTION( LINESTRING(220 360, 220 320), LINESTRING(220 260, 220 240, 240 240), - LINESTRING(280 240, 300 240), - LINESTRING(300 240, 300 360), + LINESTRING(280 240, 300 240, 300 360), POLYGON( (280 240, 280 120, 60 120, 60 320, 220 320, 280 320, 280 240), (120 260, 120 180, 240 180, 240 240, 240 260, 220 260, 120 260)), @@ -249,10 +240,9 @@ GEOMETRYCOLLECTION( POINT(300 240), POINT(300 360), - LINESTRING(80 300, 80 160), + LINESTRING(220 300, 80 300, 80 160), LINESTRING(80 160, 260 160, 260 240), LINESTRING(260 240, 260 300, 220 300), - LINESTRING(220 300, 80 300), LINESTRING(80 160, 80 140), LINESTRING(220 320, 220 300), LINESTRING(220 300, 220 260), @@ -280,12 +270,9 @@ GEOMETRYCOLLECTION( - LINESTRING(180 260, 120 180), - LINESTRING(120 180, 60 260), - LINESTRING(60 260, 180 260), + LINESTRING(120 180, 60 260, 180 260, 120 180), LINESTRING(60 300, 60 260), - LINESTRING(60 260, 60 80), - LINESTRING(60 80, 60 40), + LINESTRING(60 260, 60 80, 60 40), POLYGON( (60 80, 120 180, 180 80, 60 80)), POLYGON( diff --git a/tests/xmltester/tests/general/TestOverlayLL.xml b/tests/xmltester/tests/general/TestOverlayLL.xml index 89c481eb5..3d723abb0 100644 --- a/tests/xmltester/tests/general/TestOverlayLL.xml +++ b/tests/xmltester/tests/general/TestOverlayLL.xml @@ -24,9 +24,7 @@ - MULTILINESTRING( - (0 0, 50 50), - (50 50, 100 100)) + LINESTRING(0 0, 50 50, 100 100) @@ -68,9 +66,7 @@ - MULTILINESTRING( - (100 100, 200 200), - (100 100, 200 0)) + LINESTRING(200 0, 100 100, 200 200) @@ -119,10 +115,7 @@ - MULTILINESTRING( - (220 160, 80 300), - (80 320, 220 320), - (220 320, 220 160)) + LINESTRING(80 320, 220 320, 220 160, 80 300) @@ -172,9 +165,7 @@ - MULTILINESTRING( - (60 200, 60 260, 140 200), - (60 200, 60 140, 140 200)) + LINESTRING(60 200, 60 260, 140 200, 60 140, 60 200) @@ -204,19 +195,16 @@ - MULTILINESTRING( - (100 120, 180 200), - (180 200, 100 280, 20 200, 100 120)) + LINESTRING(180 200, 100 280, 20 200, 100 120, 180 200) MULTILINESTRING( (100 120, 180 200), - (100 120, 100 200), + (100 120, 100 200, 180 200), (180 200, 100 280, 20 200, 100 120), - (180 200, 220 200, 220 80, 100 80, 100 120), - (100 200, 180 200)) + (180 200, 220 200, 220 80, 100 80, 100 120)) diff --git a/tests/xmltester/tests/general/TestOverlayLLPrec.xml b/tests/xmltester/tests/general/TestOverlayLLPrec.xml index e47e58661..17321cebc 100644 --- a/tests/xmltester/tests/general/TestOverlayLLPrec.xml +++ b/tests/xmltester/tests/general/TestOverlayLLPrec.xml @@ -72,29 +72,20 @@ MULTILINESTRING( - (200 220, 177 237), - (177 237, 153 253), - (153 253, 60 320, 40 300, 133 233), - (133 233, 157 217), - (157 217, 180 200), - (160 180, 137 197), - (137 197, 113 213), - (113 213, 20 280)) + (200 220, 177 237, 153 253, 60 320, 40 300, 133 233, 157 217, 180 200), + (160 180, 137 197, 113 213, 20 280)) MULTILINESTRING( - (200 220, 177 237), + (157 217, 180 200, 200 220, 177 237), (177 237, 153 253), (153 253, 60 320, 40 300, 133 233), (133 233, 157 217), - (157 217, 180 200), - (160 180, 137 197), (137 197, 113 213), (113 213, 20 280), - (200 220, 180 200), - (160 180, 140 160, 120 180, 137 197), + (137 197, 160 180, 140 160, 120 180, 137 197), (137 197, 157 217), (157 217, 177 237), (177 237, 220 280, 200 300, 153 253), diff --git a/tests/xmltester/tests/general/TestOverlayPL.xml b/tests/xmltester/tests/general/TestOverlayPL.xml index ba072db46..e47bd6bbf 100644 --- a/tests/xmltester/tests/general/TestOverlayPL.xml +++ b/tests/xmltester/tests/general/TestOverlayPL.xml @@ -108,8 +108,7 @@ MULTILINESTRING( (100 320, 100 220), (100 180, 200 180), - (220 180, 220 320), - (220 320, 160 320)) + (220 180, 220 320, 160 320)) @@ -120,8 +119,7 @@ POINT(140 320), LINESTRING(100 320, 100 220), LINESTRING(100 180, 200 180), - LINESTRING(220 180, 220 320), - LINESTRING(220 320, 160 320)) + LINESTRING(220 180, 220 320, 160 320)) @@ -157,9 +155,7 @@ MULTILINESTRING( - (-500 -140, -500 -280), - (-500 -280, -320 -280, -320 -140), - (-320 -140, -500 -140), + (-500 -280, -320 -280, -320 -140, -500 -140, -500 -280), (-500 -280, -500 -340)) @@ -170,13 +166,10 @@ POINT(-420 -180), POINT(-320 -120), POINT(-280 -140), - LINESTRING(-500 -140, -500 -280), - LINESTRING(-500 -280, -320 -280, -320 -140), - LINESTRING(-320 -140, -500 -140), + LINESTRING(-500 -280, -320 -280, -320 -140, -500 -140, -500 -280), LINESTRING(-500 -280, -500 -340)) - - + mPmL - points in I, B and E of lines, lines overlap, points overlap @@ -217,8 +210,7 @@ MULTILINESTRING( (100 320, 100 220), (100 180, 200 180), - (220 180, 220 320), - (220 320, 160 320)) + (220 180, 220 320, 160 320)) @@ -229,8 +221,7 @@ POINT(140 320), LINESTRING(100 320, 100 220), LINESTRING(100 180, 200 180), - LINESTRING(220 180, 220 320), - LINESTRING(220 320, 160 320)) + LINESTRING(220 180, 220 320, 160 320)) diff --git a/tests/xmltester/tests/issue/issue-geos-527.xml b/tests/xmltester/tests/issue/issue-geos-527.xml index ba1b2f61e..ecbf20aeb 100644 --- a/tests/xmltester/tests/issue/issue-geos-527.xml +++ b/tests/xmltester/tests/issue/issue-geos-527.xml @@ -19,7 +19,7 @@ LINESTRING( -MULTILINESTRING ((1725064.13656044 4819094.70235069, 1725063 4819121), (1725063 4819121, 1725064.141830536 4819094.702276371), (1725064.13656044 4819094.70235069, 1725064.141830536 4819094.702276371), (1725064.141830536 4819094.702276371, 1725064.14183882 4819094.70208557, 1725064.13656044 4819094.70235069), (1725064.141830536 4819094.702276371, 1725064.14210362 4819094.70227252)) +MULTILINESTRING ((1725064.13656044 4819094.70235069, 1725063 4819121, 1725064.141830536 4819094.702276371), (1725064.13656044 4819094.70235069, 1725064.141830536 4819094.702276371), (1725064.141830536 4819094.702276371, 1725064.14183882 4819094.70208557, 1725064.13656044 4819094.70235069), (1725064.141830536 4819094.702276371, 1725064.14210362 4819094.70227252)) diff --git a/tests/xmltester/tests/misc/makevalid.xml b/tests/xmltester/tests/misc/makevalid.xml index bee4be4c7..b11a3462c 100644 --- a/tests/xmltester/tests/misc/makevalid.xml +++ b/tests/xmltester/tests/misc/makevalid.xml @@ -139,7 +139,7 @@ MULTIPOLYGON(((0 0,1 1,0 1,1 0,0 0)),((0.8 0.1,2 0.1,2 0.9,0.8 0.9,0.8 0.1))) - GEOMETRYCOLLECTION(MULTIPOLYGON(((0 0,0.5 0.5,0.8 0.2,0.8 0.8,0.9 0.9,2 0.9,2 0.1,0.9 0.1,1 0,0 0)),((0 1,1 1,0.9 0.9,0.8 0.9,0.8 0.8,0.5 0.5,0 1))),MULTILINESTRING((0.8 0.1,0.9 0.1),(0.8 0.1,0.8 0.2))) + GEOMETRYCOLLECTION(MULTIPOLYGON(((0 0,0.5 0.5,0.8 0.2,0.8 0.8,0.9 0.9,2 0.9,2 0.1,0.9 0.1,1 0,0 0)),((0 1,1 1,0.9 0.9,0.8 0.9,0.8 0.8,0.5 0.5,0 1))),LINESTRING(0.8 0.2, 0.8 0.1,0.9 0.1)) diff --git a/tests/xmltester/tests/misc/split.xml b/tests/xmltester/tests/misc/split.xml index b884e529e..0a4a79445 100644 --- a/tests/xmltester/tests/misc/split.xml +++ b/tests/xmltester/tests/misc/split.xml @@ -1,49 +1,45 @@ - - Line/line difference/split + Line/line split LINESTRING(2 0,10 0,10 10,0 10,0 2) LINESTRING(-5 5, 15 5) - - MULTILINESTRING((2 0,10 0,10 5),(10 5,10 10,0 10,0 5),(0 5,0 2)) + + GEOMETRYCOLLECTION(LINESTRING(2 0,10 0,10 5),LINESTRING(10 5,10 10,0 10,0 5),LINESTRING(0 5,0 2)) - Line/line difference/split with boundary intersection + Line/line split with boundary intersection LINESTRING(2 0,10 0,10 10,0 10,0 2) LINESTRING(0 5, 10 5) - - MULTILINESTRING((2 0,10 0,10 5),(10 5,10 10,0 10,0 5),(0 5,0 2)) + + GEOMETRYCOLLECTION(LINESTRING(2 0,10 0,10 5),LINESTRING(10 5,10 10,0 10,0 5),LINESTRING(0 5,0 2)) - Multiline/line difference/split + Multiline/line split MULTILINESTRING((0 0, 0 10), (5 0, 5 10), (10 0, 10 10)) LINESTRING(-5 5, 15 5) - - MULTILINESTRING((0 0,0 5),(0 5,0 10),(5 0,5 5),(5 5,5 10),(10 0,10 5),(10 5,10 10)) + + GEOMETRYCOLLECTION(LINESTRING(0 0,0 5),LINESTRING(0 5,0 10),LINESTRING(5 0,5 5),LINESTRING(5 5,5 10),LINESTRING(10 0,10 5),LINESTRING(10 5,10 10)) - Multiline/line difference/split with boundary intersection + Multiline/line split with boundary intersection MULTILINESTRING((0 0, 0 10), (5 0, 5 10), (10 0, 10 10)) LINESTRING(0 5, 10 5) - - MULTILINESTRING((0 0,0 5),(0 5,0 10),(5 0,5 5),(5 5,5 10),(10 0,10 5),(10 5,10 10)) + + GEOMETRYCOLLECTION(LINESTRING(0 0,0 5),LINESTRING(0 5,0 10),LINESTRING(5 0,5 5),LINESTRING(5 5,5 10),LINESTRING(10 0,10 5),LINESTRING(10 5,10 10)) ----------------------------------------------------------------------- Summary of changes: NEWS.md | 2 ++ src/operation/overlayng/LineBuilder.cpp | 4 +-- tests/unit/capi/GEOSDifferenceTest.cpp | 2 +- tests/unit/operation/geounion/UnaryUnionOpTest.cpp | 3 ++ .../operation/overlayng/CoverageUnionNGTest.cpp | 19 +++++++------ .../overlayng/OverlayNGGeometryCollectionTest.cpp | 4 ++- tests/unit/operation/overlayng/OverlayNGTest.cpp | 12 ++++---- tests/unit/operation/overlayng/OverlayNGZTest.cpp | 6 ++-- .../operation/overlayng/PrecisionReducerTest.cpp | 4 ++- .../operation/sharedpaths/SharedPathsOpTest.cpp | 10 +++---- tests/xmltester/XMLTester.cpp | 5 ++++ tests/xmltester/tests/general/TestNGOverlayA.xml | 5 ++-- .../xmltester/tests/general/TestNGOverlayAPrec.xml | 11 ++++---- tests/xmltester/tests/general/TestNGOverlayGC.xml | 2 +- tests/xmltester/tests/general/TestNGOverlayL.xml | 18 ++++++------ .../xmltester/tests/general/TestNGOverlayLPrec.xml | 22 +++++++-------- tests/xmltester/tests/general/TestOverlayAA.xml | 24 ++++------------ .../xmltester/tests/general/TestOverlayAAPrec.xml | 23 ++++----------- tests/xmltester/tests/general/TestOverlayLA.xml | 33 +++++++--------------- tests/xmltester/tests/general/TestOverlayLL.xml | 26 +++++------------ .../xmltester/tests/general/TestOverlayLLPrec.xml | 17 +++-------- tests/xmltester/tests/general/TestOverlayPL.xml | 23 +++++---------- tests/xmltester/tests/issue/issue-geos-527.xml | 2 +- tests/xmltester/tests/misc/makevalid.xml | 2 +- tests/xmltester/tests/misc/split.xml | 28 ++++++++---------- 25 files changed, 126 insertions(+), 181 deletions(-) hooks/post-receive -- GEOS From git at osgeo.org Tue Jul 7 08:38:00 2026 From: git at osgeo.org (git at osgeo.org) Date: Tue, 7 Jul 2026 08:38:00 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. 4b90f39171430dd3c5a3c327b59dbd5df082df2c Message-ID: <20260707153800.CC7B6197897@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via 4b90f39171430dd3c5a3c327b59dbd5df082df2c (commit) from d6809e45fdb9d6705ab9e55dcda3fdee07f6eddd (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 4b90f39171430dd3c5a3c327b59dbd5df082df2c Author: Daniel Baston Date: Tue Jul 7 11:37:43 2026 -0400 Update NEWS.md diff --git a/NEWS.md b/NEWS.md index 9f0ae69ad..c2872e8dc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -18,6 +18,7 @@ - Polygonizer (GH-1420, Dan Baston) - Add GEOSLineToCurve, GEOSCurveToLine (GH-1382, Dan Baston) - Add GeometrySplitter (GH-1424, Dan Baston) + - Add progress reporting to GEOSCoverageSimplify, GEOSUnaryUnion (GH-1466, Even Rouault / Dan Baston) - Breaking Changes: - BufferOp returns POLYGON EMPTY when fed Inf/Nan coords (GH-1332) ----------------------------------------------------------------------- Summary of changes: NEWS.md | 1 + 1 file changed, 1 insertion(+) hooks/post-receive -- GEOS From git at osgeo.org Tue Jul 7 14:31:03 2026 From: git at osgeo.org (git at osgeo.org) Date: Tue, 7 Jul 2026 14:31:03 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. 9276016099d4f04efeee034b5d4d00d4e27cb2b7 Message-ID: <20260707213103.609601A75E5@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via 9276016099d4f04efeee034b5d4d00d4e27cb2b7 (commit) via fa485a550914d963413d85a8878b32d4c43d540f (commit) from 4b90f39171430dd3c5a3c327b59dbd5df082df2c (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 9276016099d4f04efeee034b5d4d00d4e27cb2b7 Author: Paul Ramsey Date: Tue Jul 7 14:29:51 2026 -0700 Beta release to website diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2931ed4ae..309573c6b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -62,7 +62,7 @@ jobs: mkdir _build && cd _build cmake -DBUILD_DOCUMENTATION=YES .. cmake --version - cmake --build . --target distcheck + cmake --build . --target distcheck -j $(nproc) - name: Create Release if: ${{ steps.tag.outputs.tag == steps.version.outputs.geosversion }} diff --git a/web/content/usage/download.md b/web/content/usage/download.md index 33f4d3003..2958dfd0d 100644 --- a/web/content/usage/download.md +++ b/web/content/usage/download.md @@ -1,6 +1,6 @@ --- title: "Download and Build" -date: 2025-08-21 +date: 2025-07-07 draft: false --- @@ -8,6 +8,7 @@ draft: false | Release | Release Date | First Release | Final Release | Download Link | Changes | | :--: | :--:| :--: |:--: | :--: | :--: | +| **3.15.0beta1** | 2026/07/07 | 2026/07/07 | *2030/10/01* | [geos-3.15.0beta1.tar.bz2](https://download.osgeo.org/geos/geos-3.15.0beta1.tar.bz2) | [Changes](https://github.com/libgeos/geos/blob/3.15.0beta1/NEWS.md) | | **3.14.1** | 2025/10/27 | 2025/08/21 | *2029/10/01* | [geos-3.14.1.tar.bz2](https://download.osgeo.org/geos/geos-3.14.1.tar.bz2) | [Changes](https://github.com/libgeos/geos/blob/3.14.1/NEWS.md) | | **3.13.1** | 2025/03/03 | 2024/09/06 | *2028/10/01* | [geos-3.13.1.tar.bz2](https://download.osgeo.org/geos/geos-3.13.1.tar.bz2) | [Changes](https://github.com/libgeos/geos/blob/3.13.1/NEWS.md) | | **3.12.3** | 2025/03/03 | 2023/06/27 | *2027/07/01* | [geos-3.12.3.tar.bz2](https://download.osgeo.org/geos/geos-3.12.3.tar.bz2) | [Changes](https://github.com/libgeos/geos/blob/3.12.3/NEWS.md) | @@ -61,7 +62,7 @@ They are specified on the `cmake` cmdline as `-DVAR=VALUE`. | :------------------: | :--------: | :---: | | CMAKE_BUILD_TYPE | Release | Use `Debug` to build with debug flags and optimizations off. Use `Release` for packaging and production installs. Use `RelWithDebInfo` for optimized build with debug symbols. Use `ASAN` to enable memory usage checking. | | CMAKE_INSTALL_PREFIX | /usr/local | Set to install root. Libraries end up in `./lib` or `./lib64`, headers in `./include`, executables in `./bin` | -| BUILD_DOCUMENTATION | ON | Attempt to find `doxygen` executable and build API docs | +| BUILD_DOCUMENTATION | OFF | Attempt to find `doxygen` executable and build API docs | | BUILD_SHARED_LIBS | ON | Build dynamically linkable libraries | | BUILD_TESTING | ON | Build unit tests | | BUILD_BENCHMARKS | OFF | Build benchmark tests | commit fa485a550914d963413d85a8878b32d4c43d540f Author: Paul Ramsey Date: Tue Jul 7 13:59:45 2026 -0700 Bump versions for beta release diff --git a/Version.txt b/Version.txt index 93dccb60b..c6ea1ee9a 100644 --- a/Version.txt +++ b/Version.txt @@ -5,7 +5,7 @@ GEOS_VERSION_MINOR=15 GEOS_VERSION_PATCH=0 # OPTIONS: "", "dev", "rc1" etc. -GEOS_PATCH_WORD=dev +GEOS_PATCH_WORD=beta1 # GEOS CAPI Versions # @@ -20,4 +20,4 @@ CAPI_INTERFACE_REVISION=0 CAPI_INTERFACE_AGE=21 # JTS Port -JTS_PORT=1.18.0 +JTS_PORT=1.20.0 ----------------------------------------------------------------------- Summary of changes: .github/workflows/release.yml | 2 +- Version.txt | 4 ++-- web/content/usage/download.md | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) hooks/post-receive -- GEOS From git at osgeo.org Tue Jul 7 14:31:50 2026 From: git at osgeo.org (git at osgeo.org) Date: Tue, 7 Jul 2026 14:31:50 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. 1cd8399189d96ca1b9fac9a02517e84ce9aadee4 Message-ID: <20260707213150.76C531A7362@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via 1cd8399189d96ca1b9fac9a02517e84ce9aadee4 (commit) from 9276016099d4f04efeee034b5d4d00d4e27cb2b7 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 1cd8399189d96ca1b9fac9a02517e84ce9aadee4 Author: Paul Ramsey Date: Tue Jul 7 14:31:10 2026 -0700 Bump version to catch next changes diff --git a/Version.txt b/Version.txt index c6ea1ee9a..46e851682 100644 --- a/Version.txt +++ b/Version.txt @@ -5,7 +5,7 @@ GEOS_VERSION_MINOR=15 GEOS_VERSION_PATCH=0 # OPTIONS: "", "dev", "rc1" etc. -GEOS_PATCH_WORD=beta1 +GEOS_PATCH_WORD=beta2 # GEOS CAPI Versions # ----------------------------------------------------------------------- Summary of changes: Version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) hooks/post-receive -- GEOS From git at osgeo.org Tue Jul 7 18:11:23 2026 From: git at osgeo.org (git at osgeo.org) Date: Tue, 7 Jul 2026 18:11:23 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. 678482bd85082ab0c671a100373a1d9c46764ffe Message-ID: <20260708011123.D3DB31A7C40@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via 678482bd85082ab0c671a100373a1d9c46764ffe (commit) from 1cd8399189d96ca1b9fac9a02517e84ce9aadee4 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 678482bd85082ab0c671a100373a1d9c46764ffe Author: Tom Payne Date: Wed Jul 8 03:10:55 2026 +0200 geos_c.h: Make enums available with GEOS_USE_ONLY_R_API defined (#1470) diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in index cb91502fd..26b88aca6 100644 --- a/capi/geos_c.h.in +++ b/capi/geos_c.h.in @@ -1108,6 +1108,16 @@ extern GEOSGeometry GEOS_DLL *GEOSPolygonHullSimplify_r( unsigned int isOuter, double vertexNumFraction); +/** +* Controls the behavior of the GEOSPolygonHullSimplify parameter. +*/ +enum GEOSPolygonHullParameterModes { + /** Fraction of input vertices retained */ + GEOSHULL_PARAM_VERTEX_RATIO = 1, + /** Ratio of simplified hull area to input area */ + GEOSHULL_PARAM_AREA_RATIO = 2 +}; + /** \see GEOSPolygonHullSimplifyMode */ extern GEOSGeometry GEOS_DLL *GEOSPolygonHullSimplifyMode_r( GEOSContextHandle_t handle, @@ -1371,6 +1381,17 @@ extern GEOSGeometry GEOS_DLL * GEOSConstrainedDelaunayTriangulation_r( GEOSContextHandle_t handle, const GEOSGeometry *g); +/** Change behaviour of \ref GEOSVoronoiDiagram */ +enum GEOSVoronoiFlags +{ + /** Return only edges of the Voronoi cells, as a MultiLineString **/ + GEOS_VORONOI_ONLY_EDGES = 1, + /** Preserve order of inputs, such that the nth cell in the result corresponds + * to the nth vertex in the input. If this cannot be done, such as for inputs + * that contain repeated points, \ref GEOSVoronoiDiagram will return NULL. **/ + GEOS_VORONOI_PRESERVE_ORDER = 2 +}; + /** \see GEOSVoronoiDiagram */ extern GEOSGeometry GEOS_DLL * GEOSVoronoiDiagram_r( GEOSContextHandle_t extHandle, @@ -5282,16 +5303,6 @@ extern GEOSGeometry GEOS_DLL *GEOSPolygonHullSimplify( double vertexNumFraction); -/** -* Controls the behavior of the GEOSPolygonHullSimplify parameter. -*/ -enum GEOSPolygonHullParameterModes { - /** Fraction of input vertices retained */ - GEOSHULL_PARAM_VERTEX_RATIO = 1, - /** Ratio of simplified hull area to input area */ - GEOSHULL_PARAM_AREA_RATIO = 2 -}; - /** * Computes a topology-preserving simplified hull of a polygonal geometry, * with hull shape determined by the parameter, controlled by a parameter @@ -5521,17 +5532,6 @@ extern GEOSGeometry GEOS_DLL * GEOSDelaunayTriangulation( extern GEOSGeometry GEOS_DLL * GEOSConstrainedDelaunayTriangulation( const GEOSGeometry *g); -/** Change behaviour of \ref GEOSVoronoiDiagram */ -enum GEOSVoronoiFlags -{ - /** Return only edges of the Voronoi cells, as a MultiLineString **/ - GEOS_VORONOI_ONLY_EDGES = 1, - /** Preserve order of inputs, such that the nth cell in the result corresponds - * to the nth vertex in the input. If this cannot be done, such as for inputs - * that contain repeated points, \ref GEOSVoronoiDiagram will return NULL. **/ - GEOS_VORONOI_PRESERVE_ORDER = 2 -}; - /** * Returns the 2D Voronoi polygons or edges computed from the vertices * of the given geometry. ----------------------------------------------------------------------- Summary of changes: capi/geos_c.h.in | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) hooks/post-receive -- GEOS From git at osgeo.org Wed Jul 8 04:30:09 2026 From: git at osgeo.org (git at osgeo.org) Date: Wed, 8 Jul 2026 04:30:09 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. 4f2442b078b7e26df23ced6193dc62f11090f674 Message-ID: <20260708113010.01FB21A3120@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via 4f2442b078b7e26df23ced6193dc62f11090f674 (commit) from 678482bd85082ab0c671a100373a1d9c46764ffe (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 4f2442b078b7e26df23ced6193dc62f11090f674 Author: Daniel Baston Date: Wed Jul 8 07:29:46 2026 -0400 GEOSPreparedDistance: fallback to brute-force distance for curved inputs (#1467) * FacetSequenceTreeBuilder: guard against curved components * GEOSPreparedDistance: fallback to brute-force distance for curved inputs diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp index 32254f9e3..21202a2b0 100644 --- a/capi/geos_ts_c.cpp +++ b/capi/geos_ts_c.cpp @@ -4587,6 +4587,10 @@ extern "C" { const PreparedGeometry* pg, const Geometry* g) { return execute(extHandle, [&]() -> geos::geom::CoordinateSequence* { + if (g->hasCurvedComponents()) { + geos::operation::distance::DistanceOp dop(pg->getGeometry(), *g); + return dop.nearestPoints().release(); + } return pg->nearestPoints(g).release(); }); } @@ -4597,7 +4601,11 @@ extern "C" { const Geometry* g, double* dist) { return execute(extHandle, 0, [&]() { - *dist = pg->distance(g); + if (g->hasCurvedComponents()) { + *dist = pg->getGeometry().distance(g); + } else { + *dist = pg->distance(g); + } return 1; }); } @@ -4608,10 +4616,14 @@ extern "C" { const Geometry* g, double dist) { return execute(extHandle, 2, [&]() { + if (g->hasCurvedComponents()) { + return pg->getGeometry().isWithinDistance(g, dist); + } return pg->isWithinDistance(g, dist); }); } + //----------------------------------------------------------------- // STRtree //----------------------------------------------------------------- diff --git a/src/operation/distance/FacetSequenceTreeBuilder.cpp b/src/operation/distance/FacetSequenceTreeBuilder.cpp index ec10c8c57..9727abe19 100644 --- a/src/operation/distance/FacetSequenceTreeBuilder.cpp +++ b/src/operation/distance/FacetSequenceTreeBuilder.cpp @@ -30,6 +30,8 @@ namespace distance { std::unique_ptr> FacetSequenceTreeBuilder::build(const Geometry* g) { + util::ensureNoCurvedComponents(g); + std::unique_ptr> tree(new FacetSequenceTree(computeFacetSequences(g))); tree->build(); diff --git a/tests/unit/capi/GEOSPreparedDistanceTest.cpp b/tests/unit/capi/GEOSPreparedDistanceTest.cpp index 5ea53f1e0..758d18432 100644 --- a/tests/unit/capi/GEOSPreparedDistanceTest.cpp +++ b/tests/unit/capi/GEOSPreparedDistanceTest.cpp @@ -213,5 +213,18 @@ void object::test<14> ); } +template<> +template<> +void object::test<15>() +{ + set_test_name("curved second geometry argument"); + + checkDistance( + "LINESTRING (0 0, 0 5)", + "CIRCULARSTRING (-5 0, 0 -5, 5 0)", + 5 + ); +} + } // namespace tut diff --git a/tests/unit/capi/GEOSPreparedDistanceWithinTest.cpp b/tests/unit/capi/GEOSPreparedDistanceWithinTest.cpp index 7be2a2d2b..f29c54fb1 100644 --- a/tests/unit/capi/GEOSPreparedDistanceWithinTest.cpp +++ b/tests/unit/capi/GEOSPreparedDistanceWithinTest.cpp @@ -280,5 +280,19 @@ void object::test<16>() ); } +template<> +template<> +void object::test<17>() +{ + set_test_name("curved second geometry argument"); + + checkDistanceWithin( + "LINESTRING (0 0, 0 5)", + "CIRCULARSTRING (-5 0, 0 -5, 5 0)", + 5, + 1 + ); +} + } // namespace tut diff --git a/tests/unit/capi/GEOSPreparedNearestPointsTest.cpp b/tests/unit/capi/GEOSPreparedNearestPointsTest.cpp index effa0628b..800266ee1 100644 --- a/tests/unit/capi/GEOSPreparedNearestPointsTest.cpp +++ b/tests/unit/capi/GEOSPreparedNearestPointsTest.cpp @@ -153,5 +153,19 @@ void object::test<6> ); } +template<> +template<> +void object::test<7>() +{ + set_test_name("curved second geometry argument"); + + checkNearestPoints( + "LINESTRING (3 0, 4 0)", + "CIRCULARSTRING (0 0, 1 1, 2 0)", + 3, 0, 2, 0 + ); +} + + } // namespace tut ----------------------------------------------------------------------- Summary of changes: capi/geos_ts_c.cpp | 14 +++++++++++++- src/operation/distance/FacetSequenceTreeBuilder.cpp | 2 ++ tests/unit/capi/GEOSPreparedDistanceTest.cpp | 13 +++++++++++++ tests/unit/capi/GEOSPreparedDistanceWithinTest.cpp | 14 ++++++++++++++ tests/unit/capi/GEOSPreparedNearestPointsTest.cpp | 14 ++++++++++++++ 5 files changed, 56 insertions(+), 1 deletion(-) hooks/post-receive -- GEOS From git at osgeo.org Wed Jul 8 04:30:41 2026 From: git at osgeo.org (git at osgeo.org) Date: Wed, 8 Jul 2026 04:30:41 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. b4a4ace48b2c3fe6ac4d9a0219d6b232158ca931 Message-ID: <20260708113041.7607C1A25DA@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via b4a4ace48b2c3fe6ac4d9a0219d6b232158ca931 (commit) from 4f2442b078b7e26df23ced6193dc62f11090f674 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit b4a4ace48b2c3fe6ac4d9a0219d6b232158ca931 Author: Daniel Baston Date: Wed Jul 8 07:30:23 2026 -0400 Prepared geometries: linearize test geom if needed (#1469) diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp index 21202a2b0..91ac5898e 100644 --- a/capi/geos_ts_c.cpp +++ b/capi/geos_ts_c.cpp @@ -4461,7 +4461,7 @@ extern "C" { const PreparedGeometry* pg, const Geometry* g) { return execute(extHandle, 2, [&]() { - return pg->contains(g); + return pg->contains(convertToLineIfNeeded(extHandle, g)); }); } @@ -4479,7 +4479,7 @@ extern "C" { const PreparedGeometry* pg, const Geometry* g) { return execute(extHandle, 2, [&]() { - return pg->containsProperly(g); + return pg->containsProperly(convertToLineIfNeeded(extHandle, g)); }); } @@ -4488,7 +4488,7 @@ extern "C" { const PreparedGeometry* pg, const Geometry* g) { return execute(extHandle, 2, [&]() { - return pg->coveredBy(g); + return pg->coveredBy(convertToLineIfNeeded(extHandle, g)); }); } @@ -4497,7 +4497,7 @@ extern "C" { const PreparedGeometry* pg, const Geometry* g) { return execute(extHandle, 2, [&]() { - return pg->covers(g); + return pg->covers(convertToLineIfNeeded(extHandle, g)); }); } @@ -4506,7 +4506,7 @@ extern "C" { const PreparedGeometry* pg, const Geometry* g) { return execute(extHandle, 2, [&]() { - return pg->crosses(g); + return pg->crosses(convertToLineIfNeeded(extHandle, g)); }); } @@ -4515,7 +4515,7 @@ extern "C" { const PreparedGeometry* pg, const Geometry* g) { return execute(extHandle, 2, [&]() { - return pg->disjoint(g); + return pg->disjoint(convertToLineIfNeeded(extHandle, g)); }); } @@ -4524,7 +4524,7 @@ extern "C" { const PreparedGeometry* pg, const Geometry* g) { return execute(extHandle, 2, [&]() { - return pg->intersects(g); + return pg->intersects(convertToLineIfNeeded(extHandle, g)); }); } @@ -4542,7 +4542,7 @@ extern "C" { const PreparedGeometry* pg, const Geometry* g) { return execute(extHandle, 2, [&]() { - return pg->overlaps(g); + return pg->overlaps(convertToLineIfNeeded(extHandle, g)); }); } @@ -4551,7 +4551,7 @@ extern "C" { const PreparedGeometry* pg, const Geometry* g) { return execute(extHandle, 2, [&]() { - return pg->touches(g); + return pg->touches(convertToLineIfNeeded(extHandle, g)); }); } @@ -4560,7 +4560,7 @@ extern "C" { const PreparedGeometry* pg, const Geometry* g) { return execute(extHandle, 2, [&]() { - return pg->within(g); + return pg->within(convertToLineIfNeeded(extHandle, g)); }); } @@ -4569,7 +4569,7 @@ extern "C" { const PreparedGeometry* pg, const Geometry* g) { return execute(extHandle, [&]() -> char * { - return gstrdup(pg->relate(g)->toString()); + return gstrdup(pg->relate(convertToLineIfNeeded(extHandle, g))->toString()); }); } @@ -4578,7 +4578,7 @@ extern "C" { const PreparedGeometry* pg, const Geometry* g, const char* imPattern) { return execute(extHandle, 2, [&]() { - return pg->relate(g, std::string(imPattern)); + return pg->relate(convertToLineIfNeeded(extHandle, g), std::string(imPattern)); }); } diff --git a/src/geom/prep/AbstractPreparedPolygonContains.cpp b/src/geom/prep/AbstractPreparedPolygonContains.cpp index f0dbdeacc..c977c94e6 100644 --- a/src/geom/prep/AbstractPreparedPolygonContains.cpp +++ b/src/geom/prep/AbstractPreparedPolygonContains.cpp @@ -106,6 +106,8 @@ AbstractPreparedPolygonContains::findAndClassifyIntersections(const geom::Geomet bool AbstractPreparedPolygonContains::eval(const geom::Geometry* geom) { + geos::util::ensureNoCurvedComponents(geom); + // Do point-in-poly tests first, since they are cheaper and may result // in a quick negative result. auto outermostLoc = getOutermostTestComponentLocation(geom); diff --git a/src/geom/prep/PreparedPolygonContainsProperly.cpp b/src/geom/prep/PreparedPolygonContainsProperly.cpp index 403af5c33..de663576e 100644 --- a/src/geom/prep/PreparedPolygonContainsProperly.cpp +++ b/src/geom/prep/PreparedPolygonContainsProperly.cpp @@ -27,6 +27,7 @@ #include #include #include +#include namespace geos { namespace geom { // geos.geom @@ -45,6 +46,8 @@ namespace prep { // geos.geom.prep bool PreparedPolygonContainsProperly::containsProperly(const geom::Geometry* geom) { + geos::util::ensureNoCurvedComponents(geom); + // Do point-in-poly tests first, since they are cheaper and may result // in a quick negative result. // If a point of any test components does not lie in target, diff --git a/tests/unit/capi/GEOSPreparedGeometryTest.cpp b/tests/unit/capi/GEOSPreparedGeometryTest.cpp index 28e24b187..5b74ceed4 100644 --- a/tests/unit/capi/GEOSPreparedGeometryTest.cpp +++ b/tests/unit/capi/GEOSPreparedGeometryTest.cpp @@ -482,5 +482,91 @@ void object::test<17>() GEOSFree(r2); } +template<> +template<> +void object::test<18>() +{ + set_test_name("Polygon first argument, CurvePolygon second argument"); + useContext(); + + geom1_ = fromWKT("POLYGON ((0 0, 2 0, 2 8, 8 8, 8 0, 10 0, 10 10, 0 10, 0 0))"); + geom2_ = fromWKT("CURVEPOLYGON (COMPOUNDCURVE(CIRCULARSTRING (5 1, 6 2, 7 1), (7 1, 5 1)))"); + geom3_ = fromWKT("CURVEPOLYGON (COMPOUNDCURVE((-100 100, -100 -100, 100 -100, 100 100), CIRCULARSTRING (100 100, 0 200, -100 100)))"); + + prepGeom1_ = GEOSPrepare_r(ctxt_, geom1_); + + // No curve-to-line params; all functions return 2 (error) + ensure_equals("contains", GEOSPreparedContains_r(ctxt_, prepGeom1_, geom2_), 2); + ensure_equals("contains properly", GEOSPreparedContainsProperly_r(ctxt_, prepGeom1_, geom2_), 2); + ensure_equals("crosses", GEOSPreparedCrosses_r(ctxt_, prepGeom1_, geom2_), 2); + ensure_equals("covered by", GEOSPreparedCoveredBy_r(ctxt_, prepGeom1_, geom3_), 2); + ensure_equals("touches", GEOSPreparedTouches_r(ctxt_, prepGeom1_, geom2_), 2); + ensure_equals("overlaps", GEOSPreparedOverlaps_r(ctxt_, prepGeom1_, geom2_), 2); + ensure_equals("intersects", GEOSPreparedIntersects_r(ctxt_, prepGeom1_, geom2_), 2); + ensure_equals("disjoint", GEOSPreparedDisjoint_r(ctxt_, prepGeom1_, geom2_), 2); + ensure_equals("relate pattern", GEOSPreparedRelatePattern_r(ctxt_, prepGeom1_, geom2_, "T********"), 2); + ensure_equals("within", GEOSPreparedWithin_r(ctxt_, prepGeom1_, geom3_), 2); + ensure(GEOSPreparedRelate_r(ctxt_, prepGeom1_, geom2_) == nullptr); + + GEOSContext_setCurveToLineParams_r(ctxt_, curveToLineParams_); + + ensure_equals("contains", GEOSPreparedContains_r(ctxt_, prepGeom1_, geom2_), 0); + ensure_equals("contains properly", GEOSPreparedContainsProperly_r(ctxt_, prepGeom1_, geom2_), 0); + ensure_equals("crosses", GEOSPreparedCrosses_r(ctxt_, prepGeom1_, geom2_), 0); + ensure_equals("covered by", GEOSPreparedCoveredBy_r(ctxt_, prepGeom1_, geom3_), 1); + ensure_equals("touches", GEOSPreparedTouches_r(ctxt_, prepGeom1_, geom2_), 0); + ensure_equals("overlaps", GEOSPreparedOverlaps_r(ctxt_, prepGeom1_, geom2_), 0); + ensure_equals("intersects", GEOSPreparedIntersects_r(ctxt_, prepGeom1_, geom2_), 0); + ensure_equals("disjoint", GEOSPreparedDisjoint_r(ctxt_, prepGeom1_, geom2_), 1); + ensure_equals("relate pattern", GEOSPreparedRelatePattern_r(ctxt_, prepGeom1_, geom2_, "T********"), 0); + ensure_equals("within", GEOSPreparedWithin_r(ctxt_, prepGeom1_, geom3_), 1); + + str_ = GEOSPreparedRelate_r(ctxt_, prepGeom1_, geom2_); + ensure(str_); +} + +template<> +template<> +void object::test<19>() +{ + set_test_name("LineString first argument, CircularString second argument"); + useContext(); + + geom1_ = fromWKT("LINESTRING (-10 -10, 10 10)"); + geom2_ = fromWKT("CIRCULARSTRING (-5 0, 0 5, 5 0)"); + geom3_ = fromWKT("CIRCULARSTRING (-100 -100, 0 100, 100 0)"); + + prepGeom1_ = GEOSPrepare_r(ctxt_, geom1_); + + // No curve-to-line params; all functions return 2 (error) + ensure_equals("contains", GEOSPreparedContains_r(ctxt_, prepGeom1_, geom2_), 2); + ensure_equals("contains properly", GEOSPreparedContainsProperly_r(ctxt_, prepGeom1_, geom2_), 2); + ensure_equals("crosses", GEOSPreparedCrosses_r(ctxt_, prepGeom1_, geom2_), 2); + ensure_equals("covered by", GEOSPreparedCoveredBy_r(ctxt_, prepGeom1_, geom3_), 2); + ensure_equals("touches", GEOSPreparedTouches_r(ctxt_, prepGeom1_, geom2_), 2); + ensure_equals("overlaps", GEOSPreparedOverlaps_r(ctxt_, prepGeom1_, geom2_), 2); + ensure_equals("intersects", GEOSPreparedIntersects_r(ctxt_, prepGeom1_, geom2_), 2); + ensure_equals("disjoint", GEOSPreparedDisjoint_r(ctxt_, prepGeom1_, geom2_), 2); + ensure_equals("relate pattern", GEOSPreparedRelatePattern_r(ctxt_, prepGeom1_, geom2_, "T********"), 2); + ensure_equals("within", GEOSPreparedWithin_r(ctxt_, prepGeom1_, geom3_), 2); + ensure(GEOSPreparedRelate_r(ctxt_, prepGeom1_, geom2_) == nullptr); + + GEOSContext_setCurveToLineParams_r(ctxt_, curveToLineParams_); + + ensure_equals("contains", GEOSPreparedContains_r(ctxt_, prepGeom1_, geom2_), 0); + ensure_equals("contains properly", GEOSPreparedContainsProperly_r(ctxt_, prepGeom1_, geom2_), 0); + ensure_equals("crosses", GEOSPreparedCrosses_r(ctxt_, prepGeom1_, geom2_), 1); + ensure_equals("covered by", GEOSPreparedCoveredBy_r(ctxt_, prepGeom1_, geom3_), 0); + ensure_equals("touches", GEOSPreparedTouches_r(ctxt_, prepGeom1_, geom2_), 0); + ensure_equals("overlaps", GEOSPreparedOverlaps_r(ctxt_, prepGeom1_, geom2_), 0); + ensure_equals("intersects", GEOSPreparedIntersects_r(ctxt_, prepGeom1_, geom2_), 1); + ensure_equals("disjoint", GEOSPreparedDisjoint_r(ctxt_, prepGeom1_, geom2_), 0); + ensure_equals("relate pattern", GEOSPreparedRelatePattern_r(ctxt_, prepGeom1_, geom2_, "T********"), 1); + ensure_equals("within", GEOSPreparedWithin_r(ctxt_, prepGeom1_, geom3_), 0); + + str_ = GEOSPreparedRelate_r(ctxt_, prepGeom1_, geom2_); + ensure(str_); +} + } // namespace tut ----------------------------------------------------------------------- Summary of changes: capi/geos_ts_c.cpp | 24 +++---- src/geom/prep/AbstractPreparedPolygonContains.cpp | 2 + src/geom/prep/PreparedPolygonContainsProperly.cpp | 3 + tests/unit/capi/GEOSPreparedGeometryTest.cpp | 86 +++++++++++++++++++++++ 4 files changed, 103 insertions(+), 12 deletions(-) hooks/post-receive -- GEOS From git at osgeo.org Wed Jul 8 17:05:59 2026 From: git at osgeo.org (git at osgeo.org) Date: Wed, 8 Jul 2026 17:05:59 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. 96292c0df9587f6a6cb9d0b00e52772db2672348 Message-ID: <20260709000559.D39441B029B@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via 96292c0df9587f6a6cb9d0b00e52772db2672348 (commit) from b4a4ace48b2c3fe6ac4d9a0219d6b232158ca931 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 96292c0df9587f6a6cb9d0b00e52772db2672348 Author: Daniel Baston Date: Wed Jul 8 20:05:36 2026 -0400 C API: Do not linearize inputs to overlay functions (#1471) diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp index 91ac5898e..f95c294df 100644 --- a/capi/geos_ts_c.cpp +++ b/capi/geos_ts_c.cpp @@ -1636,8 +1636,10 @@ extern "C" { Geometry* GEOSIntersection_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) { - return convertCurvesAndExecute(extHandle, g1, g2, [](const Geometry* geom1, const Geometry* geom2) { - return geom1->intersection(geom2); + return execute(extHandle, [&]() { + auto g3 = g1->intersection(g2); + g3->setSRID(g1->getSRID()); + return g3.release(); }); } @@ -2028,8 +2030,10 @@ extern "C" { Geometry* GEOSUnion_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) { - return convertCurvesAndExecute(extHandle, g1, g2, [&](const Geometry* input1, const Geometry* input2) { - return input1->Union(input2); + return execute(extHandle, [&]() { + auto g3 = g1->Union(g2); + g3->setSRID(g1->getSRID()); + return g3.release(); }); } diff --git a/tests/unit/capi/GEOSDifferenceTest.cpp b/tests/unit/capi/GEOSDifferenceTest.cpp index c5f70196b..259376fc0 100644 --- a/tests/unit/capi/GEOSDifferenceTest.cpp +++ b/tests/unit/capi/GEOSDifferenceTest.cpp @@ -104,5 +104,23 @@ void object::test<4>() ensure_equals(fracs[0], 1.0); } +template<> +template<> +void object::test<5>() +{ + set_test_name("curved input is not linearized when CurveToLineParams registered with a context"); + useContext(); + + geom1_ = fromWKT("CIRCULARSTRING (0 0, 1 1, 2 0)"); + geom2_ = fromWKT("POINT (2 0)"); + + GEOSCurveToLineParams_setMaxStepDegrees_r(ctxt_, curveToLineParams_, 45); + GEOSContext_setCurveToLineParams_r(ctxt_, curveToLineParams_); + + result_ = GEOSDifference_r(ctxt_, geom1_, geom2_); + + ensure_geometry_equals(result_, geom1_, 1e-8); +} + } // namespace tut diff --git a/tests/unit/capi/GEOSIntersectionTest.cpp b/tests/unit/capi/GEOSIntersectionTest.cpp index a4aabb439..8f4d67467 100644 --- a/tests/unit/capi/GEOSIntersectionTest.cpp +++ b/tests/unit/capi/GEOSIntersectionTest.cpp @@ -177,5 +177,23 @@ void object::test<9>() ensure("HasM", GEOSHasM(result_)); } +template<> +template<> +void object::test<10>() +{ + set_test_name("curved input is not linearized when CurveToLineParams registered with a context"); + useContext(); + + geom1_ = fromWKT("CIRCULARSTRING (0 0, 1 1, 2 0)"); + geom2_ = fromWKT("POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))"); + + GEOSCurveToLineParams_setMaxStepDegrees_r(ctxt_, curveToLineParams_, 45); + GEOSContext_setCurveToLineParams_r(ctxt_, curveToLineParams_); + + result_ = GEOSIntersection_r(ctxt_, geom1_, geom2_); + + ensure_geometry_equals(result_, geom1_, 1e-8); +} + } // namespace tut diff --git a/tests/unit/capi/GEOSSymDifferenceTest.cpp b/tests/unit/capi/GEOSSymDifferenceTest.cpp index 63436e135..61d27eb9b 100644 --- a/tests/unit/capi/GEOSSymDifferenceTest.cpp +++ b/tests/unit/capi/GEOSSymDifferenceTest.cpp @@ -45,5 +45,23 @@ void object::test<2>() ensure_geometry_equals(result_, expected_, 1e-8); } +template<> +template<> +void object::test<3>() +{ + set_test_name("curved input is not linearized when CurveToLineParams registered with a context"); + useContext(); + + geom1_ = fromWKT("CIRCULARSTRING (0 0, 1 1, 2 0)"); + geom2_ = fromWKT("POINT (2 0)"); + + GEOSCurveToLineParams_setMaxStepDegrees_r(ctxt_, curveToLineParams_, 45); + GEOSContext_setCurveToLineParams_r(ctxt_, curveToLineParams_); + + result_ = GEOSSymDifference_r(ctxt_, geom1_, geom2_); + + ensure_geometry_equals(result_, geom1_, 1e-8); +} + } // namespace tut diff --git a/tests/unit/capi/GEOSUnionTest.cpp b/tests/unit/capi/GEOSUnionTest.cpp index 835aaaf0f..2c0567dab 100644 --- a/tests/unit/capi/GEOSUnionTest.cpp +++ b/tests/unit/capi/GEOSUnionTest.cpp @@ -79,5 +79,24 @@ void object::test<3>() ensure_geometry_equals(result_, expected_, 1e-8); } +template<> +template<> +void object::test<10>() +{ + set_test_name("curved input is not linearized when CurveToLineParams registered with a context"); + useContext(); + + geom1_ = fromWKT("CIRCULARSTRING (0 0, 1 1, 2 0)"); + geom2_ = fromWKT("POINT (2 0)"); + + GEOSCurveToLineParams_setMaxStepDegrees_r(ctxt_, curveToLineParams_, 45); + GEOSContext_setCurveToLineParams_r(ctxt_, curveToLineParams_); + + result_ = GEOSUnion_r(ctxt_, geom1_, geom2_); + + ensure_geometry_equals(result_, geom1_, 1e-8); +} + + } // namespace tut ----------------------------------------------------------------------- Summary of changes: capi/geos_ts_c.cpp | 12 ++++++++---- tests/unit/capi/GEOSDifferenceTest.cpp | 18 ++++++++++++++++++ tests/unit/capi/GEOSIntersectionTest.cpp | 18 ++++++++++++++++++ tests/unit/capi/GEOSSymDifferenceTest.cpp | 18 ++++++++++++++++++ tests/unit/capi/GEOSUnionTest.cpp | 19 +++++++++++++++++++ 5 files changed, 81 insertions(+), 4 deletions(-) hooks/post-receive -- GEOS From git at osgeo.org Thu Jul 9 10:46:38 2026 From: git at osgeo.org (git at osgeo.org) Date: Thu, 9 Jul 2026 10:46:38 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. 7dfd43b6b5ab718abfc4ebee79834b27eb4bd9b1 Message-ID: <20260709174638.C0C3E1C209E@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via 7dfd43b6b5ab718abfc4ebee79834b27eb4bd9b1 (commit) via 31d5c40bb349e6c8528da368b9fd058b0670aa22 (commit) from 96292c0df9587f6a6cb9d0b00e52772db2672348 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 7dfd43b6b5ab718abfc4ebee79834b27eb4bd9b1 Author: Paul Ramsey Date: Thu Jul 9 10:46:12 2026 -0700 Put breaking changes up front in news diff --git a/NEWS.md b/NEWS.md index c2872e8dc..1765d1eec 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,12 @@ ## Changes in 3.15.0 2026-xx-xx +- Breaking Changes: + - BufferOp returns POLYGON EMPTY when fed Inf/Nan coords (GH-1332) + - Return Inf when calculating distance to an empty geometry (GH-1345, Even Rouault) + - Overlay operations now produce a LineString geometry in cases that would previously + produce a MultiLineString with contiguous sub-geometries. (GH-1459, Dan Baston) + - New things: - Add GEOSMinimumSpanningTree (Paul Ramsey) - Add GEOSCoverageEdges (Paul Ramsey) @@ -20,13 +26,6 @@ - Add GeometrySplitter (GH-1424, Dan Baston) - Add progress reporting to GEOSCoverageSimplify, GEOSUnaryUnion (GH-1466, Even Rouault / Dan Baston) -- Breaking Changes: - - BufferOp returns POLYGON EMPTY when fed Inf/Nan coords (GH-1332) - - Return Inf when calculating distance to an empty geometry (GH-1345, Even Rouault) - - Overlay operations now produce a LineString geometry in cases that would previously - produce a MultiLineString with contiguous sub-geometries. (GH-1459, Dan Baston) - - - Fixes/Improvements: - Buffer of Linestring includes spurious hole (GH-1217, Moritz Kirmse) - Preserve M values in GEOSInterpolate (GH-1390, Dan Baston) @@ -42,6 +41,7 @@ - Overlay performance improvements (GH-1353, arriopolis, Martin Davis) - Fix unintended ring rotation in Overlay results (GH-1412, Dan Baston) + ## Changes in 3.14.0 2025-08-21 commit 31d5c40bb349e6c8528da368b9fd058b0670aa22 Author: Paul Ramsey Date: Thu Jul 9 10:45:05 2026 -0700 Document output type letters for geosop Adds documentation on output letters and makes them consistent with what they actually do. Closes #1465 diff --git a/util/geosop/GeometryOp.cpp b/util/geosop/GeometryOp.cpp index 7ef4c4613..df913da38 100644 --- a/util/geosop/GeometryOp.cpp +++ b/util/geosop/GeometryOp.cpp @@ -183,6 +183,7 @@ std::vector opRegistry { {"lineMerge", [](std::string name) { return GeometryOp::create(name, catGeom, "merge the lines of geometry", + Result::typeGeomList, [](const Geometry& geom) { geos::operation::linemerge::LineMerger lmrgr; lmrgr.add(&geom); @@ -554,6 +555,7 @@ std::vector opRegistry { {"polygonize", [](std::string name) { return GeometryOp::create(name, catConst, "polygonize lines", + Result::typeGeomList, [](const Geometry& geom) { geos::operation::polygonize::Polygonizer p; p.add(&geom); @@ -569,6 +571,7 @@ std::vector opRegistry { {"polygonizeValid", [](std::string name) { return GeometryOp::create(name, catConst, "polygonize lines into a valid polygonal geometry", + Result::typeGeomList, [](const Geometry& geom) { geos::operation::polygonize::Polygonizer p(true); p.add(&geom); @@ -992,6 +995,7 @@ std::vector opRegistry { {"coverageClean", [](std::string name) { return GeometryOp::createAgg(name, catCoverage, "cleans a coverage, merging gaps narrower than a given width", + Result::typeGeomList, [](const Geometry& geom, double gapWidth) { std::vector coverage = toList(geom); std::vector> result @@ -1006,6 +1010,7 @@ std::vector opRegistry { }}, {"coverageSimplify", [](std::string name) { return GeometryOp::createAgg(name, catCoverage, "simplify a polygonal coverage by a distance tolerance", + Result::typeGeomList, [](const Geometry& geom, double d) { std::vector coverage = toList(geom); std::vector> result @@ -1032,6 +1037,7 @@ std::vector opRegistry { }}, {"coverageValidate", [](std::string name) { return GeometryOp::createAgg(name, catCoverage, "validate a polygonal coverage", + Result::typeGeomList, [](const Geometry& geom) { std::vector coverage = toList(geom); std::vector> invalidList diff --git a/util/geosop/GeometryOp.h b/util/geosop/GeometryOp.h index 628e2c821..320d2d893 100644 --- a/util/geosop/GeometryOp.h +++ b/util/geosop/GeometryOp.h @@ -151,6 +151,14 @@ public: return new GeometryOp(name, cat, desc, true, Result::typeGeometry, fun); } + static GeometryOp* createAgg(std::string name, + std::string cat, + std::string desc, + int resType, + geomFunSig_G fun) { + return new GeometryOp(name, cat, desc, true, resType, fun); + } + static GeometryOp* createAgg(std::string name, std::string cat, std::string desc, @@ -158,6 +166,14 @@ public: return new GeometryOp(name, cat, desc, true, Result::typeGeometry, fun); } + static GeometryOp* createAgg(std::string name, + std::string cat, + std::string desc, + int resType, + geomFunSig_GD fun) { + return new GeometryOp(name, cat, desc, true, resType, fun); + } + static GeometryOp* find(std::string name); static std::vector listOps(); diff --git a/util/geosop/GeosOp.cpp b/util/geosop/GeosOp.cpp index 3e91c1d3b..398085519 100644 --- a/util/geosop/GeosOp.cpp +++ b/util/geosop/GeosOp.cpp @@ -125,6 +125,12 @@ int main(int argc, char** argv) { std::cout << "- to use negative numeric op arguments use '--' before the op name" << std::endl; std::cout << "- Negative numeric op arguments can be specified with leading N: e.g. N-0.1" << std::endl; std::cout << std::endl; + std::cout << "Operation signature legend:" << std::endl; + std::cout << " Inputs: A - geometry A B - geometry B N - numeric argument" << std::endl; + std::cout << " Result: G - geometry [G] - geometry list B - boolean" << std::endl; + std::cout << " D - double I - integer S - string" << std::endl; + std::cout << " Flags: AGG - operates on collected (aggregated) geometry" << std::endl; + std::cout << std::endl; std::cout << "Operations:" << std::endl; std::vector ops = GeometryOp::listOps(); for (auto opName : ops) { ----------------------------------------------------------------------- Summary of changes: NEWS.md | 14 +++++++------- util/geosop/GeometryOp.cpp | 6 ++++++ util/geosop/GeometryOp.h | 16 ++++++++++++++++ util/geosop/GeosOp.cpp | 6 ++++++ 4 files changed, 35 insertions(+), 7 deletions(-) hooks/post-receive -- GEOS From git at osgeo.org Thu Jul 9 11:12:51 2026 From: git at osgeo.org (git at osgeo.org) Date: Thu, 9 Jul 2026 11:12:51 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. b50bb516081b9c3193fbc8001c08493d275f8fa2 Message-ID: <20260709181251.AD90D1C1D6F@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via b50bb516081b9c3193fbc8001c08493d275f8fa2 (commit) from 7dfd43b6b5ab718abfc4ebee79834b27eb4bd9b1 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit b50bb516081b9c3193fbc8001c08493d275f8fa2 Author: Paul Ramsey Date: Thu Jul 9 11:12:05 2026 -0700 Add in Curve geometry info to WKB section Closes #1472 diff --git a/web/content/specifications/wkb.md b/web/content/specifications/wkb.md index 048f96794..57c5ee024 100644 --- a/web/content/specifications/wkb.md +++ b/web/content/specifications/wkb.md @@ -85,18 +85,23 @@ with each ordinate value set to an IEEE-754 quiet NaN value GEOS only supports the seven original simple features geometry types. ``` -enum wkbGeometryType { +enum wkbType { wkbPoint = 1, wkbLineString = 2, wkbPolygon = 3, wkbMultiPoint = 4, wkbMultiLineString = 5, wkbMultiPolygon = 6, - wkbGeometryCollection = 7 + wkbGeometryCollection = 7, + wkbCircularString = 8, + wkbCompoundCurve = 9, + wkbCurvePolygon = 10, + wkbMultiCurve = 11, + wkbMultiSurface = 12 }; ``` -Other systems (eg PostGIS) support a wider range of types (for example, CircularString, CurvePolygon), and hence more geometry type numbers, but GEOS is currently unable to consume those geometries. +Other systems (eg PostGIS) support a wider range of types (for example, PolyhedralSurface, TIN), and hence even more geometry type numbers, but GEOS is cannot consume those geometries. ``` WKBPoint { @@ -133,7 +138,7 @@ WKBMultiLineString { WKBLineString WKBLineStrings[numWkbLineStrings]; } -wkbMultiPolygon { +WKBMultiPolygon { byte byteOrder; uint32 wkbType; // 6 uint32 numWkbPolygons; @@ -158,8 +163,59 @@ WKBGeometryCollection { uint32 numWkbGeometries; WKBGeometry wkbGeometries[numWkbGeometries]; } + +WKBCurve { + union { + WKBLineString linestring; + WKBCircularString circstring; + WKBCompoundCurve compound; + } +} + +WKBCircularString { + byte byteOrder; + uint32 wkbType; // 8 + uint32 numPoints; + Point points[numPoints]; +} + +WKBCompoundCurve { + byte byteOrder; + uint32 wkbType; // 9 + uint32 numCurves; + WKBCurve curves[numCurves]; // start/end points must match +} + +WKBCurvePolygon { + byte byteOrder; + uint32 wkbType; // 10 + uint32 numRings; + WKBCurve rings[numRings]; // rings must be closed +} + +WKBSurface { + union { + WKBPolygon polygon; + WKBCurvePolygon curvepoly; + } +} + +WKBMultiCurve { + byte byteOrder; + uint32 wkbType; // 11 + uint32 numCurves; + WKBCurve curves[numCurves]; +} + +WKBMultiSurface { + byte byteOrder; + uint32 wkbType; // 12 + uint32 numSurfaces; + WKBSurface surfaces[numSurfaces]; +} ``` + ### Example The following bytes (in hex) make up the WKB for a `LINESTRING(0 0, 1 1, 2 1)`: ----------------------------------------------------------------------- Summary of changes: web/content/specifications/wkb.md | 64 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 4 deletions(-) hooks/post-receive -- GEOS From git at osgeo.org Thu Jul 9 11:18:48 2026 From: git at osgeo.org (git at osgeo.org) Date: Thu, 9 Jul 2026 11:18:48 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. 4fb859518f4642a0ac8b0f967d1dd54d2a2a7f02 Message-ID: <20260709181848.B09C71C1CF6@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via 4fb859518f4642a0ac8b0f967d1dd54d2a2a7f02 (commit) from b50bb516081b9c3193fbc8001c08493d275f8fa2 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 4fb859518f4642a0ac8b0f967d1dd54d2a2a7f02 Author: Daniel Baston Date: Thu Jul 9 14:18:30 2026 -0400 UnaryUnionOp: support curved geometries (#1468) diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp index f95c294df..ee6391ca2 100644 --- a/capi/geos_ts_c.cpp +++ b/capi/geos_ts_c.cpp @@ -2077,9 +2077,10 @@ extern "C" { Geometry* GEOSUnaryUnion_r(GEOSContextHandle_t extHandle, const Geometry* g) { - return convertCurvesAndExecute(extHandle, g, [&](const Geometry* input1) { - return geos::operation::geounion::UnaryUnionOp::Union(*input1, &extHandle->progressFunction); - return input1->Union(); + return execute(extHandle, [&]() { + auto g3 = geos::operation::geounion::UnaryUnionOp::Union(*g, &extHandle->progressFunction); + g3->setSRID(g->getSRID()); + return g3.release(); }); } diff --git a/include/geos/operation/union/CascadedPolygonUnion.h b/include/geos/operation/union/CascadedPolygonUnion.h index fcc5ba9a7..710a56ac4 100644 --- a/include/geos/operation/union/CascadedPolygonUnion.h +++ b/include/geos/operation/union/CascadedPolygonUnion.h @@ -32,7 +32,7 @@ namespace geos { namespace geom { class GeometryFactory; class Geometry; -class Polygon; +class Surface; class MultiPolygon; class Envelope; } @@ -103,7 +103,7 @@ private: */ class GEOS_DLL CascadedPolygonUnion { private: - std::vector* inputPolys; + const std::vector& inputPolys; geom::GeometryFactory const* geomFactory; /** @@ -116,23 +116,23 @@ private: static int const STRTREE_NODE_CAPACITY = 4; /** \brief - * Computes a [Geometry](@ref geom::Geometry) containing only polygonal components. + * Computes a [Geometry](@ref geom::Geometry) containing only + * surface components (dimension == 2). * - * Extracts the [Polygons](@ref geom::Polygon) from the input + * Extracts the [Surfaces](@ref geom::Surface) from the input * and returns them as an appropriate polygonal geometry. * - * If the input is already `Polygonal`, it is returned unchanged. + * If the input is already a surface, it is returned unchanged. * - * A particular use case is to filter out non-polygonal components + * A particular use case is to filter out non-surface components * returned from an overlay operation. * * @param g the geometry to filter - * @return a Polygonal geometry + * @return a Surface geometry */ - static std::unique_ptr restrictToPolygons(std::unique_ptr g); + static std::unique_ptr restrictToSurfaces(std::unique_ptr g); public: - CascadedPolygonUnion(); /** \brief * Computes the union of a collection of polygonal [Geometrys](@ref geom::Geometry). @@ -140,13 +140,13 @@ public: * @param polys a collection of polygonal [Geometrys](@ref geom::Geometry). * 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, geos::util::ProgressFunction* progressFunction); + static std::unique_ptr Union(const std::vector& polys); + static std::unique_ptr Union(const std::vector& polys, UnionStrategy* unionFun, geos::util::ProgressFunction* progressFunction); /** \brief - * Computes the union of a set of polygonal [Geometrys](@ref geom::Geometry). + * Computes the union of a set of surface [Geometrys](@ref geom::Geometry). * - * @tparam T an iterator yielding something castable to const Polygon * + * @tparam T an iterator yielding something castable to const Surface* * @param start start iterator * @param end end iterator * @param unionStrategy strategy to apply @@ -156,12 +156,12 @@ public: static std::unique_ptr Union(T start, T end, UnionStrategy *unionStrategy, geos::util::ProgressFunction* progressFunction) { - std::vector polys; + std::vector polys; for(T i = start; i != end; ++i) { - const geom::Polygon* p = dynamic_cast(*i); - polys.push_back(const_cast(p)); + const auto* p = dynamic_cast(*i); + polys.push_back(p); } - return Union(&polys, unionStrategy, progressFunction); + return Union(polys, unionStrategy, progressFunction); } /** \brief @@ -177,16 +177,16 @@ public: * Creates a new instance to union the given collection of * [Geometrys](@ref geom::Geometry). * - * @param polys a collection of polygonal [Geometrys](@ref geom::Geometry). + * @param polys a collection of surface [Geometrys](@ref geom::Geometry). * Ownership of elements *and* vector are left to caller. */ - CascadedPolygonUnion(std::vector* polys) + explicit CascadedPolygonUnion(const std::vector& polys) : inputPolys(polys) , geomFactory(nullptr) , unionFunction(&defaultUnionFunction) {} - CascadedPolygonUnion(std::vector* polys, UnionStrategy* unionFun) + CascadedPolygonUnion(const std::vector& polys, UnionStrategy* unionFun) : inputPolys(polys) , geomFactory(nullptr) , unionFunction(unionFun) @@ -232,7 +232,7 @@ private: */ std::unique_ptr unionSafe(const geom::Geometry* g0, const geom::Geometry* g1) const; - std::unique_ptr unionSafe(std::unique_ptr &&, std::unique_ptr &&); + std::unique_ptr unionSafe(std::unique_ptr &&, std::unique_ptr &&) const; /** * Encapsulates the actual unioning of two polygonal geometries. diff --git a/include/geos/operation/union/UnaryUnionOp.h b/include/geos/operation/union/UnaryUnionOp.h index 98b6a9e70..39e127d89 100644 --- a/include/geos/operation/union/UnaryUnionOp.h +++ b/include/geos/operation/union/UnaryUnionOp.h @@ -171,21 +171,7 @@ private: } } - void - extract(const geom::Geometry& geom) - { - util::ensureNoCurvedComponents(geom); - - using namespace geom::util; - - if(! geomFact) { - geomFact = geom.getFactory(); - } - - GeometryExtracter::extract(geom, polygons); - GeometryExtracter::extract(geom, lines); - GeometryExtracter::extract(geom, points); - } + void extract(const geom::Geometry& geom); /** * Computes a unary union with no extra optimization, @@ -217,14 +203,14 @@ private: * @return the union of the input(s) * @return null if both inputs are null */ - std::unique_ptr unionWithNull( + static std::unique_ptr unionWithNull( std::unique_ptr g0, std::unique_ptr g1 ); // Members - std::vector polygons; - std::vector lines; + std::vector polygons; + std::vector lines; std::vector points; const geom::GeometryFactory* geomFact; diff --git a/src/operation/union/CascadedPolygonUnion.cpp b/src/operation/union/CascadedPolygonUnion.cpp index aac120c80..4973124a3 100644 --- a/src/operation/union/CascadedPolygonUnion.cpp +++ b/src/operation/union/CascadedPolygonUnion.cpp @@ -21,13 +21,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include // std @@ -44,7 +44,7 @@ namespace geounion { // geos.operation.geounion // //////////////////////////////////////////////////////////////////////////// std::unique_ptr -CascadedPolygonUnion::Union(std::vector* polys) +CascadedPolygonUnion::Union(const std::vector& polys) { CascadedPolygonUnion op(polys); geos::util::ProgressFunction* progressFunction = nullptr; @@ -52,7 +52,7 @@ CascadedPolygonUnion::Union(std::vector* polys) } std::unique_ptr -CascadedPolygonUnion::Union(std::vector* polys, UnionStrategy* unionFun, geos::util::ProgressFunction* progressFunction) +CascadedPolygonUnion::Union(const std::vector& polys, UnionStrategy* unionFun, geos::util::ProgressFunction* progressFunction) { CascadedPolygonUnion op(polys, unionFun); return op.Union(progressFunction); @@ -61,24 +61,24 @@ CascadedPolygonUnion::Union(std::vector* polys, UnionStrategy* u std::unique_ptr CascadedPolygonUnion::Union(const geom::MultiPolygon* multipoly, geos::util::ProgressFunction* progressFunction) { - std::vector polys; + std::vector polys; for(const auto& g : *multipoly) { polys.push_back(dynamic_cast(g.get())); } - CascadedPolygonUnion op(&polys); + CascadedPolygonUnion op(polys); return op.Union(progressFunction); } std::unique_ptr CascadedPolygonUnion::Union(geos::util::ProgressFunction* progressFunction) { - if(inputPolys->empty()) { + if(inputPolys.empty()) { return nullptr; } - geomFactory = inputPolys->front()->getFactory(); + geomFactory = inputPolys.front()->getFactory(); /* * A spatial index to organize the collection @@ -87,8 +87,8 @@ CascadedPolygonUnion::Union(geos::util::ProgressFunction* progressFunction) * to be eliminated on each round. */ - index::strtree::TemplateSTRtree index(10, inputPolys->size()); - for (const auto& p : *inputPolys) { + index::strtree::TemplateSTRtree index(10, inputPolys.size()); + for (const auto& p : inputPolys) { index.insert(p); } @@ -153,7 +153,7 @@ CascadedPolygonUnion::unionSafe(const geom::Geometry* g0, const geom::Geometry* } std::unique_ptr -CascadedPolygonUnion::unionSafe(std::unique_ptr && g0, std::unique_ptr && g1) +CascadedPolygonUnion::unionSafe(std::unique_ptr && g0, std::unique_ptr && g1) const { if(g0 == nullptr && g1 == nullptr) { return nullptr; @@ -174,7 +174,7 @@ CascadedPolygonUnion::unionActual(const geom::Geometry* g0, const geom::Geometry { std::unique_ptr ug; ug = unionFunction->Union(g0, g1); - return restrictToPolygons(std::move(ug)); + return restrictToSurfaces(std::move(ug)); } std::unique_ptr @@ -182,11 +182,11 @@ CascadedPolygonUnion::unionActual(std::unique_ptr && g0, std::un { std::unique_ptr ug; ug = unionFunction->Union(std::move(g0), std::move(g1)); - return restrictToPolygons(std::move(ug)); + return restrictToSurfaces(std::move(ug)); } std::unique_ptr -CascadedPolygonUnion::restrictToPolygons(std::unique_ptr g) +CascadedPolygonUnion::restrictToSurfaces(std::unique_ptr g) { using namespace geom; @@ -199,12 +199,19 @@ CascadedPolygonUnion::restrictToPolygons(std::unique_ptr g) auto coll = dynamic_cast(g.get()); if (coll) { - // Release polygons from the collection and re-form into MultiPolygon + // Release polygons from the collection and re-form into Surface auto components = coll->releaseGeometries(); components.erase(std::remove_if(components.begin(), components.end(), [](const std::unique_ptr & cmp) { return !cmp->isPolygonal(); }), components.end()); + const bool hasCurves = std::any_of(components.begin(), components.end(), [](const auto & cmp) { + return cmp->hasCurvedComponents(); + }); + + if (hasCurves) { + return gfact->createMultiSurface(std::move(components)); + } return gfact->createMultiPolygon(std::move(components)); } else { // Not polygonal and not a collection? No polygons here. diff --git a/src/operation/union/UnaryUnionOp.cpp b/src/operation/union/UnaryUnionOp.cpp index 7b6601c14..94f1bafff 100644 --- a/src/operation/union/UnaryUnionOp.cpp +++ b/src/operation/union/UnaryUnionOp.cpp @@ -34,8 +34,9 @@ #include #include #include +#include -#include "geos/util.h" +using geos::geom::util::GeometryExtracter; namespace geos { namespace operation { // geos::operation @@ -62,6 +63,18 @@ UnaryUnionOp::unionWithNull(std::unique_ptr g0, return ret; } +void +UnaryUnionOp::extract(const geom::Geometry& geom) +{ + if(! geomFact) { + geomFact = geom.getFactory(); + } + + GeometryExtracter::extract(geom, polygons); + GeometryExtracter::extract(geom, lines); + GeometryExtracter::extract(geom, points); +} + /*public*/ std::unique_ptr UnaryUnionOp::Union(geos::util::ProgressFunction* progressFunction) diff --git a/tests/unit/capi/GEOSUnaryUnionTest.cpp b/tests/unit/capi/GEOSUnaryUnionTest.cpp index 24f99fcb9..af484b8ef 100644 --- a/tests/unit/capi/GEOSUnaryUnionTest.cpp +++ b/tests/unit/capi/GEOSUnaryUnionTest.cpp @@ -220,37 +220,15 @@ template<> template<> void object::test<12>() { - set_test_name("curved inputs"); - useContext(); + set_test_name("curved dim=1 inputs -> CompoundCurve"); - input_ = fromWKT("MULTICURVE (CIRCULARSTRING (-5 0, 0 5, 5 0), (-5 3, 5 3))"); - ensure(input_); - GEOSSetSRID_r(ctxt_, input_, 4326); - double input_length = -1; - ensure(GEOSLength_r(ctxt_, input_, &input_length)); + input_ = fromWKT("MULTICURVE (CIRCULARSTRING (-5 0, 0 5, 5 0), (5 0, 5 3))"); - result_ = GEOSUnaryUnion_r(ctxt_, input_); - ensure(result_ == nullptr); - - GEOSCurveToLineParams_setMaxStepDegrees_r(ctxt_, curveToLineParams_, 1); - GEOSContext_setCurveToLineParams_r(ctxt_, curveToLineParams_); - - // Input converted to line, output not converted to curve - result_ = GEOSUnaryUnion_r(ctxt_, input_); + result_ = GEOSUnaryUnion(input_); ensure(result_); - ensure_equals(GEOSGeomTypeId_r(ctxt_, result_), GEOS_MULTILINESTRING); - ensure_equals(GEOSGetSRID_r(ctxt_, result_), 4326); - GEOSGeom_destroy_r(ctxt_, result_); - // Input converted to line, output converted to curve - GEOSContext_setLineToCurveParams_r(ctxt_, lineToCurveParams_); - result_ = GEOSUnaryUnion_r(ctxt_, input_); - ensure_equals(GEOSGeomTypeId_r(ctxt_, result_), GEOS_MULTICURVE); - double result_length = -1; - ensure(GEOSLength_r(ctxt_, result_, &result_length)); - - ensure_equals(GEOSGetSRID_r(ctxt_, result_), 4326); - ensure_equals("length does not match", result_length, input_length, 1e-5); + expected_ = fromWKT("COMPOUNDCURVE (CIRCULARSTRING (-5 0, 0 5, 5 0), (5 0, 5 3))"); + ensure_geometry_equals(result_, expected_); } template<> @@ -308,6 +286,53 @@ void object::test<14>() { ensure_equals(fracs[0], 1.0); } +template<> +template<> +void object::test<15>() +{ + set_test_name("curved polygonal inputs"); + + input_ = fromWKT("MULTISURFACE (CURVEPOLYGON (COMPOUNDCURVE ((5 0, 0 0, 0 5, 5 5), CIRCULARSTRING (5 5, 2.5 2.5, 5 0))), CURVEPOLYGON (COMPOUNDCURVE (CIRCULARSTRING (5 0, 1.5 2.5, 5 5), (5 5, 10 5, 10 0, 5 0))))"); + + result_ = GEOSUnaryUnion(input_); + ensure(result_); + + expected_ = fromWKT("CURVEPOLYGON (COMPOUNDCURVE ((0 0, 0 5, 3.2857142857 5), CIRCULARSTRING (3.2857142857 5, 4.1428571429 5.1428571429, 5 5), (5 5, 10 5, 10 0, 5 0), CIRCULARSTRING (5 0, 4.1428571429 -0.1428571429, 3.2857142857 0), (3.2857142857 0, 0 0)))"); + ensure_geometry_equals(result_, expected_, 1e-6); +} + +template<> +template<> +void object::test<16>() +{ + set_test_name("curved dim=1 inputs -> MultiCurve"); + + input_ = fromWKT("MULTICURVE (CIRCULARSTRING (-5 0, 0 5, 5 0), (0 0, 5 5))"); + + result_ = GEOSUnaryUnion(input_); + ensure(result_); + + expected_ = fromWKT("MULTICURVE (CIRCULARSTRING (3.5355339059 3.5355339059, 4.6193976626 1.9134171618, 5 0), CIRCULARSTRING (-5 0, -1.9134171618 4.6193976626, 3.5355339059 3.5355339059), (3.5355339059 3.5355339059, 5 5), (0 0, 3.5355339059 3.5355339059))"); + ensure_geometry_equals(result_, expected_, 1e-6); +} + +template<> +template<> +void object::test<17>() +{ + set_test_name("curved input is not linearized when CurveToLineParams registered with a context"); + useContext(); + + input_ = fromWKT("CIRCULARSTRING (0 0, 1 1, 2 0)"); + + GEOSCurveToLineParams_setMaxStepDegrees_r(ctxt_, curveToLineParams_, 45); + GEOSContext_setCurveToLineParams_r(ctxt_, curveToLineParams_); + + result_ = GEOSUnaryUnion_r(ctxt_, input_); + + ensure_geometry_equals(result_, input_, 1e-8); +} + } // namespace tut diff --git a/tests/unit/geom/CircularStringTest.cpp b/tests/unit/geom/CircularStringTest.cpp index 59f0de931..3be6a7b7e 100644 --- a/tests/unit/geom/CircularStringTest.cpp +++ b/tests/unit/geom/CircularStringTest.cpp @@ -169,7 +169,7 @@ void object::test<3>() ensure("equalsIdentical", cs_->equalsIdentical(cs2.get())); // Overlay - ensure_THROW(cs_->Union(), geos::util::UnsupportedOperationException); + ensure_equals_geometry(cs_->Union().get(), static_cast(cs_.get())); ensure_equals(cs_->Union(cs_.get())->getLength(), cs_->getLength()); ensure(cs_->difference(cs_.get())->isEmpty()); ensure_equals(cs_->intersection(cs_.get())->getLength(), cs_->getLength()); diff --git a/tests/unit/geom/CompoundCurveTest.cpp b/tests/unit/geom/CompoundCurveTest.cpp index 79956e095..d6f5cbfde 100644 --- a/tests/unit/geom/CompoundCurveTest.cpp +++ b/tests/unit/geom/CompoundCurveTest.cpp @@ -170,11 +170,10 @@ void object::test<3>() ensure("equalsIdentical", cc_->equalsIdentical(cc2.get())); // Overlay - ensure_THROW(cc_->Union(), geos::util::UnsupportedOperationException); - // TODO: Prevent overlay from degrading CompoundCurve into MultiCurve - //ensure_equals_geometry(cc_->Union(cc_.get()).get(), static_cast(cc_.get())); + ensure_equals_geometry(cc_->Union().get(), static_cast(cc_.get())); + ensure_equals_geometry(cc_->Union(cc_.get()).get(), static_cast(cc_.get())); ensure(cc_->difference(cc_.get())->isEmpty()); - //ensure_equals_geometry(cc_->intersection(cc_.get()).get(), static_cast(cc_.get())); + ensure_equals_geometry(cc_->intersection(cc_.get()).get(), static_cast(cc_.get())); ensure(cc_->symDifference(cc_.get())->isEmpty()); // Distance diff --git a/tests/unit/geom/CurvePolygonTest.cpp b/tests/unit/geom/CurvePolygonTest.cpp index 52eb905b2..562b27637 100644 --- a/tests/unit/geom/CurvePolygonTest.cpp +++ b/tests/unit/geom/CurvePolygonTest.cpp @@ -167,7 +167,7 @@ void object::test<3>() ensure("equalsIdentical", cp_->equalsIdentical(cp2.get())); // Overlay - ensure_THROW(cp_->Union(), geos::util::UnsupportedOperationException); + ensure_equals_geometry(cp_->Union().get(), static_cast(cp_.get())); ensure_equals(cp_->Union(cp_.get())->getLength(), cp_->getLength()); ensure(cp_->difference(cp_.get())->isEmpty()); ensure_equals(cp_->intersection(cp_.get())->getLength(), cp_->getLength()); diff --git a/tests/unit/geom/MultiCurveTest.cpp b/tests/unit/geom/MultiCurveTest.cpp index e4f264c8c..3ec6cd3d2 100644 --- a/tests/unit/geom/MultiCurveTest.cpp +++ b/tests/unit/geom/MultiCurveTest.cpp @@ -154,11 +154,10 @@ void object::test<3>() ensure("equalsIdentical", mc_->equalsIdentical(cc2.get())); // Overlay - ensure_THROW(mc_->Union(), geos::util::UnsupportedOperationException); - // TODO: Prevent overlay from degrading CompoundCurve into MultiCurve - //ensure_equals_geometry(mc_->Union(mc_.get()).get(), static_cast(mc_.get())); + ensure_equals_geometry(mc_->Union().get(), static_cast(mc_.get())); + ensure_equals_geometry(mc_->Union(mc_.get()).get(), static_cast(mc_.get())); ensure(mc_->difference(mc_.get())->isEmpty()); - //ensure_equals_geometry(mc_->intersection(mc_.get()).get(), static_cast(mc_.get())); + ensure_equals_geometry(mc_->intersection(mc_.get()).get(), static_cast(mc_.get())); ensure(mc_->symDifference(mc_.get())->isEmpty()); // Distance diff --git a/tests/unit/geom/MultiSurfaceTest.cpp b/tests/unit/geom/MultiSurfaceTest.cpp index 9f84cdc28..46679188e 100644 --- a/tests/unit/geom/MultiSurfaceTest.cpp +++ b/tests/unit/geom/MultiSurfaceTest.cpp @@ -143,7 +143,7 @@ void object::test<3>() ensure("equalsIdentical", ms_->equalsIdentical(cp2.get())); // Overlay - ensure_THROW(ms_->Union(), geos::util::UnsupportedOperationException); + ensure_equals_geometry(ms_->Union().get(), static_cast(ms_.get())); ensure_equals_geometry(ms_->Union(ms_.get()).get(), static_cast(ms_.get())); ensure(ms_->difference(ms_.get())->isEmpty()); ensure_equals_geometry(ms_->intersection(ms_.get()).get(), static_cast(ms_.get())); diff --git a/tests/unit/operation/geounion/CascadedPolygonUnionTest.cpp b/tests/unit/operation/geounion/CascadedPolygonUnionTest.cpp index adf5b2628..b084e7f39 100644 --- a/tests/unit/operation/geounion/CascadedPolygonUnionTest.cpp +++ b/tests/unit/operation/geounion/CascadedPolygonUnionTest.cpp @@ -42,13 +42,12 @@ typedef group::object object; group test_cascadedpolygonuniontest_group("geos::operation::geounion::CascadedPolygonUnion"); // test runner -geos::geom::Geometry* -unionIterated( - std::vector* geoms) +std::unique_ptr +unionIterated(const std::vector& geoms) { std::unique_ptr unionAll; - for(const auto& p : *geoms) { + for(const auto& p : geoms) { if(unionAll == nullptr) { unionAll = p->clone(); } @@ -56,20 +55,19 @@ unionIterated( unionAll = unionAll->Union(p); } } - return unionAll.release(); + return unionAll; } std::unique_ptr unionCascaded( - std::vector* geoms) + const std::vector& geoms) { using geos::operation::geounion::CascadedPolygonUnion; return CascadedPolygonUnion::Union(geoms); } void -p_test_runner( - std::vector* geoms) +p_test_runner(const std::vector& geoms) { std::unique_ptr union1(unionIterated(geoms)); std::unique_ptr union2(unionCascaded(geoms)); @@ -78,12 +76,6 @@ p_test_runner( ensure_equals_geometry(union1.get(), union2.get(), 0.000001); } -void -delete_geometry(geos::geom::Geometry* g) -{ - delete g; -} - // // Test Cases // @@ -100,7 +92,7 @@ void object::test<1> nullptr }; - std::vector g; + std::vector g; for(char const * const* p = polygons; *p != nullptr; ++p) { std::string wkt(*p); geos::geom::Polygon* geom = @@ -108,9 +100,11 @@ void object::test<1> g.push_back(geom); } - p_test_runner(&g); + p_test_runner(g); - for_each(g.begin(), g.end(), delete_geometry); + for (auto& p : g) { + delete p; + } } void ----------------------------------------------------------------------- Summary of changes: capi/geos_ts_c.cpp | 7 +- .../geos/operation/union/CascadedPolygonUnion.h | 42 ++++++------ include/geos/operation/union/UnaryUnionOp.h | 22 ++---- src/operation/union/CascadedPolygonUnion.cpp | 35 ++++++---- src/operation/union/UnaryUnionOp.cpp | 15 +++- tests/unit/capi/GEOSUnaryUnionTest.cpp | 79 ++++++++++++++-------- tests/unit/geom/CircularStringTest.cpp | 2 +- tests/unit/geom/CompoundCurveTest.cpp | 7 +- tests/unit/geom/CurvePolygonTest.cpp | 2 +- tests/unit/geom/MultiCurveTest.cpp | 7 +- tests/unit/geom/MultiSurfaceTest.cpp | 2 +- .../geounion/CascadedPolygonUnionTest.cpp | 28 +++----- 12 files changed, 136 insertions(+), 112 deletions(-) hooks/post-receive -- GEOS From git at osgeo.org Thu Jul 9 15:52:24 2026 From: git at osgeo.org (git at osgeo.org) Date: Thu, 9 Jul 2026 15:52:24 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. 6354193e908802263057d0ef8dcd3689f6c99a4a Message-ID: <20260709225224.8431B1C6514@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via 6354193e908802263057d0ef8dcd3689f6c99a4a (commit) from 4fb859518f4642a0ac8b0f967d1dd54d2a2a7f02 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 6354193e908802263057d0ef8dcd3689f6c99a4a Author: Daniel Baston Date: Thu Jul 9 18:51:56 2026 -0400 PointGeometryUnion: Avoid dropping M values (#1474) diff --git a/src/operation/union/PointGeometryUnion.cpp b/src/operation/union/PointGeometryUnion.cpp index 4ff569957..e997b9aab 100644 --- a/src/operation/union/PointGeometryUnion.cpp +++ b/src/operation/union/PointGeometryUnion.cpp @@ -43,7 +43,7 @@ PointGeometryUnion::Union() const PointLocator locater; // use a set to eliminate duplicates, as required for union - std::set exteriorCoords; + std::set exteriorCoords; for(std::size_t i = 0, n = pointGeom.getNumGeometries(); i < n; ++i) { const Point* point = dynamic_cast(pointGeom.getGeometryN(i)); @@ -53,10 +53,12 @@ PointGeometryUnion::Union() const continue; } - const Coordinate* coord = static_cast(point->getCoordinate()); + const CoordinateXY* coord = point->getCoordinate(); Location loc = locater.locate(*coord, &otherGeom); if(loc == Location::EXTERIOR) { - exteriorCoords.insert(*coord); + CoordinateXYZM ptZM; + point->getCoordinatesRO()->getAt(0, ptZM); + exteriorCoords.insert(ptZM); } } diff --git a/tests/unit/operation/geounion/UnaryUnionOpTest.cpp b/tests/unit/operation/geounion/UnaryUnionOpTest.cpp index 9f2023e39..e849daa93 100644 --- a/tests/unit/operation/geounion/UnaryUnionOpTest.cpp +++ b/tests/unit/operation/geounion/UnaryUnionOpTest.cpp @@ -76,7 +76,7 @@ struct test_unaryuniontest_data { using std::endl; GeomPtr a2 = normalize(a); GeomPtr b2 = normalize(b); - bool eq = a2->equalsExact(b2.get()); + bool eq = a2->equalsIdentical(b2.get()); if(! eq) { cout << "EXPECTED: " << wktwriter.write(a2.get()) << endl; cout << "OBTAINED: " << wktwriter.write(b2.get()) << endl; @@ -206,5 +206,19 @@ void object::test<7> doTest(geoms, "LINESTRING EMPTY"); } +template<> +template<> +void object::test<8>() { + static char const* const geoms[] = { + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + "POINT M (12 13 14)", + "POINT Z (12 13 8)", + "POINT M (12 13 16)", + nullptr + }; + + doTest(geoms, "GEOMETRYCOLLECTION (POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0)), POINT M (12 13 14))"); +} + } // namespace tut ----------------------------------------------------------------------- Summary of changes: src/operation/union/PointGeometryUnion.cpp | 8 +++++--- tests/unit/operation/geounion/UnaryUnionOpTest.cpp | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 4 deletions(-) hooks/post-receive -- GEOS From git at osgeo.org Thu Jul 9 17:51:08 2026 From: git at osgeo.org (git at osgeo.org) Date: Thu, 9 Jul 2026 17:51:08 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. 9cccf277d788e6326ca2dbb6d2d928941f211b97 Message-ID: <20260710005109.2A2001C65CF@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via 9cccf277d788e6326ca2dbb6d2d928941f211b97 (commit) via bf1980bed13ebb05fac0cff5e1f75e91a33c967d (commit) via d4fb3df21da6ec42792409f7e7269ef4a8abe6b2 (commit) from 6354193e908802263057d0ef8dcd3689f6c99a4a (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 9cccf277d788e6326ca2dbb6d2d928941f211b97 Author: Daniel Baston Date: Thu Jul 9 13:51:37 2026 -0400 EdgeNodingBuilder: avoid clipping linear rings of a CurvePolygon diff --git a/src/operation/overlayng/EdgeNodingBuilder.cpp b/src/operation/overlayng/EdgeNodingBuilder.cpp index ded0c128b..b0818bce9 100644 --- a/src/operation/overlayng/EdgeNodingBuilder.cpp +++ b/src/operation/overlayng/EdgeNodingBuilder.cpp @@ -375,9 +375,12 @@ EdgeNodingBuilder::clip(const LineString* ring) const /** * If no clipper or ring is completely contained then no need to clip. + * We also cannot clip if any input contains curves, because curved + * rings are not currently clipped and clipping only some rings could + * disrupt the polygon topology. * But repeated points must be removed to ensure correct noding. */ - if (clipper == nullptr || clipEnv->covers(env)) { + if (clipper == nullptr || clipEnv->covers(env) || inputHasCurves) { return removeRepeatedPoints(ring); } diff --git a/tests/xmltester/tests/general/TestOverlayCurvesAA.xml b/tests/xmltester/tests/general/TestOverlayCurvesAA.xml new file mode 100644 index 000000000..0629b027f --- /dev/null +++ b/tests/xmltester/tests/general/TestOverlayCurvesAA.xml @@ -0,0 +1,16 @@ + + + AA - A with hole intersecting B + + CURVEPOLYGON((20 20, 20 160, 160 160, 160 20, 20 20), COMPOUNDCURVE ( CIRCULARSTRING (140 40, 115 115, 40 140), (40 140, 40 40, 140 40))) + + + POLYGON((80 100, 220 100, 220 240, 80 240, 80 100)) + + + + CURVEPOLYGON (COMPOUNDCURVE ((80 136.50148808205722, 80 160), (80 160, 160 160, 160 100, 127.04025757937788 100), CIRCULARSTRING (127.04025757937788 100, 106.68619012043186 122.3309157911624, 80 136.50148808205722))) + + + + commit bf1980bed13ebb05fac0cff5e1f75e91a33c967d Author: Daniel Baston Date: Thu Jul 9 10:30:16 2026 -0400 XMLTester: support curved geometries diff --git a/tests/xmltester/XMLTester.cpp b/tests/xmltester/XMLTester.cpp index 01803404b..16b1690aa 100644 --- a/tests/xmltester/XMLTester.cpp +++ b/tests/xmltester/XMLTester.cpp @@ -510,28 +510,10 @@ XMLTester::parseGeometry(const std::string& in, const char* label) std::unique_ptr ret; - switch(first_char) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': + if (first_char == '0') { ret = wkbreader->readHEX(is); - break; - default: + } else { ret = wktreader->read(in); - break; } if(testValidInput) { @@ -690,16 +672,20 @@ bool Test::checkOverlaySuccess(Geometry const& gExpected, Geometry const& gActual) { double tol = operation::overlay::snap::GeometrySnapper::computeSizeBasedSnapTolerance(gExpected); - //-- BUG: this allows all empties to test equal - if(gExpected.equals(&gActual)) { - return 1; + + if (!gExpected.hasCurvedComponents()) { + //-- BUG: this allows all empties to test equal + if(gExpected.equals(&gActual)) { + return true; + } } + //TODO: is this needed by any tests? std::cerr << "Using an overlay tolerance of " << tol << std::endl; if(gExpected.equalsExact(&gActual, tol)) { - return 1; + return true; } - return 0; + return false; } /* static */ commit d4fb3df21da6ec42792409f7e7269ef4a8abe6b2 Author: Daniel Baston Date: Thu Jul 9 10:29:01 2026 -0400 OverlayNG: improve exception message on curved geometry failure diff --git a/src/operation/overlayng/OverlayNGRobust.cpp b/src/operation/overlayng/OverlayNGRobust.cpp index 3e8da6f9d..bca5e6e44 100644 --- a/src/operation/overlayng/OverlayNGRobust.cpp +++ b/src/operation/overlayng/OverlayNGRobust.cpp @@ -120,6 +120,10 @@ OverlayNGRobust::Overlay(const Geometry* geom0, const Geometry* geom1, int opCod return result; } catch (const std::runtime_error &ex) { + if (geom0->hasCurvedComponents() || geom1->hasCurvedComponents()) { + // Snapping strategies do not support curved geometries. + throw; + } /** * Capture original exception, * so it can be rethrown if the remaining strategies all fail. @@ -130,6 +134,7 @@ OverlayNGRobust::Overlay(const Geometry* geom0, const Geometry* geom1, int opCod #endif } + /** * On failure retry using snapping noding with a "safe" tolerance. * if this throws an exception just let it go, ----------------------------------------------------------------------- Summary of changes: src/operation/overlayng/EdgeNodingBuilder.cpp | 5 ++- src/operation/overlayng/OverlayNGRobust.cpp | 5 +++ tests/xmltester/XMLTester.cpp | 36 +++++++--------------- .../tests/general/TestOverlayCurvesAA.xml | 16 ++++++++++ 4 files changed, 36 insertions(+), 26 deletions(-) create mode 100644 tests/xmltester/tests/general/TestOverlayCurvesAA.xml hooks/post-receive -- GEOS From git at osgeo.org Fri Jul 10 07:27:26 2026 From: git at osgeo.org (git at osgeo.org) Date: Fri, 10 Jul 2026 07:27:26 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. c6e5ea1bee84a255495413887e8db4b7ab7d323d Message-ID: <20260710142726.CEA551CF980@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via c6e5ea1bee84a255495413887e8db4b7ab7d323d (commit) from 9cccf277d788e6326ca2dbb6d2d928941f211b97 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit c6e5ea1bee84a255495413887e8db4b7ab7d323d Author: Pieter Roggemans Date: Fri Jul 10 16:27:07 2026 +0200 DOC: small improvements to docs of CoverageCleanWithParams (#1475) diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in index 26b88aca6..8121146ba 100644 --- a/capi/geos_c.h.in +++ b/capi/geos_c.h.in @@ -4979,18 +4979,22 @@ GEOSCoverageCleanParams_destroy( GEOSCoverageCleanParams* params); /** +* Set the snapping distance to use when cleaning a coverage. +* * Snapping to nearby vertices and line segment snapping * is used to improve noding robustness -* and eliminate small errors in an efficient way, +* and eliminate small errors in an efficient way. +* * By default this uses a very small snapping distance * based on the extent of the input data. -* The snapping distance may be specified explicitly. -* This can reduce the number of overlaps and gaps that need to be merged, -* and reduce the risk of spikes formed by merging gaps. -* However, a large snapping distance may introduce undesirable +* +* Specifying a larger snapping distance can reduce the number of overlaps and +* gaps that need to be merged, and reduce the risk of spikes formed by merging +* gaps. However, a large snapping distance can also introduce undesirable * data alteration. * * A distance of zero prevents snapping from being used. +* * \see geos::coverage::CoverageCleaner::setSnappingDistance * \param params The GEOSCoverageCleanParams to operate on * \param snappingDistance Set to 0.0 for no snapping. @@ -5004,19 +5008,22 @@ GEOSCoverageCleanParams_setSnappingDistance( double snappingDistance); /** -* Gaps which are wider than a given distance are merged with an adjacent polygon. -* Polygon width is determined as twice the radius of the MaximumInscribedCircle -* of the gap polygon. -* Gaps are merged with the adjacent polygon with longest shared border. +* Set the maximum width of gaps to merge when cleaning a coverage. +* +* Gaps wider than the given distance are merged with the adjacent polygon +* having the longest shared border with the gap. (The overlap merge strategy +* is not used.) +* +* Gaps are defined as enclosed empty areas with width less than a distance tolerance. * Empty holes in input polygons are treated as gaps, and may be filled in. * Gaps which are not fully enclosed ("inlets") are not removed. * -* The width of a gap is twice the radius of the Maximum Inscribed Circle in the gap polygon, +* Width is computed as the diameter of the Maximum Inscribed Circle of the gap polygon. * A width of zero prevents gaps from being merged. * * \see geos::coverage::CoverageCleaner::setGapMaximumWidth * \param params The GEOSCoverageCleanParams to operate on -* \param gapMaximumWidth Set to 0.0 for no snapping. +* \param gapMaximumWidth Set to 0.0 for no gap merging. * \return 0 on exception, 1 on success. * * \since 3.14 @@ -5026,21 +5033,23 @@ GEOSCoverageCleanParams_setGapMaximumWidth( GEOSCoverageCleanParams* params, double gapMaximumWidth); -/* +/** * Sets the overlap merge strategy to use, using one from \ref GEOSOverlapMerge. -* Overlaps are merged with an adjacent polygon chosen according to a specified merge strategy. -* The supported strategies are: * -* * **Longest Border**: (default) merge with the polygon with longest shared border (GEOS_MERGE_LONGEST_BORDER.) -* * **Maximum/Minimum Area**: merge with the polygon with largest or smallest area (GEOS_MERGE_MAX_AREA, GEOS_MERGE_MIN_AREA.) -* * **Minimum Index**: merge with the polygon with the lowest index in the input array (GEOS_MERGE_MIN_INDEX.) +* Overlaps are merged with an adjacent polygon chosen according to a specified +* merge strategy. The supported strategies are: * -* This allows sorting the input according to some criteria to provide a priority -* for merging gaps. +* - **Longest Border**: (default) merge with the polygon with longest shared +* border (GEOS_MERGE_LONGEST_BORDER.) +* - **Maximum/Minimum Area**: merge with the polygon with largest or smallest +* area (GEOS_MERGE_MAX_AREA, GEOS_MERGE_MIN_AREA.) +* - **Minimum Index**: merge with the polygon with the lowest index in the +* input array (GEOS_MERGE_MIN_INDEX.). This allows sorting the input +* according to some criteria to provide a priority for merging gaps. * * The default is GEOS_MERGE_LONGEST_BORDER. * -* \see geos::coverage::CoverageCleaner::setGapMaximumWidth +* \see geos::coverage::CoverageCleaner::setOverlapMergeStrategy * \param params The GEOSCoverageCleanParams to operate on * \param overlapMergeStrategy One of \ref GEOSOverlapMerge strategies * \return 0 on exception, 1 on success. @@ -5053,9 +5062,11 @@ GEOSCoverageCleanParams_setOverlapMergeStrategy( int overlapMergeStrategy); /** +* Clean a coverage. +* * Operates on a list of polygonal geometry with "exactly matching" -* edge geometry, to fix cases where the geometry does not in fact -* exactly match. +* edge geometry. Removes any overlaps and fixes cases where the geometry +* does not in fact exactly match. * * The input is a collection of polygons, and the output is a collection * with the same number of cleaned polygons, in the same order as diff --git a/include/geos/coverage/CoverageCleaner.h b/include/geos/coverage/CoverageCleaner.h index 233302045..612c9589e 100644 --- a/include/geos/coverage/CoverageCleaner.h +++ b/include/geos/coverage/CoverageCleaner.h @@ -288,7 +288,7 @@ public: /** * Sets the maximum width of the gaps that will be filled and merged. - * The width of a gap is twice the radius of the Maximum Inscribed Circle in the gap polygon, + * The width of a gap is the diameter of the Maximum Inscribed Circle in the gap polygon. * A width of zero prevents gaps from being merged. * * @param maxWidth the maximum gap width to merge ----------------------------------------------------------------------- Summary of changes: capi/geos_c.h.in | 55 ++++++++++++++++++++------------- include/geos/coverage/CoverageCleaner.h | 2 +- 2 files changed, 34 insertions(+), 23 deletions(-) hooks/post-receive -- GEOS From git at osgeo.org Mon Jul 13 06:30:02 2026 From: git at osgeo.org (git at osgeo.org) Date: Mon, 13 Jul 2026 06:30:02 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. 1d559c659e0862d5e4ff738391a9258d048d68b9 Message-ID: <20260713133002.E6FF8169344@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via 1d559c659e0862d5e4ff738391a9258d048d68b9 (commit) via 2bc8cca93c7088e57dbb7a73a5269d528b8fad1b (commit) via a4e97f3c1ebd9b7e746c1ec0f98d686b39a8ed43 (commit) via 92b27f72feeda5a27e3747abfda79c071a1fb4d4 (commit) via 7f1c5dca809b3dc4b6336ab3474011a1a57dd2ba (commit) from c6e5ea1bee84a255495413887e8db4b7ab7d323d (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 1d559c659e0862d5e4ff738391a9258d048d68b9 Author: Daniel Baston Date: Fri Jul 10 15:30:42 2026 -0400 NodableArcString: add unit test diff --git a/src/noding/NodableArcString.cpp b/src/noding/NodableArcString.cpp index 810258922..6db428795 100644 --- a/src/noding/NodableArcString.cpp +++ b/src/noding/NodableArcString.cpp @@ -287,7 +287,7 @@ NodableArcString::getNoded(std::vector>& splitArcs) { for (size_t arcIndex = 0; arcIndex < m_arcs.size(); arcIndex++) { #if DEBUG_NODABLE_ARC_STRING - std::cout << "arc " << arcIndex + 1 << " / " << m_arcs.size(); + std::cout << "arc " << arcIndex + 1 << " / " << m_arcs.size() << std::endl; #endif const CircularArc& toSplit = m_arcs[arcIndex]; const geom::CoordinateXY& center = toSplit.getCenter(); diff --git a/tests/unit/noding/NodableArcStringTest.cpp b/tests/unit/noding/NodableArcStringTest.cpp index 4bdedad95..c1f1c02ce 100644 --- a/tests/unit/noding/NodableArcStringTest.cpp +++ b/tests/unit/noding/NodableArcStringTest.cpp @@ -1,11 +1,13 @@ #include #include "utility.h" +#include #include using geos::algorithm::Orientation; using geos::geom::CircularArc; using geos::geom::Ordinate; +using geos::geom::CircularString; using geos::geom::CoordinateXY; using geos::geom::CoordinateXYZM; using geos::noding::ArcString; @@ -58,9 +60,29 @@ struct test_nodablearcstring_data { } } + void checkSplit(const std::string& wkt, const CoordinateXY& splitPt, std::size_t pathIndex, const std::vector& expected_wkt) + { + const auto g = reader_.read(wkt); + const auto& gfact = *g->getFactory(); + + NodableArcString nas(g->getArcs(), g->getSharedCoordinates(), false, false, nullptr); + nas.addIntersection(splitPt, pathIndex); + + std::vector> noded; + nas.getNoded(noded); + ensure_equals(noded.size(), expected_wkt.size()); + + for (std::size_t i = 0; i < noded.size(); i++) { + std::unique_ptr actual = gfact.createCircularString(noded[i]->getCoordinates()); + std::unique_ptr expected = reader_.read(expected_wkt[i]); + } + } + static void ensure_arc_equals(const CircularArc& actual, const CircularArc& expected, double tol) { ensure(actual.toString() + " does not equal expected " + expected.toString() ,actual.equals(expected, tol)); } + + geos::io::WKTReader reader_; }; typedef test_group group; @@ -170,4 +192,35 @@ void object::test<4>() ensure_equals(arc1.p1().m, (intPt.m + p2.m) / 2); } +template<> +template<> +void object::test<5>() +{ + set_test_name("splits at arc endpoints"); + + checkSplit("CIRCULARSTRING (100 0, 50 50, 0 0, -50 50, 0 100, 50 80, 100 100, 150 50, 100 0)", + {0, 0}, 0, { + "CIRCULARSTRING (100 0, 50 50, 0 0)", + "CIRCULARSTRING (0 0, -50 50, 0 100, 50 80, 100 100, 150 50, 100 0)" + }); + + checkSplit("CIRCULARSTRING (100 0, 50 50, 0 0, -50 50, 0 100, 50 80, 100 100, 150 50, 100 0)", + {0, 0}, 1, { + "CIRCULARSTRING (100 0, 50 50, 0 0)", + "CIRCULARSTRING (0 0, -50 50, 0 100, 50 80, 100 100, 150 50, 100 0)" + }); + + checkSplit("CIRCULARSTRING (100 0, 50 50, 0 0, -50 50, 0 100, 50 80, 100 100, 150 50, 100 0)", + {100, 100}, 2, { + "CIRCULARSTRING (100 0, 50 50, 0 0)", + "CIRCULARSTRING (0 0, -50 50, 0 100, 50 80, 100 100, 150 50, 100 0)" + }); + + checkSplit("CIRCULARSTRING (100 0, 50 50, 0 0, -50 50, 0 100, 50 80, 100 100, 150 50, 100 0)", + {100, 100}, 3, { + "CIRCULARSTRING (100 0, 50 50, 0 0)", + "CIRCULARSTRING (0 0, -50 50, 0 100, 50 80, 100 100, 150 50, 100 0)" + }); +} + } \ No newline at end of file commit 2bc8cca93c7088e57dbb7a73a5269d528b8fad1b Author: Daniel Baston Date: Fri Jul 10 14:16:54 2026 -0400 XMLTester: Use equalsExact for curve overlay result testing diff --git a/tests/xmltester/XMLTester.cpp b/tests/xmltester/XMLTester.cpp index e285ec4d9..3e4aac3a0 100644 --- a/tests/xmltester/XMLTester.cpp +++ b/tests/xmltester/XMLTester.cpp @@ -846,11 +846,17 @@ Test::areaDelta(const geom::Geometry* a, const geom::Geometry* b, std::string& r void Test::checkResult( const Geometry& result ) { - checkResult( result, - [](Geometry& expected, Geometry& actual) -> bool { - //TODO: change to equalsExact, since compareTo doesn't check empty type - return expected.compareTo(&actual) == 0; - }); + if (result.hasCurvedComponents()) { + checkResult(result, [](Geometry& expected, Geometry& actual) -> bool { + return checkOverlaySuccess(expected, actual); + }); + } else { + checkResult( result, + [](Geometry& expected, Geometry& actual) -> bool { + //TODO: change to equalsExact, since compareTo doesn't check empty type + return expected.compareTo(&actual) == 0; + }); + } } void commit a4e97f3c1ebd9b7e746c1ec0f98d686b39a8ed43 Author: Daniel Baston Date: Fri Jul 10 12:42:14 2026 -0400 NodableArcString: Avoid missed split in multi-arc ArcStrings diff --git a/src/noding/NodableArcString.cpp b/src/noding/NodableArcString.cpp index d05865e79..810258922 100644 --- a/src/noding/NodableArcString.cpp +++ b/src/noding/NodableArcString.cpp @@ -94,14 +94,18 @@ prepareArcPoints(const CircularArc& arc, std::vector splitPoints { CoordinateXYZM p2; arc.getCoordinateSequence()->getAt(arc.getCoordinatePosition() + (isCCW ? 2 : 0), p2); + if (!splitPoints.empty() && splitPoints.back().equals2D(p2)) { + splitEnd = true; + } splitPoints.push_back(p2); } #if DEBUG_NODABLE_ARC_STRING std::cout << std::setprecision(17); std::cout << std::endl; - std::cout << "Edge from " << arc.p0() << " to " << arc.p2() << " has " << splitPoints.size() << " potential split points:" << std::endl; - std::cout << "Edge is CIRCULARSTRING " << *arc.getCoordinateSequence() << std::endl; + std::cout << "Arc from " << arc.p0() << " to " << arc.p2() << " has " << splitPoints.size() << " potential split points:" << std::endl; + std::cout << "Arc is CIRCULARSTRING " << *arc.getCoordinateSequence() << std::endl; + std::cout << "Reversed? " << !isCCW << std::endl; std::cout << "Start point is " << retained.back() << " pa " << paStart << std::endl; std::cout << "Potential split points:" << std::endl; for (std::size_t i = 0; i < splitPoints.size(); i++) { @@ -215,7 +219,7 @@ prepareArcPoints(const CircularArc& arc, std::vector splitPoints } #if DEBUG_NODABLE_ARC_STRING - std::cout << "Retained split points:" << std::endl; + std::cout << "Retained split points: (splitStart " << splitStart << " " << " splitEnd " << splitEnd << ")" << std::endl; for (std::size_t i = 0; i < retained.size(); i++) { std::cout << " " << retained[i] << " paDiff " << pseudoAngleDiffCCW(paStart, geom::Quadrant::pseudoAngle(center, retained[i])); if (i > 0) { @@ -282,6 +286,9 @@ NodableArcString::getNoded(std::vector>& splitArcs) { ArcBuilder builder(splitArcs, getData(), m_constructZ, m_constructM); for (size_t arcIndex = 0; arcIndex < m_arcs.size(); arcIndex++) { +#if DEBUG_NODABLE_ARC_STRING + std::cout << "arc " << arcIndex + 1 << " / " << m_arcs.size(); +#endif const CircularArc& toSplit = m_arcs[arcIndex]; const geom::CoordinateXY& center = toSplit.getCenter(); const double radius = toSplit.getRadius(); @@ -332,7 +339,6 @@ NodableArcString::getNoded(std::vector>& splitArcs) { const bool isSplitPoint = (i < split.points.size() - 1) || split.splitEnd; if (isSplitPoint) { builder.finish(); - } } } diff --git a/tests/xmltester/tests/general/TestOverlayCurvesAA.xml b/tests/xmltester/tests/general/TestOverlayCurvesAA.xml index 0629b027f..d47e974cb 100644 --- a/tests/xmltester/tests/general/TestOverlayCurvesAA.xml +++ b/tests/xmltester/tests/general/TestOverlayCurvesAA.xml @@ -1,4 +1,34 @@ + + AA - simple polygons + + CURVEPOLYGON(COMPOUNDCURVE(CIRCULARSTRING (10 100, 100 10, 100 60), (100 60, 10 100))) + + + CURVEPOLYGON(COMPOUNDCURVE((50 50, 200 50, 200 200, 50 200), CIRCULARSTRING (50 200, -9 100, 50 50))) + + + + CURVEPOLYGON (COMPOUNDCURVE (CIRCULARSTRING (-5.521402634079415 91.63934964816265, 1.9736670312808187 96.31281538666524, 10 100), (10 100, 100 60), CIRCULARSTRING (100 60, 101.69137093859678 55.056446403350975, 103.00735254367723 50), (103.00735254367723 50, 50 50), CIRCULARSTRING (50 50, 17.21604286900091 64.12172658671918, -5.521402634079415 91.63934964816265))) + + + + + CURVEPOLYGON (COMPOUNDCURVE (CIRCULARSTRING (-5.521402634079415 91.63934964816265, -4.851528401625146 159.70036595476157, 50 200), (50 200, 200 200, 200 50, 103.00735254367723 50), CIRCULARSTRING (103.00735254367723 50, 10.05349282186549 -30.02054890272217, -5.521402634079415 91.63934964816265))) + + + + + CURVEPOLYGON (COMPOUNDCURVE (CIRCULARSTRING (-5.521402634079415 91.63934964816265, 17.21604286900091 64.12172658671918, 50 50), (50 50, 103.00735254367723 50), CIRCULARSTRING (103.00735254367723 50, 10.05349282186549 -30.02054890272217, -5.521402634079415 91.63934964816265))) + + + + + MULTISURFACE (CURVEPOLYGON (COMPOUNDCURVE (CIRCULARSTRING (-5.521402634079415 91.63934964816265, 17.21604286900091 64.12172658671918, 50 50), (50 50, 103.00735254367723 50), CIRCULARSTRING (103.00735254367723 50, 10.05349282186549 -30.02054890272217, -5.521402634079415 91.63934964816265))), CURVEPOLYGON (COMPOUNDCURVE (CIRCULARSTRING (-5.521402634079415 91.63934964816265, -4.851528401625146 159.70036595476157, 50 200), (50 200, 200 200, 200 50, 103.00735254367723 50), CIRCULARSTRING (103.00735254367723 50, 101.69137093859678 55.056446403350975, 100 60), (100 60, 10 100), CIRCULARSTRING (10 100, 1.9736670312808187 96.31281538666524, -5.521402634079415 91.63934964816265)))) + + + + AA - A with hole intersecting B @@ -12,5 +42,82 @@ CURVEPOLYGON (COMPOUNDCURVE ((80 136.50148808205722, 80 160), (80 160, 160 160, 160 100, 127.04025757937788 100), CIRCULARSTRING (127.04025757937788 100, 106.68619012043186 122.3309157911624, 80 136.50148808205722))) + + + CURVEPOLYGON ((20 20, 20 160, 80 160, 80 240, 220 240, 220 100, 160 100, 160 20, 20 20), COMPOUNDCURVE ((40 40, 140 40), CIRCULARSTRING (140 40, 138.89595632376427 71.16115566898841, 127.04025757937788 100), (127.04025757937788 100, 80 100, 80 136.50148808205722), CIRCULARSTRING (80 136.50148808205722, 60.20129186630378 140.55220101502292, 40 140), (40 140, 40 40))) + + + + + CURVEPOLYGON (COMPOUNDCURVE ((20 20, 20 160, 80 160, 80 136.50148808205722), CIRCULARSTRING (80 136.50148808205722, 60.20129186630378 140.55220101502292, 40 140), (40 140, 40 40, 140 40), CIRCULARSTRING (140 40, 138.89595632376427 71.16115566898841, 127.04025757937788 100), (127.04025757937788 100, 160 100, 160 20, 20 20))) + + + + + MULTISURFACE (CURVEPOLYGON (COMPOUNDCURVE ((20 20, 20 160, 80 160, 80 136.50148808205722), CIRCULARSTRING (80 136.50148808205722, 60.20129186630378 140.55220101502292, 40 140), (40 140, 40 40, 140 40), CIRCULARSTRING (140 40, 138.89595632376427 71.16115566898841, 127.04025757937788 100), (127.04025757937788 100, 160 100, 160 20, 20 20))), CURVEPOLYGON (COMPOUNDCURVE ((80 100, 80 136.50148808205722), CIRCULARSTRING (80 136.50148808205722, 106.68619012043186 122.3309157911624, 127.04025757937788 100), (127.04025757937788 100, 80 100))), ((80 160, 80 240, 220 240, 220 100, 160 100, 160 160, 80 160))) + + + + + AA - simple polygons #2 + + CURVEPOLYGON (COMPOUNDCURVE((20 340, 330 380, 50 40), CIRCULARSTRING (50 40, 70 220, 20 340))) + + + + POLYGON( + (210 320, 140 270, 0 270, 140 220, 210 320)) + + + + CURVEPOLYGON (COMPOUNDCURVE ((56.278836074826415 270, 140 270, 210 320, 140 220, 63.6291020022965 247.27532071346553), CIRCULARSTRING (63.6291020022965 247.27532071346553, 60.17196289508064 258.70817016660806, 56.278836074826415 270))) + + + + + CURVEPOLYGON (COMPOUNDCURVE ((0 270, 56.278836074826415 270), CIRCULARSTRING (56.278836074826415 270, 40.36422766234557 306.1530500525802, 20 340), (20 340, 330 380, 50 40), CIRCULARSTRING (50 40, 74.61109542602588 142.46747319494153, 63.6291020022965 247.27532071346553), (63.6291020022965 247.27532071346553, 0 270))) + + + + + CURVEPOLYGON (COMPOUNDCURVE ((20 340, 330 380, 50 40), CIRCULARSTRING (50 40, 74.61109542602588 142.46747319494153, 63.6291020022965 247.27532071346553), (63.6291020022965 247.27532071346553, 140 220, 210 320, 140 270, 56.278836074826415 270), CIRCULARSTRING (56.278836074826415 270, 40.36422766234557 306.1530500525802, 20 340))) + + + + + MULTISURFACE (CURVEPOLYGON (COMPOUNDCURVE ((20 340, 330 380, 50 40), CIRCULARSTRING (50 40, 74.61109542602588 142.46747319494153, 63.6291020022965 247.27532071346553), (63.6291020022965 247.27532071346553, 140 220, 210 320, 140 270, 56.278836074826415 270), CIRCULARSTRING (56.278836074826415 270, 40.36422766234557 306.1530500525802, 20 340))), CURVEPOLYGON (COMPOUNDCURVE ((0 270, 56.278836074826415 270), CIRCULARSTRING (56.278836074826415 270, 60.17196289508064 258.70817016660806, 63.6291020022965 247.27532071346553), (63.6291020022965 247.27532071346553, 0 270)))) + + + + + + AA - simple polygons intersecting in P, L and A + + CURVEPOLYGON(COMPOUNDCURVE((0 0, 110 0), CIRCULARSTRING (110 0, 90 20, 110 60), (110 60, 40 60, 100 100), CIRCULARSTRING (100 100, 150 150, 100 200), (100 200, 40 220, 110 260), CIRCULARSTRING (110 260, 50 280, 0 260, -50 130, 0 0))) + + + CURVEPOLYGON(COMPOUNDCURVE(CIRCULARSTRING(220 0, 120 20, 110 0, 90 20, 110 60), (110 60, 180 60, 100 150, 180 220, 110 260, 220 260, 220 0))) + + + + GEOMETRYCOLLECTION (CURVEPOLYGON (COMPOUNDCURVE ((100 150, 137.6288347353439 182.92523039342592), CIRCULARSTRING (137.6288347353439 182.92523039342592, 149.90186898086148 146.8689503011078, 133.218191941496 112.62953406581698), (133.218191941496 112.62953406581698, 100 150))), CIRCULARSTRING (110 0, 90 20, 110 60), POINT (110 260)) + + + + + CURVEPOLYGON (COMPOUNDCURVE (CIRCULARSTRING (0 0, -50 130, 0 260, 50 280, 110 260), (110 260, 220 260, 220 0), CIRCULARSTRING (220 0, 120 20, 110 0), (110 0, 0 0)), COMPOUNDCURVE ((40 60, 110 60, 180 60, 133.218191941496 112.62953406581698), CIRCULARSTRING (133.218191941496 112.62953406581698, 117.76902787564433 103.26391492267913, 100 100), (100 100, 40 60)), COMPOUNDCURVE ((40 220, 100 200), CIRCULARSTRING (100 200, 120.66081412152852 195.5316456965444, 137.6288347353439 182.92523039342592), (137.6288347353439 182.92523039342592, 180 220, 110 260), (110 260, 40 220))) + + + + + CURVEPOLYGON (COMPOUNDCURVE (CIRCULARSTRING (0 0, -50 130, 0 260, 50 280, 110 260), (110 260, 40 220, 100 200), CIRCULARSTRING (100 200, 120.66081412152852 195.5316456965444, 137.6288347353439 182.92523039342592), (137.6288347353439 182.92523039342592, 100 150, 133.218191941496 112.62953406581698), CIRCULARSTRING (133.218191941496 112.62953406581698, 117.76902787564433 103.26391492267913, 100 100), (100 100, 40 60, 110 60), CIRCULARSTRING (110 60, 90 20, 110 0), (110 0, 0 0))) + + + + + CURVEPOLYGON (COMPOUNDCURVE (CIRCULARSTRING (0 0, -50 130, 0 260, 50 280, 110 260), (110 260, 220 260, 220 0), CIRCULARSTRING (220 0, 120 20, 110 0), (110 0, 0 0)), COMPOUNDCURVE ((40 60, 110 60, 180 60, 133.218191941496 112.62953406581698), CIRCULARSTRING (133.218191941496 112.62953406581698, 117.76902787564433 103.26391492267913, 100 100), (100 100, 40 60)), COMPOUNDCURVE ((100 150, 133.218191941496 112.62953406581698), CIRCULARSTRING (133.218191941496 112.62953406581698, 149.90186898086148 146.8689503011078, 137.6288347353439 182.92523039342592), (137.6288347353439 182.92523039342592, 100 150)), COMPOUNDCURVE ((40 220, 100 200), CIRCULARSTRING (100 200, 120.66081412152852 195.5316456965444, 137.6288347353439 182.92523039342592), (137.6288347353439 182.92523039342592, 180 220, 110 260), (110 260, 40 220))) + + + commit 92b27f72feeda5a27e3747abfda79c071a1fb4d4 Author: Daniel Baston Date: Fri Jul 10 13:15:37 2026 -0400 XMLTester: skip validity testing for curved results diff --git a/tests/xmltester/XMLTester.cpp b/tests/xmltester/XMLTester.cpp index 0ead98773..e285ec4d9 100644 --- a/tests/xmltester/XMLTester.cpp +++ b/tests/xmltester/XMLTester.cpp @@ -479,16 +479,29 @@ bool XMLTester::testValid(const geom::Geometry* g, const std::string& label) { operation::valid::IsValidOp ivo(g); - bool valid = ivo.isValid(); - if(! valid) { - const TopologyValidationError* err = ivo.getValidationError(); - std::cerr << *curr_file << ":" - << " case" << caseCount << ":" - << " test" << testCount << ": " - << " invalid geometry (" << label - << "): " << err->toString() << std::endl; + try { + bool valid = ivo.isValid(); + + if(! valid) { + const TopologyValidationError* err = ivo.getValidationError(); + std::cerr << *curr_file << ":" + << " case" << caseCount << ":" + << " test" << testCount << ": " + << " invalid geometry (" << label + << "): " << err->toString() << std::endl; + } + + return valid; + } catch (const geos::util::UnsupportedOperationException& ex) { + if (g->hasCurvedComponents()) { + const std::string message(ex.what()); + if (message.find("Curved types not supported") != std::string::npos) { + return true; + } + } + + throw; } - return valid; } /** commit 7f1c5dca809b3dc4b6336ab3474011a1a57dd2ba Author: Daniel Baston Date: Fri Jul 10 13:04:36 2026 -0400 XMLTester: treat unexpected exceptions as test failures diff --git a/tests/xmltester/XMLTester.cpp b/tests/xmltester/XMLTester.cpp index 16b1690aa..0ead98773 100644 --- a/tests/xmltester/XMLTester.cpp +++ b/tests/xmltester/XMLTester.cpp @@ -1015,6 +1015,7 @@ void Test::execute(Geometry* geomA, Geometry* geomB) else { std::cerr << "EXCEPTION in " << tester.testcaseRef() << ": " << e.what() << std::endl; + isSuccess = false; actualResultStr = e.what(); } } @@ -1022,6 +1023,7 @@ void Test::execute(Geometry* geomA, Geometry* geomB) std::cerr << "Unknown EXCEPTION in " << tester.testcaseRef() << std::endl; + isSuccess = false; actualResultStr = "Unknown exception thrown"; } } ----------------------------------------------------------------------- Summary of changes: src/noding/NodableArcString.cpp | 14 ++- tests/unit/noding/NodableArcStringTest.cpp | 53 ++++++++++ tests/xmltester/XMLTester.cpp | 51 +++++++--- .../tests/general/TestOverlayCurvesAA.xml | 107 +++++++++++++++++++++ 4 files changed, 206 insertions(+), 19 deletions(-) hooks/post-receive -- GEOS From git at osgeo.org Tue Jul 14 10:54:15 2026 From: git at osgeo.org (git at osgeo.org) Date: Tue, 14 Jul 2026 10:54:15 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. ad2d66fb863fdfdc213dc6f07d8349e839bf801d Message-ID: <20260714175415.7CAAA1AD7A0@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via ad2d66fb863fdfdc213dc6f07d8349e839bf801d (commit) from 1d559c659e0862d5e4ff738391a9258d048d68b9 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit ad2d66fb863fdfdc213dc6f07d8349e839bf801d Author: Daniel Baston Date: Tue Jul 14 13:53:53 2026 -0400 XMLTester: add --test-index and --case-index arguments (#1477) diff --git a/tests/xmltester/XMLTester.cpp b/tests/xmltester/XMLTester.cpp index 3e4aac3a0..c493890de 100644 --- a/tests/xmltester/XMLTester.cpp +++ b/tests/xmltester/XMLTester.cpp @@ -419,11 +419,17 @@ XMLTester::parseRun(const tinyxml2::XMLNode* node) wkbwriter.reset(new io::WKBWriter()); const tinyxml2::XMLNode* casenode; + + int icase = 1; + for(casenode = node->FirstChildElement("case"); casenode; casenode = casenode->NextSiblingElement("case")) { try { - parseCase(casenode); + if (caseNum == 0 || icase == caseNum) { + parseCase(casenode); + } + icase++; } catch(const std::exception& exc) { std::cerr << exc.what() << std::endl; @@ -592,12 +598,16 @@ XMLTester::parseCase(const tinyxml2::XMLNode* node) ++caseCount; testCount = 0; + int itest = 1; const tinyxml2::XMLNode* testnode; for(testnode = node->FirstChildElement("test"); testnode; testnode = testnode->NextSiblingElement("test")) { - runTest(testnode); + if (testNum == 0 || itest == testNum) { + runTest(testnode); + } + itest++; } totalTestCount += testCount; @@ -1426,7 +1436,9 @@ usage(char* me, int exitcode, std::ostream& os) << "--test-valid-output Test output validity" << std::endl << "--test-valid-input Test input validity" << std::endl << "--sql-output Produce SQL output" << std::endl - << "--wkb-output Print Geometries as HEXWKB" << std::endl; + << "--wkb-output Print Geometries as HEXWKB" << std::endl + << "--case-index [n] Run only the specified case number (1-indexed)" << std::endl + << "--test-index [n] Run only the specified test number (1-indexed)" << std::endl; std::exit(exitcode); } @@ -1482,6 +1494,16 @@ main(int argC, char* argV[]) tester.setTestInputValidity(true); continue; } + if (!std::strcmp(argV[i], "--case-index")) { + int caseNum = std::atoi(argV[++i]); + tester.setCaseNumber(caseNum); + continue; + } + if (!std::strcmp(argV[i], "--test-index")) { + int opNum = std::atoi(argV[++i]); + tester.setTestNumber(opNum); + continue; + } std::string source = argV[i]; diff --git a/tests/xmltester/XMLTester.h b/tests/xmltester/XMLTester.h index 4a10b9fb1..170bb5d18 100644 --- a/tests/xmltester/XMLTester.h +++ b/tests/xmltester/XMLTester.h @@ -56,6 +56,9 @@ private: int testFileCount; int totalTestCount; + int caseNum = 0; + int testNum = 0; + const std::string* curr_file; std::string curr_case_desc; @@ -89,6 +92,14 @@ public: */ int setVerbosityLevel(int val); + void setCaseNumber(int num) { + caseNum = num; + } + + void setTestNumber(int num) { + testNum = num; + } + void setTestOutputValidity(bool val) { testValidOutput = val; ----------------------------------------------------------------------- Summary of changes: tests/xmltester/XMLTester.cpp | 28 +++++++++++++++++++++++++--- tests/xmltester/XMLTester.h | 11 +++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) hooks/post-receive -- GEOS From git at osgeo.org Tue Jul 14 13:36:16 2026 From: git at osgeo.org (git at osgeo.org) Date: Tue, 14 Jul 2026 13:36:16 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. 34c654fe1b772d0a54dbf64e70d99643ff032181 Message-ID: <20260714203616.67D7B1AE320@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via 34c654fe1b772d0a54dbf64e70d99643ff032181 (commit) from ad2d66fb863fdfdc213dc6f07d8349e839bf801d (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 34c654fe1b772d0a54dbf64e70d99643ff032181 Author: Paul Ramsey Date: Tue Jul 14 13:35:53 2026 -0700 Bump version forward during release diff --git a/Version.txt b/Version.txt index 46e851682..d419677e0 100644 --- a/Version.txt +++ b/Version.txt @@ -5,7 +5,7 @@ GEOS_VERSION_MINOR=15 GEOS_VERSION_PATCH=0 # OPTIONS: "", "dev", "rc1" etc. -GEOS_PATCH_WORD=beta2 +GEOS_PATCH_WORD=beta3 # GEOS CAPI Versions # ----------------------------------------------------------------------- Summary of changes: Version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) hooks/post-receive -- GEOS From git at osgeo.org Tue Jul 14 17:24:55 2026 From: git at osgeo.org (git at osgeo.org) Date: Tue, 14 Jul 2026 17:24:55 -0700 (PDT) Subject: [geos-commits] [SCM] GEOS branch main updated. fdc70563162eb1784929c68263f4ad67ce809761 Message-ID: <20260715002455.E62B61AF8B8@trac.osgeo.org> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GEOS". The branch, main has been updated via fdc70563162eb1784929c68263f4ad67ce809761 (commit) from 34c654fe1b772d0a54dbf64e70d99643ff032181 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit fdc70563162eb1784929c68263f4ad67ce809761 Author: Paul Ramsey Date: Tue Jul 14 17:24:29 2026 -0700 update download link diff --git a/web/content/usage/download.md b/web/content/usage/download.md index 2958dfd0d..34f0bf807 100644 --- a/web/content/usage/download.md +++ b/web/content/usage/download.md @@ -8,7 +8,7 @@ draft: false | Release | Release Date | First Release | Final Release | Download Link | Changes | | :--: | :--:| :--: |:--: | :--: | :--: | -| **3.15.0beta1** | 2026/07/07 | 2026/07/07 | *2030/10/01* | [geos-3.15.0beta1.tar.bz2](https://download.osgeo.org/geos/geos-3.15.0beta1.tar.bz2) | [Changes](https://github.com/libgeos/geos/blob/3.15.0beta1/NEWS.md) | +| **3.15.0beta2** | 2026/07/14 | 2026/07/07 | *2030/10/01* | [geos-3.15.0beta2.tar.bz2](https://download.osgeo.org/geos/geos-3.15.0beta2.tar.bz2) | [Changes](https://github.com/libgeos/geos/blob/3.15.0beta2/NEWS.md) | | **3.14.1** | 2025/10/27 | 2025/08/21 | *2029/10/01* | [geos-3.14.1.tar.bz2](https://download.osgeo.org/geos/geos-3.14.1.tar.bz2) | [Changes](https://github.com/libgeos/geos/blob/3.14.1/NEWS.md) | | **3.13.1** | 2025/03/03 | 2024/09/06 | *2028/10/01* | [geos-3.13.1.tar.bz2](https://download.osgeo.org/geos/geos-3.13.1.tar.bz2) | [Changes](https://github.com/libgeos/geos/blob/3.13.1/NEWS.md) | | **3.12.3** | 2025/03/03 | 2023/06/27 | *2027/07/01* | [geos-3.12.3.tar.bz2](https://download.osgeo.org/geos/geos-3.12.3.tar.bz2) | [Changes](https://github.com/libgeos/geos/blob/3.12.3/NEWS.md) | ----------------------------------------------------------------------- Summary of changes: web/content/usage/download.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) hooks/post-receive -- GEOS