diff --git a/.github/workflows/alpine/Dockerfile.ci b/.github/workflows/alpine/Dockerfile.ci index 7adca61bd2fc..2506ff2739da 100644 --- a/.github/workflows/alpine/Dockerfile.ci +++ b/.github/workflows/alpine/Dockerfile.ci @@ -62,7 +62,6 @@ RUN apk add \ sfcgal-dev \ snappy-dev \ sqlite-dev \ - swig \ tiledb-dev \ tiff-dev \ unixodbc-dev \ @@ -73,3 +72,11 @@ RUN apk add \ COPY requirements.txt /tmp/ RUN python3 -m pip install --break-system-packages -U -r /tmp/requirements.txt + +RUN apk add git autoconf automake libtool bison && \ + git clone --branch "${SWIG_GIT_TAG:-master}" --depth 1 https://github.com/swig/swig.git swig-git && \ + cd swig-git && \ + ./autogen.sh && \ + ./configure --prefix=/usr && \ + make -j$(nproc) && \ + make install diff --git a/.github/workflows/alpine/build.sh b/.github/workflows/alpine/build.sh index e7461b79f0eb..54a36123c11c 100755 --- a/.github/workflows/alpine/build.sh +++ b/.github/workflows/alpine/build.sh @@ -21,7 +21,8 @@ cmake ${GDAL_SOURCE_DIR:=..} \ -DIconv_INCLUDE_DIR=/usr/include/gnu-libiconv \ -DIconv_LIBRARY=/usr/lib/libiconv.so \ -DADD_EXTERNAL_DEFERRED_PLUGIN_FOO=/tmp/foo.cpp \ - -DCMAKE_C_FLAGS=-Werror -DCMAKE_CXX_FLAGS="-std=c++23 -Werror" -DWERROR_DEV_FLAG="-Werror=dev" + -DCMAKE_CXX_STANDARD=23 \ + -DCMAKE_C_FLAGS=-Werror -DCMAKE_CXX_FLAGS="-Werror" -DWERROR_DEV_FLAG="-Werror=dev" make -j$(nproc) make -j$(nproc) install DESTDIR=/tmp/install-gdal <<<<<<< HEAD diff --git a/.github/workflows/android_cmake.yml b/.github/workflows/android_cmake.yml index 55a556debb9a..8c481771551a 100644 --- a/.github/workflows/android_cmake.yml +++ b/.github/workflows/android_cmake.yml @@ -6,6 +6,7 @@ on: - 'doc/**' branches-ignore: - 'backport**' + - 'dependabot**' pull_request: paths-ignore: - 'doc/**' diff --git a/.github/workflows/clang_static_analyzer.yml b/.github/workflows/clang_static_analyzer.yml index 07965bbb2675..d769bd66362d 100644 --- a/.github/workflows/clang_static_analyzer.yml +++ b/.github/workflows/clang_static_analyzer.yml @@ -6,6 +6,7 @@ on: - 'doc/**' branches-ignore: - 'backport**' + - 'dependabot**' pull_request: paths-ignore: - 'doc/**' diff --git a/.github/workflows/cmake_builds.yml b/.github/workflows/cmake_builds.yml index 6b5569d3611b..88c503b0780a 100644 --- a/.github/workflows/cmake_builds.yml +++ b/.github/workflows/cmake_builds.yml @@ -8,6 +8,7 @@ on: - 'doc/**' branches-ignore: - 'backport**' + - 'dependabot**' pull_request: paths-ignore: - 'doc/**' @@ -93,7 +94,7 @@ jobs: # Workaround bug in ogdi packaging sudo ln -s /usr/lib/ogdi/libvrf.so /usr/lib # - python3 -m pip install -U pip wheel setuptools numpy + python3 -m pip install -U pip wheel setuptools numpy importlib_metadata python3 -m pip install -r $GITHUB_WORKSPACE/autotest/requirements.txt - name: Build libjxl @@ -421,7 +422,7 @@ jobs: - name: Install dependency shell: bash -l {0} run: | - conda install --yes --quiet curl libiconv icu python=3.10 swig numpy pytest pytest-env pytest-benchmark filelock zlib lxml jsonschema + conda install --yes --quiet curl libiconv icu python=3.10 swig numpy pytest pytest-env pytest-benchmark filelock zlib lxml jsonschema setuptools # FIXME: remove libnetcdf=4.9.2=nompi_h5902ca5_107 pinning as soon as https://github.com/conda-forge/libnetcdf-feedstock/issues/182 is resolved conda install --yes --quiet proj geos hdf4 hdf5 kealib \ libnetcdf=4.9.2=nompi_h5902ca5_107 openjpeg poppler libtiff libpng xerces-c expat libxml2 kealib json-c \ @@ -453,37 +454,43 @@ jobs: # otherwise interpret /bla has a file relative to the Bash root directory and would replace it by a path like c:\Program Files\git\WX # BUILD_JAVA_BINDINGS=OFF because we get "Error occurred during initialization of VM. Corrupted ZIP library: C:\Miniconda\envs\gdalenv\Library\bin\zip.dll" when running java. Not reproducible on a standard VM # Build PDF driver as plugin due to the PDFium build including libopenjp2 symbols which would conflict with external libopenjp2 + # /D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR to workaround issue with too old vcruntime140.dll on image 20240603.1.0 (https://github.com/actions/runner-images/issues/10004) run: | mkdir -p $GITHUB_WORKSPACE/build - cmake -G "${generator}" -Werror=dev "-DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/install-gdal" "-DUSE_CCACHE=ON" "-DCMAKE_PREFIX_PATH=${CONDA}/envs/gdalenv" -DCMAKE_UNITY_BUILD=${CMAKE_UNITY_BUILD} -S "$GITHUB_WORKSPACE" -B "$GITHUB_WORKSPACE/build" -DGDAL_ENABLE_PLUGINS:BOOL=ON -DGDAL_ENABLE_PLUGINS_NO_DEPS:BOOL=ON -DGDAL_USE_PUBLICDECOMPWT:BOOL=ON -DPUBLICDECOMPWT_URL=https://github.com/rouault/PublicDecompWT -DBUILD_JAVA_BINDINGS=OFF -DBUILD_CSHARP_BINDINGS=ON -DGDAL_USE_MYSQL:BOOL=OFF -DCMAKE_C_FLAGS=" /WX" -DCMAKE_CXX_FLAGS=" /WX" -DWERROR_DEV_FLAG="-Werror=dev" -DCMAKE_BUILD_TYPE=Release -DPDFIUM_ROOT=$GITHUB_WORKSPACE/install-pdfium -DGDAL_ENABLE_DRIVER_PDF_PLUGIN:BOOL=ON -DCMAKE_UNITY_BUILD=ON + cmake -G "${generator}" -Werror=dev "-DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/install-gdal" "-DUSE_CCACHE=ON" "-DCMAKE_PREFIX_PATH=${CONDA}/envs/gdalenv" -DCMAKE_UNITY_BUILD=${CMAKE_UNITY_BUILD} -S "$GITHUB_WORKSPACE" -B "$GITHUB_WORKSPACE/build" -DGDAL_ENABLE_PLUGINS:BOOL=ON -DGDAL_ENABLE_PLUGINS_NO_DEPS:BOOL=ON -DGDAL_USE_PUBLICDECOMPWT:BOOL=ON -DPUBLICDECOMPWT_URL=https://github.com/rouault/PublicDecompWT -DBUILD_JAVA_BINDINGS=OFF -DBUILD_CSHARP_BINDINGS=ON -DGDAL_USE_MYSQL:BOOL=OFF -DCMAKE_C_FLAGS=" /WX" -DCMAKE_CXX_FLAGS=" /WX /D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR" -DWERROR_DEV_FLAG="-Werror=dev" -DCMAKE_BUILD_TYPE=Release -DPDFIUM_ROOT=$GITHUB_WORKSPACE/install-pdfium -DGDAL_ENABLE_DRIVER_PDF_PLUGIN:BOOL=ON -DCMAKE_UNITY_BUILD=ON - name: Build shell: bash -l {0} run: cmake --build $GITHUB_WORKSPACE/build --config Release -j 2 env: GIT_LFS_SKIP_SMUDGE: 1 # for PublicDecompWT github repository clone - - name: test - shell: bash -l {0} - run: | - cmake --build $GITHUB_WORKSPACE/build --config Release --target quicktest - - name: test (with ctest) - shell: bash -l {0} - run: | - ctest --test-dir $GITHUB_WORKSPACE/build -C Release -V -j 3 - env: - SKIP_OGR_GMLAS_HUGE_PROCESSING_TIME: YES - SKIP_OGR_GMLAS_HTTP_RELATED: YES - SKIP_GDAL_HTTP_SSL_VERIFYSTATUS: YES - BUILD_NAME: "build-windows-conda" + # FIXME !! Disabled because of actions/runner-images#10004 + #- name: test + # shell: bash -l {0} + # run: | + # cmake --build $GITHUB_WORKSPACE/build --config Release --target quicktest + #- name: test (with ctest) + # shell: bash -l {0} + # run: | + # ctest --test-dir $GITHUB_WORKSPACE/build -C Release -V -j 3 + # env: + # SKIP_OGR_GMLAS_HUGE_PROCESSING_TIME: YES + # SKIP_OGR_GMLAS_HTTP_RELATED: YES + # SKIP_GDAL_HTTP_SSL_VERIFYSTATUS: YES + # BUILD_NAME: "build-windows-conda" - name: Install shell: bash -l {0} run: | cmake --build $GITHUB_WORKSPACE/build --config Release --target install - export PATH=$GITHUB_WORKSPACE/install-gdal/bin:$PATH - gdalinfo --version - python -VV - PYTHONPATH=$GITHUB_WORKSPACE/install-gdal/lib/site-packages python -c "from osgeo import gdal;print(gdal.VersionInfo(None))" - export PATH=$GITHUB_WORKSPACE/install-gdal/Scripts:$PATH - PYTHONPATH=$GITHUB_WORKSPACE/install-gdal/lib/site-packages gdal_edit --version + # FIXME !! Disabled because of actions/runner-images#10004 + #- name: Test install + # shell: bash -l {0} + # run: | + # export PATH=$GITHUB_WORKSPACE/install-gdal/bin:$PATH + # gdalinfo --version + # python -VV + # PYTHONPATH=$GITHUB_WORKSPACE/install-gdal/lib/site-packages python -c "from osgeo import gdal;print(gdal.VersionInfo(None))" + # export PATH=$GITHUB_WORKSPACE/install-gdal/Scripts:$PATH + # PYTHONPATH=$GITHUB_WORKSPACE/install-gdal/lib/site-packages gdal_edit --version - name: Show gdal.pc shell: bash -l {0} run: cat $GITHUB_WORKSPACE/build/gdal.pc @@ -518,16 +525,17 @@ jobs: - name: Install dependency shell: bash -l {0} run: | - conda install --yes --quiet proj pytest pytest-env pytest-benchmark filelock lxml cmake + conda install --yes --quiet proj pytest pytest-env pytest-benchmark filelock lxml cmake setuptools - name: Check CMake version shell: bash -l {0} run: | cmake --version - name: Configure shell: bash -l {0} + # /D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR to workaround issue with too old vcruntime140.dll on image 20240603.1.0 (https://github.com/actions/runner-images/issues/10004) run: | mkdir -p $GITHUB_WORKSPACE/build - cmake -A ${architecture} -G "${generator}" "-DCMAKE_PREFIX_PATH=${CONDA}/envs/gdalenv" -Werror=dev "-DCMAKE_CXX_COMPILER_LAUNCHER=clcache" -DCMAKE_UNITY_BUILD=${CMAKE_UNITY_BUILD} -S "$GITHUB_WORKSPACE" -B "$GITHUB_WORKSPACE/build" -DCMAKE_C_FLAGS=" /WX" -DCMAKE_CXX_FLAGS=" /WX" -DGDAL_USE_EXTERNAL_LIBS:BOOL=OFF -DWERROR_DEV_FLAG="-Werror=dev" + cmake -A ${architecture} -G "${generator}" "-DCMAKE_PREFIX_PATH=${CONDA}/envs/gdalenv" -Werror=dev "-DCMAKE_CXX_COMPILER_LAUNCHER=clcache" -DCMAKE_UNITY_BUILD=${CMAKE_UNITY_BUILD} -S "$GITHUB_WORKSPACE" -B "$GITHUB_WORKSPACE/build" -DCMAKE_C_FLAGS=" /WX" -DCMAKE_CXX_FLAGS=" /WX /D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR" -DGDAL_USE_EXTERNAL_LIBS:BOOL=OFF -DWERROR_DEV_FLAG="-Werror=dev" - name: Build shell: bash -l {0} run: cmake --build $GITHUB_WORKSPACE/build --config RelWithDebInfo -j 2 @@ -535,22 +543,23 @@ jobs: shell: bash -l {0} run: | rm -f build/CMakeCache.txt - cmake -A ${architecture} -G "${generator}" "-DCMAKE_PREFIX_PATH=${CONDA}/envs/gdalenv" -Werror=dev "-DCMAKE_CXX_COMPILER_LAUNCHER=clcache" -DCMAKE_UNITY_BUILD=${CMAKE_UNITY_BUILD} -S "$GITHUB_WORKSPACE" -B "$GITHUB_WORKSPACE/build" -DCMAKE_C_FLAGS=" /WX" -DCMAKE_CXX_FLAGS=" /WX" -DGDAL_USE_EXTERNAL_LIBS:BOOL=OFF -DGDAL_USE_PNG_INTERNAL=OFF -DGDAL_USE_JPEG_INTERNAL=OFF -DGDAL_USE_JPEG12_INTERNAL=OFF -DGDAL_USE_GIF_INTERNAL=OFF -DGDAL_USE_LERC_INTERNAL=OFF -DGDAL_USE_LERCV1_INTERNAL=OFF -DGDAL_USE_QHULL_INTERNAL=OFF -DGDAL_USE_OPENCAD_INTERNAL=OFF -DWERROR_DEV_FLAG="-Werror=dev" + cmake -A ${architecture} -G "${generator}" "-DCMAKE_PREFIX_PATH=${CONDA}/envs/gdalenv" -Werror=dev "-DCMAKE_CXX_COMPILER_LAUNCHER=clcache" -DCMAKE_UNITY_BUILD=${CMAKE_UNITY_BUILD} -S "$GITHUB_WORKSPACE" -B "$GITHUB_WORKSPACE/build" -DCMAKE_C_FLAGS=" /WX" -DCMAKE_CXX_FLAGS=" /WX /D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR" -DGDAL_USE_EXTERNAL_LIBS:BOOL=OFF -DGDAL_USE_PNG_INTERNAL=OFF -DGDAL_USE_JPEG_INTERNAL=OFF -DGDAL_USE_JPEG12_INTERNAL=OFF -DGDAL_USE_GIF_INTERNAL=OFF -DGDAL_USE_LERC_INTERNAL=OFF -DGDAL_USE_LERCV1_INTERNAL=OFF -DGDAL_USE_QHULL_INTERNAL=OFF -DGDAL_USE_OPENCAD_INTERNAL=OFF -DWERROR_DEV_FLAG="-Werror=dev" - name: Configure with even less dependencies, and disabling all optional drivers shell: bash -l {0} run: | rm -f build/CMakeCache.txt - cmake -A ${architecture} -G "${generator}" "-DCMAKE_PREFIX_PATH=${CONDA}/envs/gdalenv" -Werror=dev "-DCMAKE_CXX_COMPILER_LAUNCHER=clcache" -DCMAKE_UNITY_BUILD=${CMAKE_UNITY_BUILD} -S "$GITHUB_WORKSPACE" -B "$GITHUB_WORKSPACE/build" -DCMAKE_C_FLAGS=" /WX" -DCMAKE_CXX_FLAGS=" /WX" -DGDAL_USE_EXTERNAL_LIBS:BOOL=OFF -DGDAL_USE_PNG_INTERNAL=OFF -DGDAL_USE_JPEG_INTERNAL=OFF -DGDAL_USE_JPEG12_INTERNAL=OFF -DGDAL_USE_GIF_INTERNAL=OFF -DGDAL_USE_LERC_INTERNAL=OFF -DGDAL_USE_LERCV1_INTERNAL=OFF -DGDAL_USE_QHULL_INTERNAL=OFF -DGDAL_USE_OPENCAD_INTERNAL=OFF -DGDAL_BUILD_OPTIONAL_DRIVERS=OFF -DOGR_BUILD_OPTIONAL_DRIVERS=OFF -DWERROR_DEV_FLAG="-Werror=dev" + cmake -A ${architecture} -G "${generator}" "-DCMAKE_PREFIX_PATH=${CONDA}/envs/gdalenv" -Werror=dev "-DCMAKE_CXX_COMPILER_LAUNCHER=clcache" -DCMAKE_UNITY_BUILD=${CMAKE_UNITY_BUILD} -S "$GITHUB_WORKSPACE" -B "$GITHUB_WORKSPACE/build" -DCMAKE_C_FLAGS=" /WX" -DCMAKE_CXX_FLAGS=" /WX /D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR" -DGDAL_USE_EXTERNAL_LIBS:BOOL=OFF -DGDAL_USE_PNG_INTERNAL=OFF -DGDAL_USE_JPEG_INTERNAL=OFF -DGDAL_USE_JPEG12_INTERNAL=OFF -DGDAL_USE_GIF_INTERNAL=OFF -DGDAL_USE_LERC_INTERNAL=OFF -DGDAL_USE_LERCV1_INTERNAL=OFF -DGDAL_USE_QHULL_INTERNAL=OFF -DGDAL_USE_OPENCAD_INTERNAL=OFF -DGDAL_BUILD_OPTIONAL_DRIVERS=OFF -DOGR_BUILD_OPTIONAL_DRIVERS=OFF -DWERROR_DEV_FLAG="-Werror=dev" - name: Build shell: bash -l {0} run: cmake --build $GITHUB_WORKSPACE/build --config RelWithDebInfo -j 2 - - name: test (with ctest) - shell: bash -l {0} - run: | - ctest --test-dir $GITHUB_WORKSPACE/build -C RelWithDebInfo -V -j 3 - env: - SKIP_GDAL_HTTP_SSL_VERIFYSTATUS: YES - BUILD_NAME: "build-windows-minimum" + # FIXME !! Disable tests because of actions/runner-images#10004 + #- name: test (with ctest) + # shell: bash -l {0} + # run: | + # ctest --test-dir $GITHUB_WORKSPACE/build -C RelWithDebInfo -V -j 3 + # env: + # SKIP_GDAL_HTTP_SSL_VERIFYSTATUS: YES + # BUILD_NAME: "build-windows-minimum" - name: Show gdal.pc shell: bash -l {0} run: cat $GITHUB_WORKSPACE/build/gdal.pc diff --git a/.github/workflows/code_checks.yml b/.github/workflows/code_checks.yml index b11e0ceee661..55a1b342742b 100644 --- a/.github/workflows/code_checks.yml +++ b/.github/workflows/code_checks.yml @@ -6,6 +6,7 @@ on: - 'doc/**' branches-ignore: - 'backport**' + - 'dependabot**' pull_request: paths-ignore: - 'doc/**' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3e38e1168049..fca5e1a9c714 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -4,6 +4,9 @@ on: push: paths-ignore: - 'doc/**' + branches-ignore: + - 'backport**' + - 'dependabot**' pull_request: paths-ignore: - 'doc/**' diff --git a/.github/workflows/conda.yml b/.github/workflows/conda.yml index d64da3f0c831..ddb4a2612f8c 100644 --- a/.github/workflows/conda.yml +++ b/.github/workflows/conda.yml @@ -6,6 +6,7 @@ on: - 'doc/**' branches-ignore: - 'backport**' + - 'dependabot**' # Disabled because run is quite slow, especially for Mac #pull_request: diff --git a/.github/workflows/delete_untagged_containers.yml b/.github/workflows/delete_untagged_containers.yml index a31790db89c0..6e0fdc998e41 100644 --- a/.github/workflows/delete_untagged_containers.yml +++ b/.github/workflows/delete_untagged_containers.yml @@ -6,6 +6,7 @@ on: - 'doc/**' branches-ignore: - 'backport**' + - 'dependabot**' permissions: contents: read diff --git a/.github/workflows/doc_build.yml b/.github/workflows/doc_build.yml index 250730b65364..282858e5e00d 100644 --- a/.github/workflows/doc_build.yml +++ b/.github/workflows/doc_build.yml @@ -4,6 +4,7 @@ on: push: branches-ignore: - 'backport**' + - 'dependabot**' pull_request: concurrency: diff --git a/.github/workflows/fedora_rawhide/Dockerfile.ci b/.github/workflows/fedora_rawhide/Dockerfile.ci index 584c58369f73..cb764b75ae00 100644 --- a/.github/workflows/fedora_rawhide/Dockerfile.ci +++ b/.github/workflows/fedora_rawhide/Dockerfile.ci @@ -1,6 +1,9 @@ FROM fedora:rawhide -RUN dnf upgrade -y +# FIXME: Exclude update of dnf&rpm themselves as this results in a no longer working dnf +# cf https://github.com/OSGeo/gdal/actions/runs/9448190401/job/26021669415?pr=10173 +# Likely a transient issue with Fedora 41 dev cycle +RUN dnf upgrade -y -x dnf -x rpm RUN dnf install -y --setopt=install_weak_deps=False proj-devel RUN dnf install -y clang make diffutils ccache cmake \ libxml2-devel libxslt-devel expat-devel xerces-c-devel \ diff --git a/.github/workflows/freebsd.yml.disabled b/.github/workflows/freebsd.yml.disabled index 2ad11b426b73..d6d757e1cd69 100644 --- a/.github/workflows/freebsd.yml.disabled +++ b/.github/workflows/freebsd.yml.disabled @@ -6,6 +6,7 @@ on: - 'doc/**' branches: - '!backport**' + - '!dependabot**' pull_request: paths-ignore: - 'doc/**' diff --git a/.github/workflows/linux_build.yml b/.github/workflows/linux_build.yml index 69f75d555f31..31f52dd7cb5f 100644 --- a/.github/workflows/linux_build.yml +++ b/.github/workflows/linux_build.yml @@ -6,6 +6,7 @@ on: - 'doc/**' branches-ignore: - 'backport**' + - 'dependabot**' pull_request: paths-ignore: - 'doc/**' @@ -75,7 +76,7 @@ jobs: - name: Fedora Rawhide, clang++ id: fedora_rawhide - travis_branch: sanitize + travis_branch: fedora_rawhide container: fedora_rawhide build_script: build.sh os: ubuntu-22.04 @@ -283,6 +284,7 @@ jobs: env: TRAVIS: yes TRAVIS_BRANCH: ${{ matrix.travis_branch }} + BUILD_NAME: ${{ matrix.travis_branch }} run: | if test -f ".github/workflows/${{ matrix.id }}/${{ matrix.test_script }}"; then TEST_CMD="$(pwd)/.github/workflows/${{ matrix.id }}/${{ matrix.test_script }}" @@ -305,6 +307,7 @@ jobs: -e GITHUB_WORKFLOW \ -e TRAVIS \ -e TRAVIS_BRANCH \ + -e BUILD_NAME \ -e "GDAL_SOURCE_DIR=$(pwd)" \ -u $(id -u ${USER}):$(id -g ${USER}) \ --security-opt seccomp=unconfined \ @@ -317,11 +320,13 @@ jobs: ${TEST_CMD} - name: Coveralls - uses: coverallsapp/github-action@3dfc5567390f6fa9267c0ee9c251e4c8c3f18949 # v2.2.3 + uses: coverallsapp/github-action@643bc377ffa44ace6394b2b5d0d3950076de9f63 # v2.3.0 if: ${{ matrix.id == 'coverage' }} with: format: lcov file: build-coverage/gdal_filtered.info + # Pin to v0.6.10 because of issue with v0.6.11 (https://github.com/coverallsapp/coverage-reporter/issues/127) + coverage-reporter-version: v0.6.10 - name: Push build environment if: github.event_name == 'push' diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index d4b5e2087ec9..053cd36c7283 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -6,6 +6,7 @@ on: - 'doc/**' branches-ignore: - 'backport**' + - 'dependabot**' pull_request: paths-ignore: - 'doc/**' diff --git a/.github/workflows/ubuntu_20.04/Dockerfile.ci b/.github/workflows/ubuntu_20.04/Dockerfile.ci index 571bdd4495db..af9107f7e3bb 100644 --- a/.github/workflows/ubuntu_20.04/Dockerfile.ci +++ b/.github/workflows/ubuntu_20.04/Dockerfile.ci @@ -262,4 +262,3 @@ RUN ldconfig COPY requirements.txt /tmp/ RUN python3 -m pip install -U -r /tmp/requirements.txt -RUN python3 -m pip install cfchecker diff --git a/.github/workflows/windows_build.yml b/.github/workflows/windows_build.yml index bba1e21e9b07..cf970ca82bb7 100644 --- a/.github/workflows/windows_build.yml +++ b/.github/workflows/windows_build.yml @@ -6,6 +6,7 @@ on: - 'doc/**' branches-ignore: - 'backport**' + - 'dependabot**' pull_request: paths-ignore: - 'doc/**' diff --git a/.travis.yml b/.travis.yml index 546aaf9e8546..930210c396ff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,24 +26,24 @@ matrix: - BUILD_NAME=s390x - DETAILS="" - - os: linux - arch: arm64-graviton2 - virt: lxd - group: edge - compiler: gcc - language: cpp - sudo: false - dist: jammy - cache: - apt: true - directories: - - $HOME/.ccache - apt: - packages: - - ccache - env: - - BUILD_NAME=graviton2 - - DETAILS= + #- os: linux + # arch: arm64-graviton2 + # virt: lxd + # group: edge + # compiler: gcc + # language: cpp + # sudo: false + # dist: jammy + # cache: + # apt: true + # directories: + # - $HOME/.ccache + # apt: + # packages: + # - ccache + # env: + # - BUILD_NAME=graviton2 + # - DETAILS= before_install: - if ! git diff --name-only $TRAVIS_COMMIT_RANGE | grep -qvE '(.rst)$'; then travis_terminate 0; fi diff --git a/CITATION.cff b/CITATION.cff index 1f035ff76d03..e89453ce2119 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -2,8 +2,8 @@ cff-version: 1.2.0 message: Please cite this software using these metadata or in the CITATION file. type: software title: GDAL -version: 3.9.0 -date-released: 2024-05-06 +version: 3.9.2 +date-released: 2024-08-13 doi: 10.5281/zenodo.5884351 abstract: GDAL is a translator library for raster and vector geospatial data formats that is released under an MIT style Open Source License by the Open diff --git a/NEWS.md b/NEWS.md index d8658b89d116..de02c4ee5ef3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,417 @@ +# GDAL/OGR 3.9.2 Release Notes + +GDAL 3.9.2 is a bugfix release. + +## Build + +* Fix compilation against openssl-libs-3.2.2-3 of fedora:rawhide +* Fix compilation against libarchive-3.3.3-5 as shipped by RHEL 8 (#10428) +* Fix -Wnull-dereference warnings of gcc 14.2 + +## GDAL 3.9.2 + +### Port + +* CPLFormFilename()/CPLGetDirname()/CPLGetPath(): make it work with + 'vsicurl/http://example.com?foo' type of filename, to fix Zarr driver + +### Core + +* GDALCopyWords(): Fix double->uint64 when input value > UINT64_MAX that was + wrongly converted to 0 + +### Raster utilities + +* gdalinfo_output.schema.json: pin stac-extensions/eo to v1.1.0 +* gdal_translate: fix -a_nodata to accept '-inf' as input (3.9.0 regression) +* gdalwarp: fix -srcnodata/-dstnodata to accept negative value in first position + (3.9.0 regression) +* gdalbuildvrt: -fix -srcnodata/-vrtnodata to accept negative value in first + position (3.9.0 regression) +* gdallocationinfo: avoid extra newline character in -valonly mode if coordinate + is outside raster extent (3.9.0 regression) +* gdal_rasterize: on a int64 band, set nodata value as int64 (#10306) +* gdal_rasterize: restrict to defaulting to Int64 raster data type only if + output driver supports it (and the burned field is Int64) +* gdal_retile: error out with clear message when trying to retile a file with a + geotransform with rotation terms, or several input files with inconsistent SRS + (#10333) +* gdallocationinfo: in -E echo mode, always report input coordinates, not pixel,line +* gdal2tiles: update links in generate_leaflet(), remove OSM Toner (#10304) +* gdal2tiles: uUse correct OpenStreetMap tile url (openstreetmap/operations#737) +* gdal2tiles: fix exception with --nodata-values-pct-threshold but not + --excluded-values on a multi-band raster + +### Raster drivers + +COG driver: + * properly deal with mask bands w.r.t resampling when generating JPEG output, + or when input has a mask band (3.5 regression) (#10536) + +GTI driver: + * start looking for OVERVIEW__xxxx metadata items at index 0 + * automatically add overviews of overviews, unless the OVERVIEW_LEVEL=NONE open + option is specified + +GTiff driver: + * make SetNoDataValue(double) work on a Int64/UInt64 band (#10306) + +KEA driver: + * don't derive from PAM classes (#10355) + +JPEG driver: + * ReadFLIRMetadata(): avoid potential infinite loop + +netCDF driver: + * multidim: fix use-after-free on string variables in ReadOneElement() + +NITF driver: + * 12-bit JPEG writer: fix crash if raster width > block width (#10441) + +OGCAPI driver: + * do not emit 'Server does not support specified IMAGE_FORMAT: AUTO' when + opening a vector layer with MVT tiles only + * fix reading encodingInfo/dataType for Coverage API + +SRTMHGT driver: + * add support for 0.5 deg resolution datasets (#10514) + +VRT driver: + * fix reading from virtual overviews when SrcRect / DstRect elements are missing + +WMTS driver: + * make sure not to request tiles outside of tile matrix / tile matrix limits, + if the dataset extent goes beyond them + * clip layer extent with union of extent of tile matrices (#10348) + +## OGR 3.9.2 + +### Core + +* WKT geometry importer: accept (non conformant) PointZ/PointZM without space as + generated by current QGIS versions +* OGR SQL: do not make backslash a special character inside single-quoted + strings, to improve compliance with SQL92 (#10416) +* OGRSQL: return in error when 'Did not find end-of-string character' is emitted + (#10515) + +### OGRSpatialReference + +* EPSGTreatsAsNorthingEasting(): make it work correctly with compound CRS + +### Vector drivers + +GeoJSON driver: + * writer: make sure geometry is reprojected even when it is invalid after + coordinate rounding (3.9.0 regression) (qgis/QGIS#58169) + +GML driver: + * writer: fix missing SRS in Featurecollection's boundedBy element (3.9.1 + regression) (#10332) + +GMLAS driver: + * avoid crash on a OSSFuzz generated xsd (ossfuzz#70511) + +GTFS: + * fix error when applying an attribute filter on a field whose advertized data + type is not string (duckdb/duckdb_spatial#343) + +JSONFG driver: + * fix reading non-FeatureCollection documents larger than 6,000 bytes (#10525) + +LIBKML driver: + * fix writing a .kmz to cloud storage (#10313) + * fix LIBKML_RESOLVE_STYLE=YES when reading a KML file with dataset-level + styles (3.9.1 regression) (qgis/qgis#58208) + * fix reading/writing style URL in KMZ files (#10478) + +MiraMonVector driver: + * fix oss-fuzz#70860 + +OAPIF driver: + * fix resolving of relative links (#10410) + +OpenFileGDB driver: + * fix attribute filter when an equality comparison is involved with a field + with an index created with a LOWER(field_name) expression (#10345) + +OSM driver: + * avoid creating /vsimem/osm_importer directory (#10566) + +PG driver: + * fix ogr2ogr scenarios to PostgreSQL when there are several input layer names + like schema_name.layer_name (3.9.0 regression) + * fix support for geometry/geography OID in range between 2^31 and 2^32 (#10486) + +PG/PGDump drivers: + * make sure spatial index name is unique, even if several tables have a long + enough prefix (#10522, #7629) + +Shapefile driver: + * fix recognizing an empty string as a NULL date (#10405) + * fix off-by-one write heap buffer overflow when deleting exactly 128, 232, + etc. features and repacking (#10451) + +XLSX driver: + * support documents whose XML elements have a prefix (duckdb +/duckdb_spatial#362) + +## Python bindings + +* fix typos in gdal.Footprint() help message +* make MDArray.Write(array_of_strings) work with a 0-d string variable + +# GDAL/OGR 3.9.1 Release Notes + +GDAL 3.9.1 is a bugfix release. + +## Build + +* PDF: fix build with PoDoFo >= 0.10 with C++20 compilation (#9875) +* PDF: fix build against PoDoFo with MSYS2 UCRT64 and CLANG64 (#9976) +* Fix compiler errors in C++20 and C++23 modes +* Fix compiler warnings with gcc 14 +* CMake: add infrastructure so we can build plugins that can be loaded against + a libgdal that has been without any support for them. + Note: this is an ABI break for in-tree drivers, with deferred loading capability, when built as plugin. That is such a driver built against + GDAL 3.9.0 won't be able to be loaded by GDAL 3.9.1. + +## GDAL 3.9.1 + +### Port + +* /vsis3/: include AWS_CONTAINER_CREDENTIALS_TOKEN in container credentials + flow (#9881) +* VSIStatL(): allow trailing slash on mingw64 builds + +### Algorithms + +* Warper: relax longitude extend check to decide whether we can insert a + CENTER_LONG wrapping longitude +* GCPTransformer: accept only 2 points for order=1 transformer (#10074) +* GDALContourGenerateEx(): validate LEVEL_INTERVAL option (#10103) +* GDALTranslate(): use directly generated VRT even if BLOCKXSIZE/BLOCKYSIZE + creation options are specified +* GDALSieveFilter(): avoid assert() when all pixels are masked (3.9.0 regression) + (raster/rasterio#3101) +* Overview generation: fix multi-threaded bug, resulting in locks/crashes with + GeoPackage in particular (#10245) + +### Core + +* GDALIdentifyDriverEx(): transmit papszAllowedDrivers to Identify() (#9946) +* GDALNoDataMaskBand::IRasterIO(): fix crash on memory allocation failure + (rasterio/rasterio#3028) +* Rasterband methods (histogram, statistics): make them compatible of a dataset + with more 2 billion blocks + +### Raster utilities + +* Add argument name after 'Too few arguments' error (#10011) +* gdalwarp: fix help message for -wm (#9960) +* gdalinfo text output: avoid weird truncation of coordinate epoch when + its value is {year}.0 +* Python utilities: avoid UseExceptions() related deprecation warning when + launched from launcher shell scripts (#10010) + +### Raster drivers + +AAIGRID driver: + * fix forcing datatype to Float32 when NODATA_value is nan and pixel values + look as ints + +ESRIC driver: + * ingest more bytes to be able to identify more datasets (#10006) + * properly takes into account minLOD for .tpkx (#10229) + * validate tile size + * add a EXTENT_SOURCE open option and default to FULL_EXTENT (#10229) + +GRIB driver: + * make .idx reading compatible of /vsisubfile/ (#10214) + +GTiff driver: + * fix reading angular projection parameters in non-degree unit + (typically grads), when reading projection from ProjXXXXGeoKeys (#10154) + * fix writing angular projection parameters in non-degree unit (typically + grads), when writing ProjXXXXGeoKeys + * multithreaded reading: avoid emitting tons of warnings when ExtraSamples tag + is missing + * writer: limit number of GCPs in GeoTIFF tag to 10922 to avoid overflowing + uint16_t (#10164) + +HDF5 driver: + * add support for libhdf5 >= 1.14.4.2 when built with Float16 + * make logic to assign per-band attribute more generic + +netCDF driver: + * netCDFAttribute::IWrite(): fix writing an array of (NC4) strings, from a + non-string array + * try to better round geotransform values when read from single-precsion + lon/lat float arrays + +STACIT driver: + * add a OVERLAP_STRATEGY opening option to determine how to handle sources + fully overlapping others, and taking into account nodata values + * honour MAX_ITEMS=0 as meaning unlimited as documented + * implement paging using POST method + +VRT driver: + * fix processing of LUT where the first source value is NaN + * fix serialization of separatable kernel in VRTKernelFilteredSource (#10253) + +Zarr driver: + * SerializeNumericNoData(): use CPLJSonObject::Add(uint64_t) to avoid potential + undefined behavior casts + +## OGR 3.9.1 + +### Core + +* OGRSQL: validate column name in COUNT(field_name) and error out if it + doesn't exist (#9972) +* OGR SQL: fix crash when the ON expression of a JOIN contains OGR special + fields (in particular feature id) +* OGR layer algebra: honour PROMOTE_TO_MULTI=YES for Points +* OGRFeature::SetField(int, double): avoid UndefinedBehavior when passing NaN +* OGRFeature::SetField(int, GIntBig): avoid UndefinedBehavior when passing value + close to INT64_MAX +* OGRLayer::WriteArrowBatch(): avoid UndefinedBehavior when trying to convert + NaN to Int64 + +### OGRSpatialReference + +* importFromESRI() Arc/Info 7 .prj: fix importing LAMBERT_AZIMUTHAL with a + 'radius of the sphere of reference' +* workaround bug of PROJ < 9.5 regarding wrong conversion id for UTM south +* importFromUSGS(): use plain WGS84 datum with WGS84 ellipsoid, and identify + EPSG code + +### Vector utilities + +* ogrinfo: fix error message when not specifying a filename +* ogrinfo text output: avoid weird truncation of coordinate epoch when + its value is {year}.0 +* ogr2ogr: do not call CreateLayer() with a geom field defn of type wkbNone + (#10071) +* ogr2ogr: error out if GCP transform creation fails (#10073) + +### Vector drivers + +AVCBin driver: + * avoid integer overflow (ossfuzz #68814) + +DXF driver: + * avoid slight numeric imprecision with closed lwpolyline and arcs (#10153) + +ESRIJSON driver: + * do not set width on DateTime field + * fix paging when URL contains f=pjson instead of f=json (#10094) + +FileGDB driver: + * be robust to be called with a geometry field definition of type wkbNone + (#10071) + +FlatGeoBuf driver: + * more explicit error message in case of feature geometry type != layer + geometry type (#9893) + +GeoJSON driver: + * do not recognize GeoJSON files with a featureType feature property as JSONFG + (#9946) + * make it possible with -if/papszAllowedDrivers to force opening a JSONFG file + with the GeoJSON driver + * implement IUpdateFeature() that was broken up to now + * declare missing capabilities DELETE_FIELD, REORDER_FIELDS, + ALTER_FIELD_DEFN_FLAGS + +GML driver: + * fix memory leak due do xlink:href substitution in a non-nominal case + (ossfuzz #68850) + * GML_SKIP_RESOLVE_ELEMS=HUGE: keep gml:id in .resolved.gml file + * avoid assertion due to trying to load existing .gfs file after reading + .resolved.gml (#10015) + * fix issue with nested elements with identical names in + GML_SKIP_RESOLVE_ELEMS=HUGE mode (#10015) + * make sure SRS is detected when using GML_SKIP_RESOLVE_ELEMS=HUGE on a AIXM + file, otherwise GML_SWAP_COORDINATES might not work correctly + +JSONFG driver: + * dataset identification: recognize more JSON-FG specification version + +LIBKML driver: + * fix handling of styleUrl element referencing to an external document (#9975) + * fix performance issue when writing large files (#10138) + +MiraMonVector driver: + * adding which polygon cycles the linestring in the linestring metadata information (#9885) + * Adding more matches in the look-up table MM_m_idofic.csv + * Fixing error in linestring (from not polygon) metadata file + * Robustness fixes (ossfuzz #68809) + * fix a case of sensitive comparison + * fix MMResetFeatureRecord + * fix accepted metadata versions/subversions + +MapInfo: + * .tab: AlterFieldDefn(): fix data corruption when altering (for example + renaming) a Integer/Float32 field of size 4 (or Integer64/Float64 field of + size 8 + * avoid potential double-free (ossfuzz#69332, ossfuzz#69334) + +netCDF driver: + * writer: use CF-1.8 with FORMAT=NC4 and GEOMETRY_ENCODING=WKT + +ODS driver: + * implement IUpdateFeature() that was broken up to now + * declare missing capabilities DELETE_FIELD, REORDER_FIELDS, + ALTER_FIELD_DEFN_FLAGS + * fix CreateFeature() implementation when a FID is set + +OpenFileGDB driver: + * writer: .gdbtable header must be rewritten when updating an existing feature + at the end of file (otherwise resulting file might not be readable by + Esri software) + * detect and try to repair corruption of .gdbtable header related to above item + * BuildSRS(): do not use CPLErrorReset() + +Parquet driver: + * GeoParquet: always write version=1.1.0 + +PDF driver: + * fix wrong order of matrix multiplication for 'cm' operator... (#9870) + * make ExploreContentsNonStructured() be able to parse OGCs as generated + by ArcGIS 12.9 (fixes #9870) + * just ignore unknown objects referenced by /Do (#9870) + +PMTiles driver: + * writer: fix crash in ogr2ogr (or Arrow based workflows) from GeoPackage/ + GeoParquet to PMTiles (#10199) + +PG driver: + * avoid errors related to ogr_system_tables.metadata when user has not enough + permissions (#9994) + * really honor OGR_PG_ENABLE_METADATA=NO in SerializeMetadata() + +SQLite/GPKG drivers + * detect UNIQUE constraints expressed as a ', CONSTRAINT name UNIQUE (column_name)' + at the end of CREATE TABLE (qgis/QGIS#57823) + +XLSX driver: + * implement IUpdateFeature() that was broken up to now + * declare missing capabilities DELETE_FIELD, REORDER_FIELDS, + ALTER_FIELD_DEFN_FLAGS + * fix CreateFeature() implementation when a FID is set + +## Python bindings + +* do not emit warnings about not having used [Dont]UseExceptions() if run under + gdal.ExceptionMgr() +* avoid gdal.ExceptionMgr() to re-throw a GDAL exception already caught under it +* avoid exception emitted and caught under gdal.ExceptionMgr() to cause later + issues +* Avoid crash when using orphaned subgeometry (#9920) +* make them compatible of SWIG 4.3.0dev + + # GDAL/OGR 3.9.0 Releases Notes GDAL/OGR 3.9.0 is a feature release. diff --git a/VERSION b/VERSION index a5c4c763394f..2009c7dfad99 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.9.0 +3.9.2 diff --git a/alg/contour.cpp b/alg/contour.cpp index 34f29761b4f3..c95b28e41ea5 100644 --- a/alg/contour.cpp +++ b/alg/contour.cpp @@ -562,6 +562,14 @@ CPLErr GDALContourGenerateEx(GDALRasterBandH hBand, void *hLayer, if (opt) { contourInterval = CPLAtof(opt); + // Written this way to catch NaN as well. + if (!(contourInterval > 0)) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid value for LEVEL_INTERVAL. Should be strictly " + "positive."); + return CE_Failure; + } } double contourBase = 0.0; diff --git a/alg/gdal_crs.cpp b/alg/gdal_crs.cpp index 74ea4ceff9a9..9c134f0b69f5 100644 --- a/alg/gdal_crs.cpp +++ b/alg/gdal_crs.cpp @@ -220,6 +220,22 @@ static void *GDALCreateGCPTransformerEx(int nGCPCount, psInfo->nRefCount = 1; psInfo->asGCPs = gdal::GCP::fromC(pasGCPList, nGCPCount); + if (nGCPCount == 2 && nReqOrder == 1 && + psInfo->asGCPs[0].X() != psInfo->asGCPs[1].X() && + psInfo->asGCPs[0].Y() != psInfo->asGCPs[1].Y()) + { + // Assumes that the 2 GCPs form opposite corners of a rectangle, + // and synthetize a 3rd corner + gdal::GCP newGCP; + newGCP.X() = psInfo->asGCPs[1].X(); + newGCP.Y() = psInfo->asGCPs[0].Y(); + newGCP.Pixel() = psInfo->asGCPs[1].Pixel(); + newGCP.Line() = psInfo->asGCPs[0].Line(); + psInfo->asGCPs.emplace_back(std::move(newGCP)); + + nGCPCount = 3; + pasGCPList = gdal::GCP::c_ptr(psInfo->asGCPs); + } memcpy(psInfo->sTI.abySignature, GDAL_GTI2_SIGNATURE, strlen(GDAL_GTI2_SIGNATURE)); diff --git a/alg/gdalsievefilter.cpp b/alg/gdalsievefilter.cpp index a3c60a0cdb6a..5d11f97105a7 100644 --- a/alg/gdalsievefilter.cpp +++ b/alg/gdalsievefilter.cpp @@ -33,7 +33,6 @@ #include #include -#include #include #include #include @@ -211,30 +210,36 @@ CPLErr CPL_STDCALL GDALSieveFilter(GDALRasterBandH hSrcBand, /* -------------------------------------------------------------------- */ int nXSize = GDALGetRasterBandXSize(hSrcBand); int nYSize = GDALGetRasterBandYSize(hSrcBand); - auto *panLastLineVal = static_cast( - VSI_MALLOC2_VERBOSE(sizeof(std::int64_t), nXSize)); - auto *panThisLineVal = static_cast( - VSI_MALLOC2_VERBOSE(sizeof(std::int64_t), nXSize)); - auto *panLastLineId = - static_cast(VSI_MALLOC2_VERBOSE(sizeof(GInt32), nXSize)); - auto *panThisLineId = - static_cast(VSI_MALLOC2_VERBOSE(sizeof(GInt32), nXSize)); - auto *panThisLineWriteVal = static_cast( - VSI_MALLOC2_VERBOSE(sizeof(std::int64_t), nXSize)); - GByte *pabyMaskLine = hMaskBand != nullptr - ? static_cast(VSI_MALLOC_VERBOSE(nXSize)) - : nullptr; + auto panLastLineValKeeper = std::unique_ptr( + static_cast( + VSI_MALLOC2_VERBOSE(sizeof(std::int64_t), nXSize))); + auto panThisLineValKeeper = std::unique_ptr( + static_cast( + VSI_MALLOC2_VERBOSE(sizeof(std::int64_t), nXSize))); + auto panLastLineIdKeeper = std::unique_ptr( + static_cast(VSI_MALLOC2_VERBOSE(sizeof(GInt32), nXSize))); + auto panThisLineIdKeeper = std::unique_ptr( + static_cast(VSI_MALLOC2_VERBOSE(sizeof(GInt32), nXSize))); + auto panThisLineWriteValKeeper = + std::unique_ptr( + static_cast( + VSI_MALLOC2_VERBOSE(sizeof(std::int64_t), nXSize))); + auto pabyMaskLineKeeper = std::unique_ptr( + hMaskBand != nullptr ? static_cast(VSI_MALLOC_VERBOSE(nXSize)) + : nullptr); + + auto panLastLineVal = panLastLineValKeeper.get(); + auto panThisLineVal = panThisLineValKeeper.get(); + auto panLastLineId = panLastLineIdKeeper.get(); + auto panThisLineId = panThisLineIdKeeper.get(); + auto panThisLineWriteVal = panThisLineWriteValKeeper.get(); + auto pabyMaskLine = pabyMaskLineKeeper.get(); + if (panLastLineVal == nullptr || panThisLineVal == nullptr || panLastLineId == nullptr || panThisLineId == nullptr || panThisLineWriteVal == nullptr || (hMaskBand != nullptr && pabyMaskLine == nullptr)) { - CPLFree(panThisLineId); - CPLFree(panLastLineId); - CPLFree(panThisLineVal); - CPLFree(panLastLineVal); - CPLFree(panThisLineWriteVal); - CPLFree(pabyMaskLine); return CE_Failure; } @@ -316,12 +321,28 @@ CPLErr CPL_STDCALL GDALSieveFilter(GDALRasterBandH hSrcBand, if (eErr == CE_None) oFirstEnum.CompleteMerges(); + /* -------------------------------------------------------------------- */ + /* Check if there are polygons */ + /* -------------------------------------------------------------------- */ + if (!oFirstEnum.panPolyIdMap || !oFirstEnum.panPolyValue) + { + // Can happen if all pixels are masked + if (hSrcBand == hDstBand) + { + pfnProgress(1.0, "", pProgressArg); + return CE_None; + } + else + { + return GDALRasterBandCopyWholeRaster(hSrcBand, hDstBand, nullptr, + pfnProgress, pProgressArg); + } + } + /* -------------------------------------------------------------------- */ /* Push the sizes of merged polygon fragments into the */ /* merged polygon id's count. */ /* -------------------------------------------------------------------- */ - assert(oFirstEnum.panPolyIdMap != nullptr); // for Coverity - assert(oFirstEnum.panPolyValue != nullptr); // for Coverity for (int iPoly = 0; iPoly < oFirstEnum.nNextPolygonId; iPoly++) { if (oFirstEnum.panPolyIdMap[iPoly] != iPoly) @@ -346,10 +367,16 @@ CPLErr CPL_STDCALL GDALSieveFilter(GDALRasterBandH hSrcBand, GDALRasterPolygonEnumerator oSecondEnum(nConnectedness); std::vector anBigNeighbour; - anBigNeighbour.resize(anPolySizes.size()); - - for (int iPoly = 0; iPoly < static_cast(anPolySizes.size()); iPoly++) - anBigNeighbour[iPoly] = -1; + try + { + anBigNeighbour.resize(anPolySizes.size(), -1); + } + catch (const std::exception &) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "%s: Out of memory", + __FUNCTION__); + return CE_Failure; + } /* ==================================================================== */ /* Second pass ... identify the largest neighbour for each */ @@ -540,9 +567,7 @@ CPLErr CPL_STDCALL GDALSieveFilter(GDALRasterBandH hSrcBand, /* ==================================================================== */ oSecondEnum.Clear(); - for (int iY = 0; oFirstEnum.panPolyIdMap != nullptr && // for Coverity - eErr == CE_None && iY < nYSize; - iY++) + for (int iY = 0; eErr == CE_None && iY < nYSize; iY++) { /* -------------------------------------------------------------------- */ @@ -627,15 +652,5 @@ CPLErr CPL_STDCALL GDALSieveFilter(GDALRasterBandH hSrcBand, } } - /* -------------------------------------------------------------------- */ - /* Cleanup */ - /* -------------------------------------------------------------------- */ - CPLFree(panThisLineId); - CPLFree(panLastLineId); - CPLFree(panThisLineVal); - CPLFree(panLastLineVal); - CPLFree(panThisLineWriteVal); - CPLFree(pabyMaskLine); - return eErr; } diff --git a/alg/gdaltransformer.cpp b/alg/gdaltransformer.cpp index bff7c93cc3bc..263fbc2fa08d 100644 --- a/alg/gdaltransformer.cpp +++ b/alg/gdaltransformer.cpp @@ -1482,7 +1482,12 @@ static void InsertCenterLong(GDALDatasetH hDS, OGRSpatialReference *poSRS, adfGeoTransform[0] + nXSize * adfGeoTransform[1] + nYSize * adfGeoTransform[2])); - if (dfMaxLong - dfMinLong > 360.0) + const double dfEpsilon = + std::max(std::fabs(adfGeoTransform[1]), std::fabs(adfGeoTransform[2])); + // If the raster covers more than 360 degree (allow an extra pixel), + // give up + constexpr double RELATIVE_EPSILON = 0.05; // for numeric precision issues + if (dfMaxLong - dfMinLong > 360.0 + dfEpsilon * (1 + RELATIVE_EPSILON)) return; /* -------------------------------------------------------------------- */ diff --git a/apps/argparse/argparse.hpp b/apps/argparse/argparse.hpp index 118e2145ce1a..0a66f37e0f18 100644 --- a/apps/argparse/argparse.hpp +++ b/apps/argparse/argparse.hpp @@ -968,7 +968,8 @@ class Argument { std::bind(is_optional, std::placeholders::_1, m_prefix_chars)); dist = static_cast(std::distance(start, end)); if (dist < num_args_min) { - throw std::runtime_error("Too few arguments"); + throw std::runtime_error("Too few arguments for '" + + std::string(m_used_name) + "'."); } } @@ -1357,6 +1358,10 @@ class Argument { * '+' '-' */ static bool is_decimal_literal(std::string_view s) { + if (s == "inf") { + return true; + } + auto is_digit = [](auto c) constexpr { switch (c) { case '0': diff --git a/apps/data/gdalinfo_output.schema.json b/apps/data/gdalinfo_output.schema.json index 166a490d9e26..a4c50c780e66 100644 --- a/apps/data/gdalinfo_output.schema.json +++ b/apps/data/gdalinfo_output.schema.json @@ -277,7 +277,7 @@ }, "stac": { - "$comment": "Derived from https://raw.githubusercontent.com/stac-extensions/projection/main/json-schema/schema.json#/definitions/fields, https://raw.githubusercontent.com/stac-extensions/eo/main/json-schema/schema.json#/definitions/bands and https://raw.githubusercontent.com/stac-extensions/eo/main/json-schema/schema.json#/definitions/bands", + "$comment": "Derived from https://raw.githubusercontent.com/stac-extensions/projection/main/json-schema/schema.json#/definitions/fields, https://raw.githubusercontent.com/stac-extensions/eo/v1.1.0/json-schema/schema.json#/definitions/bands and https://raw.githubusercontent.com/stac-extensions/eo/v1.1.0/json-schema/schema.json#/definitions/bands", "type": "object", "properties": { "proj:epsg": { @@ -334,10 +334,10 @@ } }, "eo:bands": { - "$ref": "https://raw.githubusercontent.com/stac-extensions/eo/main/json-schema/schema.json#/definitions/bands" + "$ref": "https://raw.githubusercontent.com/stac-extensions/eo/v1.1.0/json-schema/schema.json#/definitions/bands" }, "raster:bands": { - "$ref": "https://raw.githubusercontent.com/stac-extensions/eo/main/json-schema/schema.json#/definitions/bands" + "$ref": "https://raw.githubusercontent.com/stac-extensions/eo/v1.1.0/json-schema/schema.json#/definitions/bands" } }, "additionalProperties": false diff --git a/apps/gdal_rasterize_lib.cpp b/apps/gdal_rasterize_lib.cpp index 39913ad306cb..493922ee1068 100644 --- a/apps/gdal_rasterize_lib.cpp +++ b/apps/gdal_rasterize_lib.cpp @@ -482,7 +482,7 @@ static GDALDatasetH CreateOutputDataset( OGREnvelope sEnvelop, GDALDriverH hDriver, const char *pszDest, int nXSize, int nYSize, double dfXRes, double dfYRes, bool bTargetAlignedPixels, int nBandCount, GDALDataType eOutputType, char **papszCreationOptions, - const std::vector &adfInitVals, int bNoDataSet, double dfNoData) + const std::vector &adfInitVals, const char *pszNoData) { bool bFirstLayer = true; char *pszWKT = nullptr; @@ -598,12 +598,16 @@ static GDALDatasetH CreateOutputDataset( } }*/ - if (bNoDataSet) + if (pszNoData) { for (int iBand = 0; iBand < nBandCount; iBand++) { GDALRasterBandH hBand = GDALGetRasterBand(hDstDS, iBand + 1); - GDALSetRasterNoDataValue(hBand, dfNoData); + if (GDALGetRasterDataType(hBand) == GDT_Int64) + GDALSetRasterNoDataValueAsInt64(hBand, + CPLAtoGIntBig(pszNoData)); + else + GDALSetRasterNoDataValue(hBand, CPLAtof(pszNoData)); } } @@ -649,8 +653,7 @@ struct GDALRasterizeOptions char **papszCreationOptions; GDALDataType eOutputType; std::vector adfInitVals; - int bNoDataSet; - double dfNoData; + char *pszNoData; OGREnvelope sEnvelop; int nXSize, nYSize; OGRSpatialReferenceH hSRS; @@ -790,6 +793,36 @@ GDALDatasetH GDALRasterize(const char *pszDest, GDALDatasetH hDstDS, } } + const auto GetOutputDataType = [&](OGRLayerH hLayer) + { + CPLAssert(bCreateOutput); + CPLAssert(hDriver); + GDALDataType eOutputType = psOptions->eOutputType; + if (eOutputType == GDT_Unknown && + psOptions->pszBurnAttribute != nullptr) + { + OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn(hLayer); + const int iBurnField = + OGR_FD_GetFieldIndex(hLayerDefn, psOptions->pszBurnAttribute); + if (iBurnField >= 0 && OGR_Fld_GetType(OGR_FD_GetFieldDefn( + hLayerDefn, iBurnField)) == OFTInteger64) + { + const char *pszMD = GDALGetMetadataItem( + hDriver, GDAL_DMD_CREATIONDATATYPES, nullptr); + if (pszMD && CPLStringList(CSLTokenizeString2(pszMD, " ", 0)) + .FindString("Int64") >= 0) + { + eOutputType = GDT_Int64; + } + } + } + if (eOutputType == GDT_Unknown) + { + eOutputType = GDT_Float64; + } + return eOutputType; + }; + /* -------------------------------------------------------------------- */ /* Process SQL request. */ /* -------------------------------------------------------------------- */ @@ -806,25 +839,7 @@ GDALDatasetH GDALRasterize(const char *pszDest, GDALDatasetH hDstDS, std::vector ahLayers; ahLayers.push_back(hLayer); - GDALDataType eOutputType = psOptions->eOutputType; - if (eOutputType == GDT_Unknown && - psOptions->pszBurnAttribute != nullptr) - { - OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn(hLayer); - int iBurnField = OGR_FD_GetFieldIndex( - hLayerDefn, psOptions->pszBurnAttribute); - if (iBurnField >= 0 && - OGR_Fld_GetType(OGR_FD_GetFieldDefn( - hLayerDefn, iBurnField)) == OFTInteger64) - { - eOutputType = GDT_Int64; - } - } - if (eOutputType == GDT_Unknown) - { - eOutputType = GDT_Float64; - } - + const GDALDataType eOutputType = GetOutputDataType(hLayer); hDstDS = CreateOutputDataset( ahLayers, psOptions->hSRS, psOptions->sEnvelop, hDriver, pszDest, psOptions->nXSize, psOptions->nYSize, @@ -832,7 +847,7 @@ GDALDatasetH GDALRasterize(const char *pszDest, GDALDatasetH hDstDS, psOptions->bTargetAlignedPixels, static_cast(psOptions->anBandList.size()), eOutputType, psOptions->papszCreationOptions, psOptions->adfInitVals, - psOptions->bNoDataSet, psOptions->dfNoData); + psOptions->pszNoData); if (hDstDS == nullptr) { GDALDatasetReleaseResultSet(hSrcDataset, hLayer); @@ -882,18 +897,10 @@ GDALDatasetH GDALRasterize(const char *pszDest, GDALDatasetH hDstDS, GDALRasterizeOptionsFree(psOptionsToFree); return nullptr; } - if (eOutputType == GDT_Unknown && - psOptions->pszBurnAttribute != nullptr) + if (eOutputType == GDT_Unknown) { - OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn(hLayer); - int iBurnField = OGR_FD_GetFieldIndex( - hLayerDefn, psOptions->pszBurnAttribute); - if (iBurnField >= 0 && - OGR_Fld_GetType(OGR_FD_GetFieldDefn( - hLayerDefn, iBurnField)) == OFTInteger64) - { + if (GetOutputDataType(hLayer) == GDT_Int64) eOutputType = GDT_Int64; - } } ahLayers.push_back(hLayer); @@ -910,7 +917,7 @@ GDALDatasetH GDALRasterize(const char *pszDest, GDALDatasetH hDstDS, psOptions->dfYRes, psOptions->bTargetAlignedPixels, static_cast(psOptions->anBandList.size()), eOutputType, psOptions->papszCreationOptions, psOptions->adfInitVals, - psOptions->bNoDataSet, psOptions->dfNoData); + psOptions->pszNoData); if (hDstDS == nullptr) { GDALRasterizeOptionsFree(psOptionsToFree); @@ -1020,8 +1027,7 @@ GDALRasterizeOptionsNew(char **papszArgv, psOptions->dfXRes = 0; psOptions->dfYRes = 0; psOptions->eOutputType = GDT_Unknown; - psOptions->bNoDataSet = FALSE; - psOptions->dfNoData = 0; + psOptions->pszNoData = nullptr; psOptions->nXSize = 0; psOptions->nYSize = 0; psOptions->hSRS = nullptr; @@ -1195,8 +1201,8 @@ GDALRasterizeOptionsNew(char **papszArgv, } else if (i < argc - 1 && EQUAL(papszArgv[i], "-a_nodata")) { - psOptions->dfNoData = CPLAtof(papszArgv[i + 1]); - psOptions->bNoDataSet = TRUE; + CPLFree(psOptions->pszNoData); + psOptions->pszNoData = CPLStrdup(papszArgv[i + 1]); i += 1; psOptions->bCreateOutput = true; } @@ -1472,6 +1478,7 @@ void GDALRasterizeOptionsFree(GDALRasterizeOptions *psOptions) CPLFree(psOptions->pszDialect); CPLFree(psOptions->pszBurnAttribute); CPLFree(psOptions->pszWHERE); + CPLFree(psOptions->pszNoData); OSRDestroySpatialReference(psOptions->hSRS); delete psOptions; diff --git a/apps/gdal_translate_lib.cpp b/apps/gdal_translate_lib.cpp index c8a5f2612a1e..79f1e4895551 100644 --- a/apps/gdal_translate_lib.cpp +++ b/apps/gdal_translate_lib.cpp @@ -2385,12 +2385,7 @@ GDALDatasetH GDALTranslate(const char *pszDest, GDALDatasetH hSrcDataset, { const double dfNoData = CPLAtof(psOptions->osNoData.c_str()); - if (dfNoData >= static_cast( - std::numeric_limits::min()) && - dfNoData <= static_cast( - std::numeric_limits::max()) && - dfNoData == - static_cast(static_cast(dfNoData))) + if (GDALIsValueExactAs(dfNoData)) { poVRTBand->SetNoDataValueAsInt64( static_cast(dfNoData)); @@ -2428,12 +2423,7 @@ GDALDatasetH GDALTranslate(const char *pszDest, GDALDatasetH hSrcDataset, { const double dfNoData = CPLAtof(psOptions->osNoData.c_str()); - if (dfNoData >= static_cast( - std::numeric_limits::min()) && - dfNoData <= static_cast( - std::numeric_limits::max()) && - dfNoData == static_cast( - static_cast(dfNoData))) + if (GDALIsValueExactAs(dfNoData)) { poVRTBand->SetNoDataValueAsUInt64( static_cast(dfNoData)); @@ -2554,7 +2544,10 @@ GDALDatasetH GDALTranslate(const char *pszDest, GDALDatasetH hSrcDataset, /* Write to the output file using CopyCreate(). */ /* -------------------------------------------------------------------- */ if (EQUAL(psOptions->osFormat.c_str(), "VRT") && - psOptions->aosCreateOptions.empty()) + (psOptions->aosCreateOptions.empty() || + (psOptions->aosCreateOptions.size() == 2 && + psOptions->aosCreateOptions.FetchNameValue("BLOCKXSIZE") && + psOptions->aosCreateOptions.FetchNameValue("BLOCKYSIZE")))) { poVDS->SetDescription(pszDest); hOutDS = GDALDataset::ToHandle(poVDS); @@ -3039,19 +3032,6 @@ GDALTranslateOptionsGetParser(GDALTranslateOptions *psOptions, argParser->add_argument("-a_nodata") .metavar("|none") - .action( - [psOptions](const std::string &s) - { - if (EQUAL(s.c_str(), "none")) - { - psOptions->bUnsetNoData = true; - } - else - { - psOptions->bSetNoData = true; - psOptions->osNoData = s; - } - }) .help(_("Assign a specified nodata value to output bands.")); argParser->add_argument("-a_gt") @@ -3390,6 +3370,23 @@ GDALTranslateOptionsNew(char **papszArgv, psOptions->anColorInterp[nIndex] = GetColorInterp(papszArgv[i]); } + // argparser will be confused if the value of a string argument + // starts with a negative sign. + else if (EQUAL(papszArgv[i], "-a_nodata") && papszArgv[i + 1]) + { + ++i; + const std::string s = papszArgv[i]; + if (EQUAL(s.c_str(), "none")) + { + psOptions->bUnsetNoData = true; + } + else + { + psOptions->bSetNoData = true; + psOptions->osNoData = s; + } + } + else { aosArgv.AddString(papszArgv[i]); diff --git a/apps/gdalbuildvrt_lib.cpp b/apps/gdalbuildvrt_lib.cpp index ae642786f383..31e720fcc866 100644 --- a/apps/gdalbuildvrt_lib.cpp +++ b/apps/gdalbuildvrt_lib.cpp @@ -2268,6 +2268,21 @@ GDALBuildVRTOptionsNew(char **papszArgv, psOptionsForBinary->osDstFilename = papszArgv[i + 1]; ++i; } + // argparser will be confused if the value of a string argument + // starts with a negative sign. + else if (EQUAL(papszArgv[i], "-srcnodata") && i + 1 < nArgc) + { + ++i; + psOptions->osSrcNoData = papszArgv[i]; + } + // argparser will be confused if the value of a string argument + // starts with a negative sign. + else if (EQUAL(papszArgv[i], "-vrtnodata") && i + 1 < nArgc) + { + ++i; + psOptions->osVRTNoData = papszArgv[i]; + } + else { aosArgv.AddString(papszArgv[i]); diff --git a/apps/gdalinfo_lib.cpp b/apps/gdalinfo_lib.cpp index 8aa00d02b6a7..e357e76e211e 100644 --- a/apps/gdalinfo_lib.cpp +++ b/apps/gdalinfo_lib.cpp @@ -639,10 +639,12 @@ char *GDALInfo(GDALDatasetH hDataset, const GDALInfoOptions *psOptions) { std::string osCoordinateEpoch = CPLSPrintf("%f", dfCoordinateEpoch); - if (osCoordinateEpoch.find('.') != std::string::npos) + const size_t nDotPos = osCoordinateEpoch.find('.'); + if (nDotPos != std::string::npos) { - while (osCoordinateEpoch.back() == '0') - osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1); + while (osCoordinateEpoch.size() > nDotPos + 2 && + osCoordinateEpoch.back() == '0') + osCoordinateEpoch.pop_back(); } Concat(osStr, psOptions->bStdoutOutput, "Coordinate epoch: %s\n", osCoordinateEpoch.c_str()); diff --git a/apps/gdallocationinfo.cpp b/apps/gdallocationinfo.cpp index 1ec5ae30b1e8..ff2bf26ccad9 100644 --- a/apps/gdallocationinfo.cpp +++ b/apps/gdallocationinfo.cpp @@ -342,6 +342,8 @@ MAIN_START(argc, argv) while (inputAvailable) { int iPixel, iLine; + const double dfXIn = dfGeoX; + const double dfYIn = dfGeoY; if (hCT) { @@ -411,7 +413,7 @@ MAIN_START(argc, argv) } else if (bEcho) { - printf("%d%s%d%s", iPixel, osFieldSep.c_str(), iLine, + printf("%.15g%s%.15g%s", dfXIn, osFieldSep.c_str(), dfYIn, osFieldSep.c_str()); } @@ -424,7 +426,12 @@ MAIN_START(argc, argv) osXML += "Location is off this file! No further details " "to report."; else if (bValOnly) - printf("\n"); + { + for (int i = 1; i < static_cast(anBandList.size()); i++) + { + printf("%s", osFieldSep.c_str()); + } + } else if (!bQuiet) printf("\nLocation is off this file! No further details to " "report.\n"); diff --git a/apps/gdalwarp_lib.cpp b/apps/gdalwarp_lib.cpp index c2e82d772623..b79dbf15b75f 100644 --- a/apps/gdalwarp_lib.cpp +++ b/apps/gdalwarp_lib.cpp @@ -3680,6 +3680,18 @@ static GDALDatasetH GDALWarpCreateOutput( return nullptr; } + // Examine desired overview level and retrieve the corresponding dataset + // if it exists. + std::unique_ptr oDstDSOverview; + if (psOptions->nOvLevel >= 0) + { + oDstDSOverview.reset(GDALCreateOverviewDataset( + GDALDataset::FromHandle(hSrcDS), psOptions->nOvLevel, + /* bThisLevelOnly = */ true)); + if (oDstDSOverview) + hSrcDS = oDstDSOverview.get(); + } + /* -------------------------------------------------------------------- */ /* Check if the source dataset shares some files with the dest @@ -4110,6 +4122,7 @@ static GDALDatasetH GDALWarpCreateOutput( { nOptions |= GDAL_SWO_FORCE_SQUARE_PIXEL; } + if (GDALSuggestedWarpOutput2(hSrcDS, psInfo->pfnTransform, hTransformArg, adfThisGeoTransform, &nThisPixels, &nThisLines, adfExtent, @@ -5731,15 +5744,15 @@ GDALWarpAppOptionsGetParser(GDALWarpAppOptions *psOptions, else psOptions->dfWarpMemoryLimit = CPLAtofM(s.c_str()); }) - .help(_("Error threshold.")); + .help(_("Set max warp memory.")); argParser->add_argument("-srcnodata") - .metavar("[ ...]") + .metavar("\"[ ]...\"") .store_into(psOptions->osSrcNodata) .help(_("Nodata masking values for input bands.")); argParser->add_argument("-dstnodata") - .metavar("[ ...]") + .metavar("\"[ ]...\"") .store_into(psOptions->osDstNodata) .help(_("Nodata masking values for output bands.")); @@ -6113,6 +6126,20 @@ GDALWarpAppOptionsNew(char **papszArgv, } psOptions->bCreateOutput = true; } + // argparser will be confused if the value of a string argument + // starts with a negative sign. + else if (EQUAL(papszArgv[i], "-srcnodata") && i + 1 < nArgc) + { + ++i; + psOptions->osSrcNodata = papszArgv[i]; + } + // argparser will be confused if the value of a string argument + // starts with a negative sign. + else if (EQUAL(papszArgv[i], "-dstnodata") && i + 1 < nArgc) + { + ++i; + psOptions->osDstNodata = papszArgv[i]; + } else { aosArgv.AddString(papszArgv[i]); diff --git a/apps/ogr2ogr_lib.cpp b/apps/ogr2ogr_lib.cpp index 3650db61ade0..7839b5934b03 100644 --- a/apps/ogr2ogr_lib.cpp +++ b/apps/ogr2ogr_lib.cpp @@ -2641,6 +2641,10 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, } } + // Automatically close poODS on error, if it has been created by this + // method. + GDALDatasetUniquePtr poODSUniquePtr(hDstDS == nullptr ? poODS : nullptr); + // Some syntaxic sugar to make "ogr2ogr [-f PostgreSQL] PG:dbname=.... // source [srclayer] -lco OVERWRITE=YES" work like "ogr2ogr -overwrite // PG:dbname=.... source [srclayer]" The former syntax used to work at @@ -2658,8 +2662,6 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, { CPLError(CE_Failure, CPLE_AppDefined, "-append and -lco OVERWRITE=YES are mutually exclusive"); - if (hDstDS == nullptr) - GDALClose(poODS); return nullptr; } bOverwrite = true; @@ -2707,8 +2709,6 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, CPLError(CE_Failure, CPLE_AppDefined, "Failed to process SRS definition: %s", psOptions->osOutputSRSDef.c_str()); - if (hDstDS == nullptr) - GDALClose(poODS); return nullptr; } oOutputSRSHolder.get()->SetCoordinateEpoch( @@ -2729,8 +2729,6 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, CPLError(CE_Failure, CPLE_AppDefined, "Failed to process SRS definition: %s", psOptions->osSourceSRSDef.c_str()); - if (hDstDS == nullptr) - GDALClose(poODS); return nullptr; } oSourceSRS.SetCoordinateEpoch(psOptions->dfSourceCoordinateEpoch); @@ -2741,17 +2739,16 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, /* Create a transformation object from the source to */ /* destination coordinate system. */ /* -------------------------------------------------------------------- */ - GCPCoordTransformation *poGCPCoordTrans = nullptr; + std::unique_ptr poGCPCoordTrans; if (psOptions->oGCPs.nGCPCount > 0) { - poGCPCoordTrans = new GCPCoordTransformation( + poGCPCoordTrans = std::make_unique( psOptions->oGCPs.nGCPCount, psOptions->oGCPs.pasGCPs, psOptions->nTransformOrder, poSourceSRS ? poSourceSRS : oOutputSRSHolder.get()); if (!(poGCPCoordTrans->IsValid())) { - delete poGCPCoordTrans; - poGCPCoordTrans = nullptr; + return nullptr; } } @@ -2805,7 +2802,7 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, oTranslator.m_poOutputSRS = oOutputSRSHolder.get(); oTranslator.m_bNullifyOutputSRS = psOptions->bNullifyOutputSRS; oTranslator.m_poUserSourceSRS = poSourceSRS; - oTranslator.m_poGCPCoordTrans = poGCPCoordTrans; + oTranslator.m_poGCPCoordTrans = poGCPCoordTrans.get(); oTranslator.m_eGType = psOptions->eGType; oTranslator.m_eGeomTypeConversion = psOptions->eGeomTypeConversion; oTranslator.m_bMakeValid = psOptions->bMakeValid; @@ -2977,9 +2974,6 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, { CPLError(CE_Failure, CPLE_AppDefined, "-splitlistfields not supported in this mode"); - if (hDstDS == nullptr) - GDALClose(poODS); - delete poGCPCoordTrans; return nullptr; } @@ -2992,9 +2986,6 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, { CPLError(CE_Failure, CPLE_AppDefined, "Couldn't fetch requested layer %s!", pszLayer); - if (hDstDS == nullptr) - GDALClose(poODS); - delete poGCPCoordTrans; return nullptr; } } @@ -3044,9 +3035,6 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, { CPLError(CE_Failure, CPLE_AppDefined, "Couldn't fetch advertised layer %d!", iLayer); - if (hDstDS == nullptr) - GDALClose(poODS); - delete poGCPCoordTrans; return nullptr; } @@ -3085,9 +3073,6 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, { CPLError(CE_Failure, CPLE_AppDefined, "Couldn't fetch advertised layer %d!", iLayer); - if (hDstDS == nullptr) - GDALClose(poODS); - delete poGCPCoordTrans; return nullptr; } @@ -3106,9 +3091,6 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, poLayer->GetName()); if (!psOptions->bSkipFailures) { - if (hDstDS == nullptr) - GDALClose(poODS); - delete poGCPCoordTrans; return nullptr; } } @@ -3133,8 +3115,8 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, while (true) { OGRLayer *poFeatureLayer = nullptr; - OGRFeature *poFeature = poDS->GetNextFeature( - &poFeatureLayer, nullptr, pfnProgress, pProgressArg); + auto poFeature = std::unique_ptr(poDS->GetNextFeature( + &poFeatureLayer, nullptr, pfnProgress, pProgressArg)); if (poFeature == nullptr) break; std::map::const_iterator oIter = @@ -3142,7 +3124,7 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, if (oIter == oMapLayerToIdx.end()) { // Feature in a layer that is not a layer of interest. - OGRFeature::DestroyFeature(poFeature); + // nothing to do } else { @@ -3170,10 +3152,6 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, if (psInfo == nullptr && !psOptions->bSkipFailures) { - if (hDstDS == nullptr) - GDALClose(poODS); - delete poGCPCoordTrans; - OGRFeature::DestroyFeature(poFeature); return nullptr; } @@ -3186,9 +3164,9 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, int iLayer = oIter->second; TargetLayerInfo *psInfo = pasAssocLayers[iLayer].psInfo.get(); if ((psInfo == nullptr || - !oTranslator.Translate(poFeature, psInfo, 0, nullptr, - nTotalEventsDone, nullptr, nullptr, - psOptions.get())) && + !oTranslator.Translate(poFeature.release(), psInfo, 0, + nullptr, nTotalEventsDone, nullptr, + nullptr, psOptions.get())) && !psOptions->bSkipFailures) { CPLError( @@ -3201,8 +3179,6 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, nRetCode = 1; break; } - if (psInfo == nullptr) - OGRFeature::DestroyFeature(poFeature); } } // while true @@ -3230,9 +3206,6 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, if (psInfo == nullptr && !psOptions->bSkipFailures) { - if (hDstDS == nullptr) - GDALClose(poODS); - delete poGCPCoordTrans; return nullptr; } @@ -3262,9 +3235,6 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, { CPLError(CE_Failure, CPLE_AppDefined, "Couldn't fetch advertised layer %d!", iLayer); - if (hDstDS == nullptr) - GDALClose(poODS); - delete poGCPCoordTrans; return nullptr; } if (!poDS->IsLayerPrivate(iLayer)) @@ -3294,9 +3264,6 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, psOptions->aosLayers[iLayer]); if (!psOptions->bSkipFailures) { - if (hDstDS == nullptr) - GDALClose(poODS); - delete poGCPCoordTrans; return nullptr; } } @@ -3346,9 +3313,6 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, psOptions->osWHERE.c_str(), poLayer->GetName()); if (!psOptions->bSkipFailures) { - if (hDstDS == nullptr) - GDALClose(poODS); - delete poGCPCoordTrans; return nullptr; } } @@ -3502,8 +3466,6 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, } } - delete poGCPCoordTrans; - // Note: this guarantees that the file can be opened in a consistent state, // without requiring to close poODS, only if the driver declares // DCAP_FLUSHCACHE_CONSISTENT_STATE @@ -3511,10 +3473,13 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS, nRetCode = 1; if (nRetCode == 0) - return GDALDataset::ToHandle(poODS); + { + if (hDstDS) + return hDstDS; + else + return GDALDataset::ToHandle(poODSUniquePtr.release()); + } - if (hDstDS == nullptr) - GDALClose(poODS); return nullptr; } @@ -4550,8 +4515,10 @@ SetupTargetLayer::Setup(OGRLayer *poSrcLayer, const char *pszNewLayerName, oGeomFieldDefn.SetSpatialRef(poOutputSRS); oGeomFieldDefn.SetCoordinatePrecision(oCoordPrec); oGeomFieldDefn.SetNullable(bGeomFieldNullable); - poDstLayer = m_poDstDS->CreateLayer(pszNewLayerName, &oGeomFieldDefn, - papszLCOTemp); + poDstLayer = m_poDstDS->CreateLayer( + pszNewLayerName, + eGCreateLayerType == wkbNone ? nullptr : &oGeomFieldDefn, + papszLCOTemp); CSLDestroy(papszLCOTemp); if (poDstLayer == nullptr) diff --git a/apps/ogrinfo_lib.cpp b/apps/ogrinfo_lib.cpp index e287a7cb7ae7..42538bc65f6b 100644 --- a/apps/ogrinfo_lib.cpp +++ b/apps/ogrinfo_lib.cpp @@ -1191,10 +1191,12 @@ static void ReportOnLayer(CPLString &osRet, CPLJSONObject &oLayer, { std::string osCoordinateEpoch = CPLSPrintf("%f", dfCoordinateEpoch); - if (osCoordinateEpoch.find('.') != std::string::npos) + const size_t nDotPos = osCoordinateEpoch.find('.'); + if (nDotPos != std::string::npos) { - while (osCoordinateEpoch.back() == '0') - osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1); + while (osCoordinateEpoch.size() > nDotPos + 2 && + osCoordinateEpoch.back() == '0') + osCoordinateEpoch.pop_back(); } Concat(osRet, psOptions->bStdoutOutput, "Coordinate epoch: %s\n", osCoordinateEpoch.c_str()); @@ -2427,15 +2429,16 @@ static std::unique_ptr GDALVectorInfoOptionsGetParser( }) .help(_("Format/driver name(s) to try when opening the input file.")); - argParser->add_argument("filename") - .nargs(argparse::nargs_pattern::optional) - .action( - [psOptionsForBinary](const std::string &s) - { - if (psOptionsForBinary) - psOptionsForBinary->osFilename = s; - }) - .help(_("The data source to open.")); + auto &argFilename = argParser->add_argument("filename") + .action( + [psOptionsForBinary](const std::string &s) + { + if (psOptionsForBinary) + psOptionsForBinary->osFilename = s; + }) + .help(_("The data source to open.")); + if (!psOptionsForBinary) + argFilename.nargs(argparse::nargs_pattern::optional); argParser->add_argument("layer") .remaining() diff --git a/apps/sozip.cpp b/apps/sozip.cpp index f5d6f10a149f..1381e158ba1d 100644 --- a/apps/sozip.cpp +++ b/apps/sozip.cpp @@ -623,7 +623,14 @@ MAIN_START(nArgc, papszArgv) if (!bVerbose && !bQuiet) { +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnull-dereference" +#endif anFileSizes.resize(aosFiles.size()); +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif for (size_t i = 0; i < aosFiles.size(); ++i) { if (VSIStatL(aosFiles[i].c_str(), &sBuf) == 0) diff --git a/apps/test_ogrsf.cpp b/apps/test_ogrsf.cpp index 9b2edcaf3ec2..3f4592b4fd28 100644 --- a/apps/test_ogrsf.cpp +++ b/apps/test_ogrsf.cpp @@ -2001,7 +2001,7 @@ static int TestOGRLayerRandomWrite(OGRLayer *poLayer) { int bRet = TRUE; - OGRFeature *papoFeatures[5], *poFeature; + OGRFeature *papoFeatures[5]; memset(papoFeatures, 0, sizeof(papoFeatures)); @@ -2086,25 +2086,27 @@ static int TestOGRLayerRandomWrite(OGRLayer *poLayer) /* -------------------------------------------------------------------- */ /* Now re-read feature 2 to verify the effect stuck. */ /* -------------------------------------------------------------------- */ - poFeature = LOG_ACTION(poLayer->GetFeature(nFID5)); - if (poFeature == nullptr) - { - bRet = FALSE; - printf("ERROR: Attempt to GetFeature( nFID5 ) failed.\n"); - goto end; - } - if (!poFeature->Equal(papoFeatures[1])) - { - bRet = FALSE; - poFeature->DumpReadable(stderr); - papoFeatures[1]->DumpReadable(stderr); - printf("ERROR: Written feature didn't seem to retain value.\n"); - } - else if (bVerbose) { - printf("INFO: Random write test passed.\n"); + auto poFeature = + std::unique_ptr(LOG_ACTION(poLayer->GetFeature(nFID5))); + if (poFeature == nullptr) + { + bRet = FALSE; + printf("ERROR: Attempt to GetFeature( nFID5 ) failed.\n"); + goto end; + } + if (!poFeature->Equal(papoFeatures[1])) + { + bRet = FALSE; + poFeature->DumpReadable(stderr); + papoFeatures[1]->DumpReadable(stderr); + printf("ERROR: Written feature didn't seem to retain value.\n"); + } + else if (bVerbose) + { + printf("INFO: Random write test passed.\n"); + } } - DestroyFeatureAndNullify(poFeature); /* -------------------------------------------------------------------- */ /* Re-invert the features to restore to original state */ @@ -2130,6 +2132,156 @@ static int TestOGRLayerRandomWrite(OGRLayer *poLayer) printf("ERROR: Attempt to restore SetFeature(4) failed.\n"); } + /* -------------------------------------------------------------------- */ + /* Test UpdateFeature() */ + /* -------------------------------------------------------------------- */ + + if (bRet) + { + int nOldVal = 0; + std::string osOldVal; + int iUpdatedFeature = -1; + int iUpdatedField = -1; + std::unique_ptr poUpdatedFeature; + + for (int iFeature = 0; iUpdatedFeature < 0 && iFeature < 5; iFeature++) + { + for (int iField = 0; + iField < poLayer->GetLayerDefn()->GetFieldCount(); ++iField) + { + if (papoFeatures[iFeature]->IsFieldSetAndNotNull(iField)) + { + if (poLayer->GetLayerDefn() + ->GetFieldDefn(iField) + ->GetType() == OFTInteger) + { + iUpdatedFeature = iFeature; + iUpdatedField = iField; + nOldVal = + papoFeatures[iFeature]->GetFieldAsInteger(iField); + poUpdatedFeature.reset(papoFeatures[iFeature]->Clone()); + poUpdatedFeature->SetField(iField, 0xBEEF); + break; + } + else if (poLayer->GetLayerDefn() + ->GetFieldDefn(iField) + ->GetType() == OFTString) + { + iUpdatedFeature = iFeature; + iUpdatedField = iField; + osOldVal = + papoFeatures[iFeature]->GetFieldAsString(iField); + poUpdatedFeature.reset(papoFeatures[iFeature]->Clone()); + poUpdatedFeature->SetField(iField, "0xBEEF"); + break; + } + } + } + } + + if (poUpdatedFeature) + { + if (LOG_ACTION(poLayer->UpdateFeature(poUpdatedFeature.get(), 1, + &iUpdatedField, 0, nullptr, + false)) != OGRERR_NONE) + { + bRet = FALSE; + printf("ERROR: UpdateFeature() failed.\n"); + } + if (bRet) + { + LOG_ACTION(poLayer->ResetReading()); + for (int iFeature = 0; iFeature < 5; iFeature++) + { + auto poFeature = std::unique_ptr(LOG_ACTION( + poLayer->GetFeature(papoFeatures[iFeature]->GetFID()))); + if (iFeature != iUpdatedFeature) + { + if (!poFeature || + !poFeature->Equal(papoFeatures[iFeature])) + { + bRet = false; + printf("ERROR: UpdateFeature() test: " + "!poFeature->Equals(papoFeatures[iFeature]) " + "unexpected.\n"); + if (poFeature) + poFeature->DumpReadable(stdout); + papoFeatures[iFeature]->DumpReadable(stdout); + } + } + } + + auto poFeature = + std::unique_ptr(LOG_ACTION(poLayer->GetFeature( + papoFeatures[iUpdatedFeature]->GetFID()))); + if (!poFeature) + { + bRet = FALSE; + printf("ERROR: at line %d", __LINE__); + goto end; + } + if (poLayer->GetLayerDefn() + ->GetFieldDefn(iUpdatedField) + ->GetType() == OFTInteger) + { + if (poFeature->GetFieldAsInteger(iUpdatedField) != 0xBEEF) + { + bRet = FALSE; + printf("ERROR: Did not get expected field value after " + "UpdateFeature().\n"); + } + poFeature->SetField(iUpdatedField, nOldVal); + } + else if (poLayer->GetLayerDefn() + ->GetFieldDefn(iUpdatedField) + ->GetType() == OFTString) + { + if (!EQUAL(poFeature->GetFieldAsString(iUpdatedField), + "0xBEEF")) + { + bRet = FALSE; + printf("ERROR: Did not get expected field value after " + "UpdateFeature().\n"); + } + poFeature->SetField(iUpdatedField, osOldVal.c_str()); + } + else + { + CPLAssert(false); + } + + if (LOG_ACTION(poLayer->UpdateFeature( + poFeature.get(), 1, &iUpdatedField, 0, nullptr, + false)) != OGRERR_NONE) + { + bRet = FALSE; + printf("ERROR: UpdateFeature() failed.\n"); + } + + poFeature.reset(LOG_ACTION(poLayer->GetFeature( + papoFeatures[iUpdatedFeature]->GetFID()))); + if (!poFeature) + { + bRet = FALSE; + printf("ERROR: at line %d", __LINE__); + goto end; + } + if (!poFeature->Equal(papoFeatures[iUpdatedFeature])) + { + bRet = false; + printf("ERROR: UpdateFeature() test: " + "!poFeature->Equals(papoFeatures[iUpdatedFeature]) " + "unexpected.\n"); + } + } + } + else + { + if (bVerbose) + printf("INFO: Could not test UpdateFeature().\n"); + } + } + end: /* -------------------------------------------------------------------- */ /* Cleanup. */ diff --git a/ci/travis/conda/0001-Fix-build-of-Python-bindings-due-to-https-github.com.patch b/ci/travis/conda/0001-Fix-build-of-Python-bindings-due-to-https-github.com.patch deleted file mode 100644 index 19d6fe4db1f3..000000000000 --- a/ci/travis/conda/0001-Fix-build-of-Python-bindings-due-to-https-github.com.patch +++ /dev/null @@ -1,38 +0,0 @@ -From c8e8942b5dd50d1abb4369662b0cb9d282c3bf69 Mon Sep 17 00:00:00 2001 -From: Even Rouault -Date: Wed, 6 Dec 2023 19:06:08 +0100 -Subject: [PATCH] Fix build of Python bindings due to - https://github.com/OSGeo/gdal/pull/8926 changes - ---- - recipe/install_python.sh | 14 ++++++++------ - 1 file changed, 8 insertions(+), 6 deletions(-) - -diff --git a/recipe/install_python.sh b/recipe/install_python.sh -index 4bcd219..1561771 100644 ---- a/recipe/install_python.sh -+++ b/recipe/install_python.sh -@@ -21,12 +21,14 @@ cmake "-UPython*" \ - cmake --build . --target python_generated_files - cd swig/python - --cat >pyproject.toml <=40.8.0", "wheel"] --build-backend = "setuptools.build_meta" --EOF -- - $PYTHON setup.py build_ext - -+# Cf https://github.com/OSGeo/gdal/pull/8926 -+# The above build_ext has been run with numpy already installed in the environment -+# because otherwise it would have failed. -+# But as we run pip install without dependencies, we have to force -+# GDAL_PYTHON_BINDINGS_WITHOUT_NUMPY=YES to disable the related check. -+# This is OK here since the bindings have already been built, and it is just -+# a matter of bundling them in the wheel. -+export GDAL_PYTHON_BINDINGS_WITHOUT_NUMPY=YES - $PYTHON -m pip install --no-deps --ignore-installed . --- -2.25.1 - diff --git a/ci/travis/conda/bld.bat.patch b/ci/travis/conda/bld.bat.patch deleted file mode 100644 index 7bb286166552..000000000000 --- a/ci/travis/conda/bld.bat.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/recipe/bld.bat b/recipe/bld.bat -index b6616ae..b98a480 100644 ---- a/recipe/bld.bat -+++ b/recipe/bld.bat -@@ -32,6 +32,8 @@ cmake -G "Ninja" ^ - -DGDAL_USE_PARQUET=OFF ^ - -DGDAL_USE_ARROW=OFF ^ - -DGDAL_USE_ARROWDATASET=OFF ^ -+ -DOGR_REGISTER_DRIVER_ARROW_FOR_LATER_PLUGIN=ON ^ -+ -DOGR_REGISTER_DRIVER_PARQUET_FOR_LATER_PLUGIN=ON ^ - "%SRC_DIR%" - - if errorlevel 1 exit /b 1 diff --git a/ci/travis/conda/build.sh.patch b/ci/travis/conda/build.sh.patch deleted file mode 100644 index 373b5103c2ff..000000000000 --- a/ci/travis/conda/build.sh.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/recipe/build.sh b/recipe/build.sh -index a3474a2..249dc89 100644 ---- a/recipe/build.sh -+++ b/recipe/build.sh -@@ -23,6 +23,8 @@ cmake -G "Unix Makefiles" \ - -DGDAL_USE_PARQUET=OFF \ - -DGDAL_USE_ARROW=OFF \ - -DGDAL_USE_ARROWDATASET=OFF \ -+ -DOGR_REGISTER_DRIVER_ARROW_FOR_LATER_PLUGIN=ON \ -+ -DOGR_REGISTER_DRIVER_PARQUET_FOR_LATER_PLUGIN=ON \ - -DGDAL_ENABLE_HDF5_GLOBAL_LOCK:BOOL=ON \ - -DBUILD_PYTHON_BINDINGS:BOOL=OFF \ - -DBUILD_JAVA_BINDINGS:BOOL=OFF \ diff --git a/ci/travis/conda/meta.yaml.patch b/ci/travis/conda/meta.yaml.patch new file mode 100644 index 000000000000..96047817d95b --- /dev/null +++ b/ci/travis/conda/meta.yaml.patch @@ -0,0 +1,28 @@ +diff --git a/recipe/meta.yaml b/recipe/meta.yaml +index f3afcef..eb825ae 100644 +--- a/recipe/meta.yaml ++++ b/recipe/meta.yaml +@@ -31,6 +31,7 @@ requirements: + - {{ stdlib("c") }} + - {{ compiler('cxx') }} + - swig ++ - setuptools + host: + - blosc + - expat +@@ -614,6 +615,7 @@ outputs: + - python # [build_platform != target_platform] + - cross-python_{{ target_platform }} # [build_platform != target_platform] + - numpy # [build_platform != target_platform] ++ - setuptools + - swig + host: + - python +@@ -623,6 +625,7 @@ outputs: + - libkml-devel + - libxml2 + - expat ++ - setuptools + run: + - python + - libgdal-core {{ version }}.* diff --git a/ci/travis/conda/setup.sh b/ci/travis/conda/setup.sh index 6692df1b9c29..44da66498178 100755 --- a/ci/travis/conda/setup.sh +++ b/ci/travis/conda/setup.sh @@ -6,6 +6,9 @@ conda config --show-sources rm -f ~/.condarc +# Cf https://github.com/conda-forge/gdal-feedstock/pull/939 +conda config --add channels conda-forge/label/numpy_rc + conda config --show-sources conda config --show @@ -16,12 +19,7 @@ git clone https://github.com/conda-forge/gdal-feedstock.git cd gdal-feedstock -# Set -DOGR_REGISTER_DRIVER_ARROW_FOR_LATER_PLUGIN=ON and -DOGR_REGISTER_DRIVER_PARQUET_FOR_LATER_PLUGIN=ON -# To be dropped once that's upstreamed in conda-forge/gdal-feedstock.git -patch -p1 < ../ci/travis/conda/build.sh.patch -patch -p1 --binary < ../ci/travis/conda/bld.bat.patch - -patch -p1 < ../ci/travis/conda/0001-Fix-build-of-Python-bindings-due-to-https-github.com.patch +patch -p1 < ../ci/travis/conda/meta.yaml.patch cat > recipe/recipe_clobber.yaml < [SOURCES [[...]]] + [CORE_SOURCES [[...]]] + NO_SHARED_SYMBOL_WITH_CORE BUILTIN | PLUGIN_CAPABLE | [PLUGIN_CAPABLE_IF ] [NO_DEPS] ) @@ -31,6 +33,34 @@ GdalDriverHelper The NO_DEPS option express that the driver has no non-core external dependencies. + The CORE_SOURCES option points to a list of files that are used for deferred plugin + loading (cf RFC 96 https://gdal.org/development/rfc/rfc96_deferred_plugin_loading.html), + when the driver is built as a plugin. + Those files are build in libgdal so it has access to plugin metadata and the + identification method, and are typically exported so that the driver can also + access them. + However it has been found that this resulted in the inability of building a + plugin for a libgdal that hadn't been built with any form of support for it, + which is undesirable. + Starting with GDAL 3.9.1, NO_SHARED_SYMBOL_WITH_CORE must also be declared + when CORE_SOURCES is declared, so that the files pointed by CORE_SOURCES + are built twice: once in libgdal with a "GDAL_core_" prefix, and another time + in the plugin itself with a "GDAL_driver_" prefix, by using the + PLUGIN_SYMBOL_NAME() macro of gdal_priv.h + + Example in ogrocidrivercore.h: + + #define OGROCIDriverIdentify \ + PLUGIN_SYMBOL_NAME(OGROCIDriverIdentify) + #define OGROCIDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(OGROCIDriverSetCommonMetadata) + + int OGROCIDriverIdentify(GDALOpenInfo *poOpenInfo); + + void OGROCIDriverSetCommonMetadata(GDALDriver *poDriver); + + + There are several examples to show how to write build cmake script. ex.1 Driver which is referenced by other drivers @@ -91,7 +121,7 @@ function(_set_driver_core_sources _KEY _DRIVER_TARGET) endfunction() function(add_gdal_driver) - set(_options BUILTIN PLUGIN_CAPABLE NO_DEPS STRONG_CXX_WFLAGS CXX_WFLAGS_EFFCXX NO_CXX_WFLAGS) + set(_options BUILTIN PLUGIN_CAPABLE NO_DEPS STRONG_CXX_WFLAGS CXX_WFLAGS_EFFCXX NO_CXX_WFLAGS NO_SHARED_SYMBOL_WITH_CORE) set(_oneValueArgs TARGET DESCRIPTION DEF PLUGIN_CAPABLE_IF DRIVER_NAME_OPTION) set(_multiValueArgs SOURCES CORE_SOURCES) cmake_parse_arguments(_DRIVER "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) @@ -254,6 +284,15 @@ function(add_gdal_driver) set_property(GLOBAL APPEND PROPERTY PLUGIN_MODULES ${_DRIVER_TARGET}) if (_DRIVER_CORE_SOURCES) + if (_DRIVER_NO_SHARED_SYMBOL_WITH_CORE) + foreach(f IN LISTS _DRIVER_CORE_SOURCES) + # Create a separate source file, to make sure we get a different object file + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/for_driver_${f}" "#include \"${CMAKE_CURRENT_SOURCE_DIR}/${f}\"") + target_sources(${_DRIVER_TARGET} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/for_driver_${f}") + endforeach() + else() + message(FATAL_ERROR "Driver ${_DRIVER_TARGET} should declare DRIVER_NO_SHARED_SYMBOL_WITH_CORE") + endif() _set_driver_core_sources(${_KEY} ${_DRIVER_TARGET} ${_DRIVER_CORE_SOURCES}) endif () diff --git a/cmake/helpers/configure.cmake b/cmake/helpers/configure.cmake index 6aee8f7d6487..55b1df0f6d6e 100644 --- a/cmake/helpers/configure.cmake +++ b/cmake/helpers/configure.cmake @@ -226,13 +226,13 @@ else () #endif #include #include - int main() { struct _stat64 buf; _wstat64( \"\", &buf ); return 0; } + int main() { struct _stat64 buf; wchar_t path = 0; _wstat64( &path, &buf ); return 0; } " - NO_UNIX_STDIO_64) + WINDOWS_STAT64) - if (NO_UNIX_STDIO_64) + if (WINDOWS_STAT64) set(VSI_STAT64 _stat64) - set(VSI_STAT64_T __stat64) + set(VSI_STAT64_T _stat64) endif () check_function_exists(fopen64 HAVE_FOPEN64) diff --git a/doc/source/development/rfc/rfc96_deferred_plugin_loading.rst b/doc/source/development/rfc/rfc96_deferred_plugin_loading.rst index 3d620115a764..dbaf1386dcdc 100644 --- a/doc/source/development/rfc/rfc96_deferred_plugin_loading.rst +++ b/doc/source/development/rfc/rfc96_deferred_plugin_loading.rst @@ -494,6 +494,66 @@ Related issues and PRs - https://github.com/OSGeo/gdal/pull/8695: candidate implementation +Adjustments done post GDAL 3.9.0, for GDAL 3.9.1 +------------------------------------------------ + +After GDAL 3.9.0 release, it has been noticed that the following setup which +used to work in prior releases no longer worked: + +- Step 1: building libgdal without support for driver X +- Step 2: building driver X as a plugin, discarding the libgdal share library, + built at that stage +- Step 3: using driver X built as a plugin against libgdal built at step 1. In that + scenario, driver X is expected to be loaded as if it was an out-of-tree drivers. + +Such scenario is used when delivering a fully open-source libgdal without any +prior knowledge of which drivers could be later built as plugins, or for which +pre-configuring libgdal to support such drivers is not practical because they +rely on a proprietary SDK and the identification method and/or driver metadata +depends on the availability of the SDK include files (e.g. MrSID). + +Starting with GDAL 3.9.1, the ``add_gdal_driver()`` function in the CMakeLists.txt +of drivers which use the ``CORE_SOURCES`` keyword must also declare the +``NO_SHARED_SYMBOL_WITH_CORE`` keyword, so that the files pointed by CORE_SOURCES +are built twice: once in libgdal with a ``GDAL_core_`` prefix, and another time +in the plugin itself with a ``GDAL_driver_`` prefix, by using the +PLUGIN_SYMBOL_NAME() macro of :file:`gdal_priv.h`. + +Example in ogr/ogrsf_frmsts/oci/CMakeLists.txt: + +.. code-block:: + + add_gdal_driver(TARGET ogr_OCI + SOURCES ${SOURCE} + CORE_SOURCES ogrocidrivercore.cpp + PLUGIN_CAPABLE + NO_SHARED_SYMBOL_WITH_CORE) + + +Example in ogrocidrivercore.h: + +.. code-block:: cpp + + #define OGROCIDriverIdentify \ + PLUGIN_SYMBOL_NAME(OGROCIDriverIdentify) + #define OGROCIDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(OGROCIDriverSetCommonMetadata) + + int OGROCIDriverIdentify(GDALOpenInfo *poOpenInfo); + + void OGROCIDriverSetCommonMetadata(GDALDriver *poDriver); + + +A consequence of that change is that drivers built as a plugin against GDAL 3.9.0 +will not be loadable by GDAL 3.9.1 (or later patch in the 3.9 series), because +they relied on driver-specific functions that are no longer exported by libgdal >= 3.9.1. + +After that, things should work as they used to, and drivers built against libgdal 3.9.1 +should work against libgdal 3.9.2 for example. + +Also note that the above only affects *in-tree* plugin drivers. Out-of-tree plugin drivers +are not affected. + Voting history -------------- diff --git a/doc/source/drivers/raster/gti.rst b/doc/source/drivers/raster/gti.rst index 6987e97360fa..fb1f8ad8d7f7 100644 --- a/doc/source/drivers/raster/gti.rst +++ b/doc/source/drivers/raster/gti.rst @@ -188,28 +188,43 @@ PostGIS, ...), the following layer metadata items may be set: Unit of the band. -* ``OVERVIEW__DATASET=`` where idx is an integer index starting at 0. +* ``OVERVIEW__DATASET=`` where idx is an integer index (starting at 0 + since GDAL 3.9.2, starting at 1 in GDAL 3.9.0 and 3.9.1) Name of the dataset to use as the first overview level. This may be a raster dataset (for example a GeoTIFF file, or another GTI dataset). This may also be a vector dataset with a GTI compatible layer, potentially specified with ``OVERVIEW__LAYER``. -* ``OVERVIEW__OPEN_OPTIONS=[,key2=value2]...`` where idx is an integer index starting at 0. + Starting with GDAL 3.9.2, overviews of ``OVERVIEW__DATASET=`` + are also automatically added, unless ``OVERVIEW__OPEN_OPTIONS=OVERVIEW_LEVEL=NONE`` + is specified. + +* ``OVERVIEW__OPEN_OPTIONS=[,key2=value2]...`` where idx is an integer index (starting at 0 + since GDAL 3.9.2, starting at 1 in GDAL 3.9.0 and 3.9.1) Open options(s) to use to open ``OVERVIEW__DATASET``. -* ``OVERVIEW__LAYER=`` where idx is an integer index starting at 0. +* ``OVERVIEW__LAYER=`` where idx is an integer index (starting at 0 + since GDAL 3.9.2, starting at 1 in GDAL 3.9.0 and 3.9.1) + + Only taken into account if ``OVERVIEW__DATASET=`` is not specified, + or points to a GTI dataset. Name of the vector layer to use as the first overview level, assuming ``OVERVIEW__DATASET`` points to a vector dataset. ``OVERVIEW__DATASET`` may also not be specified, in which case the vector dataset of the full resolution virtual mosaic is used. -* ``OVERVIEW__FACTOR=`` where idx is an integer index starting at 0. +* ``OVERVIEW__FACTOR=`` where idx is an integer index (starting at 0 + since GDAL 3.9.2, starting at 1 in GDAL 3.9.0 and 3.9.1) + + Sub-sampling factor, strictly greater than 1. - Sub-sampling factor, strictly greater than 1. If ``OVERVIEW__DATASET`` - and ``OVERVIEW__LAYER`` are not specified, then all tiles of the full + Only taken into account if ``OVERVIEW__DATASET=`` is not specified, + or points to a GTI dataset. + + If ``OVERVIEW__DATASET`` and ``OVERVIEW__LAYER`` are not specified, then all tiles of the full resolution virtual mosaic are used, with the specified sub-sampling factor (it is recommended, but not required, that those tiles do have a corresponding overview). ``OVERVIEW__DATASET`` and/or ``OVERVIEW__LAYER`` may also be @@ -219,6 +234,8 @@ All overviews *must* have exactly the same extent as the full resolution virtual mosaic. The GTI driver does not check that, and if that condition is not met, subsampled pixel request will lead to incorrect result. +They also must be listed by decreasing size with increasing overview index. + In addition to those layer metadata items, the dataset-level metadata item ``TILE_INDEX_LAYER`` may be set to indicate, for dataset with multiple layers, which one should be used as the tile index layer. @@ -307,7 +324,8 @@ mentioned in the previous section. some.tif diff --git a/doc/source/drivers/raster/gtiff.rst b/doc/source/drivers/raster/gtiff.rst index da225c74ce49..478425149a50 100644 --- a/doc/source/drivers/raster/gtiff.rst +++ b/doc/source/drivers/raster/gtiff.rst @@ -1117,6 +1117,34 @@ the default behavior of the GTiff driver. expected to be necessary, unless GDAL is incorrectly determining the disk space available on the destination file system. +- .. config:: GTIFF_READ_ANGULAR_PARAMS_IN_DEGREE + :choices: YES, NO + :default: NO + :since: 3.9.1 + + Conformant GeoTIFF files should have the values of angular projection + parameters written in the unit of the GeogAngularUnitsGeoKey. But some + non-conformant implementations, such as GDAL <= 3.9.0, always wrote them + in degrees. + This option can be set to YES when reading such non-conformant GeoTIFF + files (typically using grads), to instruct GDAL (>= 3.9.1) that the projection + parameters are in degrees, instead of being expressed in the unit of the + GeogAngularUnitsGeoKey. + +- .. config:: GTIFF_WRITE_ANGULAR_PARAMS_IN_DEGREE + :choices: YES, NO + :default: NO + :since: 3.9.1 + + Conformant GeoTIFF files should have the values of angular projection + parameters written in the unit of the GeogAngularUnitsGeoKey. But some + non-conformant implementations, such as GDAL >= 3.0 and <= 3.9.0, assumed + those values to be in degree. + This option can be set to YES to force writing such non-conformant GeoTIFF + files. It should *not* be nominally used, except to workaround interoperability + issues. + + Codec Recommendations --------------------- diff --git a/doc/source/drivers/raster/stacit.rst b/doc/source/drivers/raster/stacit.rst index 30db39e92ab1..0eea636ab768 100644 --- a/doc/source/drivers/raster/stacit.rst +++ b/doc/source/drivers/raster/stacit.rst @@ -18,10 +18,6 @@ Thus, translating it into VRT will result in a VRT file that directly references Note that `STAC API ItemCollections `_ are not the same as `STAC Collections `_. STAC API ItemCollections are GeoJSON FeatureCollections enhanced with STAC entities. -Note that when the ItemCollections contains overlapping items, and that some items -are fully covered by other items that are more recent, the STACIT virtual mosaic will -not list those fully covered items not participating to the pixel values of the mosaic. - Open syntax ----------- @@ -66,6 +62,28 @@ The following open options are supported: Strategy to use to determine dataset resolution. +- .. oo:: OVERLAP_STRATEGY + :choices: REMOVE_IF_NO_NODATA, USE_ALL, USE_MOST_RECENT + :default: REMOVE_IF_NO_NODATA + :since: 3.9.1 + + Strategy to use when the ItemCollections contains overlapping items, and + that some items are fully covered by other items that are more recent. + + Starting with GDAL 3.9.1, the ``REMOVE_IF_NO_NODATA`` strategy is applied + by default. The STACIT virtual mosaic will omit fully covered items, + only if no band declares a nodata value. + (Note that the determination whether a band has a nodata value of not is + done by opening one of the items, and assuming it is representative of + the characteristics of the others in the collection). + + This strategy can be forced in all cases by selecting the ``USE_MOST_RECENT`` + strategy (this was the strategy applied prior to 3.9.1) + + The ``USE_ALL`` strategy always causes all items to be listed in the virtual + mosaic, with the most recent ones being rendered on top of the less recent ones. + + Subdatasets ----------- diff --git a/doc/source/drivers/raster/vrt.rst b/doc/source/drivers/raster/vrt.rst index cbd911e3d090..c3dc618d7ff5 100644 --- a/doc/source/drivers/raster/vrt.rst +++ b/doc/source/drivers/raster/vrt.rst @@ -423,8 +423,9 @@ the following form: The intermediary values are calculated using a linear interpolation between the bounding destination values of the corresponding range. -Source values should be monotonically non-decreasing. Clamping is performed for -input pixel values outside of the range specified by the LUT. That is, if an +Source values should be listed in a monotonically non-decreasing order. +If there is a Not-A-Number (NaN) source value, it should be the first one. +Clamping is performed for input pixel values outside of the range specified by the LUT. That is, if an input pixel value is lower than the minimum source value, then the destination value corresponding to that minimum source value is used as the output pixel value. And similarly for an input pixel value that is greater than the maximum source value. diff --git a/doc/source/drivers/vector/gml.rst b/doc/source/drivers/vector/gml.rst index 5d1e20b98329..90e5dcafb312 100644 --- a/doc/source/drivers/vector/gml.rst +++ b/doc/source/drivers/vector/gml.rst @@ -623,6 +623,14 @@ Open options Whether to use gml:boundedBy at feature level as feature geometry, if there are no other geometry. + +.. note:: + + When changing the value of most of the above options, it is recommended to + delete the ``.gfs`` file if it pre-exists, otherwise mis-behavior might be + observed. + + Creation Issues --------------- diff --git a/doc/source/drivers/vector/pg.rst b/doc/source/drivers/vector/pg.rst index 2e5e26c5e7c8..c66e902d3c67 100644 --- a/doc/source/drivers/vector/pg.rst +++ b/doc/source/drivers/vector/pg.rst @@ -449,6 +449,15 @@ available: be destroyed. Typical use case: ``ogr2ogr -append PG:dbname=foo abc.shp --config OGR_TRUNCATE YES``. +- .. config:: OGR_PG_ENABLE_METADATA + :choices: YES, NO + :default: YES + :since: 3.9 + + If set to "YES" (the default), the driver will try to use (and potentially + create) the ``ogr_system_tables.metadata`` table to retrieve and store + layer metadata. + Examples ~~~~~~~~ diff --git a/doc/source/programs/gdal_rasterize.rst b/doc/source/programs/gdal_rasterize.rst index e7a08808509b..4d0cf59c5b39 100644 --- a/doc/source/programs/gdal_rasterize.rst +++ b/doc/source/programs/gdal_rasterize.rst @@ -172,7 +172,9 @@ raster data is only supported since GDAL 2.1.0. .. option:: -ot - Force the output bands to be of the indicated data type. Defaults to ``Float64`` + Force the output bands to be of the indicated data type. Defaults to ``Float64``, + unless the attribute field to burn is of type ``Int64``, in which case ``Int64`` + is used for the output raster data type if the output driver supports it. .. option:: -optim {AUTO|VECTOR|RASTER} diff --git a/doc/source/programs/gdal_retile.rst b/doc/source/programs/gdal_retile.rst index 7b89aeeebdf0..366dc018a4be 100644 --- a/doc/source/programs/gdal_retile.rst +++ b/doc/source/programs/gdal_retile.rst @@ -33,6 +33,7 @@ Description This utility will retile a set of input tile(s). All the input tile(s) must be georeferenced in the same coordinate system and have a matching number of bands. +The geotransform matrix of input tiles must not contain rotation terms. Optionally pyramid levels are generated. All pyramid levels are generated from the input tiles (not from previous levels). diff --git a/doc/source/programs/gdalwarp.rst b/doc/source/programs/gdalwarp.rst index 639fa780d9be..042de075e33c 100644 --- a/doc/source/programs/gdalwarp.rst +++ b/doc/source/programs/gdalwarp.rst @@ -15,27 +15,36 @@ Synopsis .. code-block:: - gdalwarp [--help] [--long-usage] [--help-general] - [--quiet] [-overwrite] [-of ] [-co =]... [-s_srs ] - [-t_srs ] - [[-srcalpha]|[-nosrcalpha]] - [-dstalpha] [-tr |square] [-ts ] [-te ] [-r near|bilinear|cubic|cubicspline|lanczos|average|rms|mode|min|max|med|q1|q3|sum] - [-ot Byte|Int8|[U]Int{16|32|64}|CInt{16|32}|[C]Float{32|64}] - ... - - Advanced options: - [-wo =]... [-multi] [-s_coord_epoch ] [-t_coord_epoch ] [-ct ] - [[-tps]|[-rpc]|[-geoloc]] - [-order <1|2|3>] [-refine_gcps []] [-to =]... - [-et ] [-wm ] [-srcnodata [ ...]] - [-dstnodata [ ...]] [-tap] [-wt Byte|Int8|[U]Int{16|32|64}|CInt{16|32}|[C]Float{32|64}] - [-cutline |] [-cutline_srs ] [-cwhere ] - [[-cl ]|[-csql ]] - [-cblend ] [-crop_to_cutline] [-nomd] [-cvmd ] [-setci] - [-oo =]... [-doo =]... [-ovr |AUTO|AUTO-|NONE] - [[-vshift]|[-novshiftgrid]] - [-if ]... [-srcband ]... [-dstband ]... + gdalwarp [--help] [--long-usage] [--help-general] + [--quiet] [-overwrite] [-of ] [-co =]... + [-s_srs ] [-t_srs ] + [[-srcalpha]|[-nosrcalpha]] + [-dstalpha] [-tr |square] [-ts ] + [-te ] + [-r near|bilinear|cubic|cubicspline|lanczos|average|rms|mode|min|max|med|q1|q3|sum] + [-ot Byte|Int8|[U]Int{16|32|64}|CInt{16|32}|[C]Float{32|64}] + ... + + Advanced options: + [-wo =]... [-multi] + [-s_coord_epoch ] [-t_coord_epoch ] [-ct ] + [[-tps]|[-rpc]|[-geoloc]] + [-order <1|2|3>] [-refine_gcps []] + [-to =]... + [-et ] [-wm ] + [-srcnodata "[ ]..."] + [-dstnodata "[ ]..."] [-tap] + [-wt Byte|Int8|[U]Int{16|32|64}|CInt{16|32}|[C]Float{32|64}] + [-cutline |] [-cutline_srs ] + [-cwhere ] + [[-cl ]|[-csql ]] + [-cblend ] [-crop_to_cutline] + [-nomd] [-cvmd ] [-setci] + [-oo =]... [-doo =]... + [-ovr |AUTO|AUTO-|NONE] + [[-vshift]|[-novshiftgrid]] + [-if ]... [-srcband ]... [-dstband ]... Description diff --git a/docker/ubuntu-full/Dockerfile b/docker/ubuntu-full/Dockerfile index 18b1fad57efe..fd221fc25526 100644 --- a/docker/ubuntu-full/Dockerfile +++ b/docker/ubuntu-full/Dockerfile @@ -233,7 +233,7 @@ RUN . /buildscripts/bh-set-envvars.sh \ && rm -rf /var/lib/apt/lists/* # Install Arrow C++ -ARG ARROW_VERSION=16.0.0-1 +ARG ARROW_VERSION=16.1.0-1 # ARROW_SOVERSION to be updated in the "Build final image" section too ARG ARROW_SOVERSION=1600 RUN . /buildscripts/bh-set-envvars.sh \ diff --git a/frmts/aaigrid/aaigriddataset.cpp b/frmts/aaigrid/aaigriddataset.cpp index 098a8a47f474..4fc099611788 100644 --- a/frmts/aaigrid/aaigriddataset.cpp +++ b/frmts/aaigrid/aaigriddataset.cpp @@ -606,6 +606,7 @@ int AAIGDataset::ParseHeader(const char *pszHeader, const char *pszDataType) if (pszDataType == nullptr && (strchr(pszNoData, '.') != nullptr || strchr(pszNoData, ',') != nullptr || + std::isnan(dfNoDataValue) || std::numeric_limits::min() > dfNoDataValue || dfNoDataValue > std::numeric_limits::max())) { @@ -718,7 +719,7 @@ int GRASSASCIIDataset::ParseHeader(const char *pszHeader, dfNoDataValue = CPLAtofM(pszNoData); if (pszDataType == nullptr && (strchr(pszNoData, '.') != nullptr || - strchr(pszNoData, ',') != nullptr || + strchr(pszNoData, ',') != nullptr || std::isnan(dfNoDataValue) || std::numeric_limits::min() > dfNoDataValue || dfNoDataValue > std::numeric_limits::max())) { diff --git a/frmts/basisu_ktx2/CMakeLists.txt b/frmts/basisu_ktx2/CMakeLists.txt index 4811ac8e81ce..413f9558c8fa 100644 --- a/frmts/basisu_ktx2/CMakeLists.txt +++ b/frmts/basisu_ktx2/CMakeLists.txt @@ -8,7 +8,8 @@ add_gdal_driver(TARGET gdal_BASISU_KTX2 ktx2drivercore.cpp commoncore.cpp STRONG_CXX_WFLAGS - PLUGIN_CAPABLE) + PLUGIN_CAPABLE + NO_SHARED_SYMBOL_WITH_CORE) if(NOT TARGET gdal_BASISU_KTX2) return() diff --git a/frmts/basisu_ktx2/basisudrivercore.h b/frmts/basisu_ktx2/basisudrivercore.h index ca2610a6c6b9..26723f7e45d4 100644 --- a/frmts/basisu_ktx2/basisudrivercore.h +++ b/frmts/basisu_ktx2/basisudrivercore.h @@ -33,8 +33,12 @@ constexpr const char *BASISU_DRIVER_NAME = "BASISU"; -int CPL_DLL BASISUDriverIdentify(GDALOpenInfo *poOpenInfo); +#define BASISUDriverIdentify PLUGIN_SYMBOL_NAME(BASISUDriverIdentify) +#define BASISUDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(BASISUDriverSetCommonMetadata) -void CPL_DLL BASISUDriverSetCommonMetadata(GDALDriver *poDriver); +int BASISUDriverIdentify(GDALOpenInfo *poOpenInfo); + +void BASISUDriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/basisu_ktx2/ktx2drivercore.h b/frmts/basisu_ktx2/ktx2drivercore.h index e5070a8fb947..23729a5e0fa8 100644 --- a/frmts/basisu_ktx2/ktx2drivercore.h +++ b/frmts/basisu_ktx2/ktx2drivercore.h @@ -33,8 +33,12 @@ constexpr const char *KTX2_DRIVER_NAME = "KTX2"; -int CPL_DLL KTX2DriverIdentify(GDALOpenInfo *poOpenInfo); +#define KTX2DriverIdentify PLUGIN_SYMBOL_NAME(KTX2DriverIdentify) +#define KTX2DriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(KTX2DriverSetCommonMetadata) -void CPL_DLL KTX2DriverSetCommonMetadata(GDALDriver *poDriver); +int KTX2DriverIdentify(GDALOpenInfo *poOpenInfo); + +void KTX2DriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/dds/CMakeLists.txt b/frmts/dds/CMakeLists.txt index a74fb706262b..23f3ac064afd 100644 --- a/frmts/dds/CMakeLists.txt +++ b/frmts/dds/CMakeLists.txt @@ -1,7 +1,8 @@ add_gdal_driver(TARGET gdal_DDS SOURCES ddsdataset.cpp CORE_SOURCES ddsdrivercore.cpp - PLUGIN_CAPABLE) + PLUGIN_CAPABLE + NO_SHARED_SYMBOL_WITH_CORE) if(NOT TARGET gdal_DDS) return() diff --git a/frmts/dds/ddsdrivercore.h b/frmts/dds/ddsdrivercore.h index d500e944f08c..31f4274aafa1 100644 --- a/frmts/dds/ddsdrivercore.h +++ b/frmts/dds/ddsdrivercore.h @@ -36,8 +36,12 @@ constexpr const char *DRIVER_NAME = "DDS"; #define DDS_SIGNATURE "DDS " -int CPL_DLL DDSDriverIdentify(GDALOpenInfo *poOpenInfo); +#define DDSDriverIdentify PLUGIN_SYMBOL_NAME(DDSDriverIdentify) +#define DDSDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(DDSDriverSetCommonMetadata) -void CPL_DLL DDSDriverSetCommonMetadata(GDALDriver *poDriver); +int DDSDriverIdentify(GDALOpenInfo *poOpenInfo); + +void DDSDriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/ecw/CMakeLists.txt b/frmts/ecw/CMakeLists.txt index ec8fdf98dc5d..8369bad2452a 100644 --- a/frmts/ecw/CMakeLists.txt +++ b/frmts/ecw/CMakeLists.txt @@ -4,7 +4,8 @@ add_gdal_driver( SOURCES ${SOURCE} CORE_SOURCES ecwdrivercore.cpp DRIVER_NAME_OPTION ECW - DEF FRMT_ecw PLUGIN_CAPABLE) + DEF FRMT_ecw PLUGIN_CAPABLE + NO_SHARED_SYMBOL_WITH_CORE) if(TARGET gdal_ECW_JP2ECW_core) target_include_directories(gdal_ECW_JP2ECW_core PRIVATE $) diff --git a/frmts/ecw/ecwdrivercore.h b/frmts/ecw/ecwdrivercore.h index 2d99273c3e48..dedc043a0ac0 100644 --- a/frmts/ecw/ecwdrivercore.h +++ b/frmts/ecw/ecwdrivercore.h @@ -35,12 +35,20 @@ constexpr const char *ECW_DRIVER_NAME = "ECW"; constexpr const char *JP2ECW_DRIVER_NAME = "JP2ECW"; -int CPL_DLL ECWDatasetIdentifyECW(GDALOpenInfo *poOpenInfo); +#define ECWDatasetIdentifyECW PLUGIN_SYMBOL_NAME(ECWDatasetIdentifyECW) +#define ECWDatasetIdentifyJPEG2000 \ + PLUGIN_SYMBOL_NAME(ECWDatasetIdentifyJPEG2000) +#define ECWDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(ECWDriverSetCommonMetadata) +#define JP2ECWDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(JP2ECWDriverSetCommonMetadata) -int CPL_DLL ECWDatasetIdentifyJPEG2000(GDALOpenInfo *poOpenInfo); +int ECWDatasetIdentifyECW(GDALOpenInfo *poOpenInfo); -void CPL_DLL ECWDriverSetCommonMetadata(GDALDriver *poDriver); +int ECWDatasetIdentifyJPEG2000(GDALOpenInfo *poOpenInfo); -void CPL_DLL JP2ECWDriverSetCommonMetadata(GDALDriver *poDriver); +void ECWDriverSetCommonMetadata(GDALDriver *poDriver); + +void JP2ECWDriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/esric/esric_dataset.cpp b/frmts/esric/esric_dataset.cpp index c8e9867a0622..c92d7132d512 100644 --- a/frmts/esric/esric_dataset.cpp +++ b/frmts/esric/esric_dataset.cpp @@ -34,6 +34,8 @@ #include #include #include "cpl_json.h" +#include "gdal_proxy.h" +#include "gdal_utils.h" using namespace std; @@ -67,9 +69,29 @@ static int IdentifyJSON(GDALOpenInfo *poOpenInfo) if (!ENDS_WITH_CI(poOpenInfo->pszFilename, "root.json")) return false; #endif - CPLString header(reinterpret_cast(poOpenInfo->pabyHeader), - poOpenInfo->nHeaderBytes); - return (CPLString::npos != header.find("tileBundlesPath")); + for (int i = 0; i < 2; ++i) + { + const std::string osHeader( + reinterpret_cast(poOpenInfo->pabyHeader), + poOpenInfo->nHeaderBytes); + if (std::string::npos != osHeader.find("tileBundlesPath")) + { + return true; + } + // If we didn't find tileBundlesPath i, the first bytes, but find + // other elements typically of .tpkx, then ingest more bytes and + // retry + constexpr int MORE_BYTES = 8192; + if (poOpenInfo->nHeaderBytes < MORE_BYTES && + (std::string::npos != osHeader.find("tileInfo") || + std::string::npos != osHeader.find("tileImageInfo"))) + { + poOpenInfo->TryToIngest(MORE_BYTES); + } + else + break; + } + return false; } // Without full XML parsing, weak, might still fail @@ -180,6 +202,8 @@ class ECDataset final : public GDALDataset } static GDALDataset *Open(GDALOpenInfo *poOpenInfo); + static GDALDataset *Open(GDALOpenInfo *poOpenInfo, + const char *pszDescription); protected: double GeoTransform[6]; @@ -196,9 +220,13 @@ class ECDataset final : public GDALDataset CPLErr InitializeFromJSON(const CPLJSONObject &oRoot); CPLString compression; std::vector resolutions; + int m_nMinLOD = 0; OGRSpatialReference oSRS; std::vector tilebuffer; // Last read tile, decompressed std::vector filebuffer; // raw tile buffer + + OGREnvelope m_sInitialExtent{}; + OGREnvelope m_sFullExtent{}; }; class ECBand final : public GDALRasterBand @@ -260,6 +288,8 @@ CPLErr ECDataset::Initialize(CPLXMLNode *CacheInfo) TSZ = static_cast(CPLAtof(CPLGetXMLValue(TCI, "TileCols", "256"))); if (TSZ != CPLAtof(CPLGetXMLValue(TCI, "TileRows", "256"))) throw CPLString("Non-square tiles are not supported"); + if (TSZ < 0 || TSZ > 8192) + throw CPLString("Unsupported TileCols value"); CPLXMLNode *LODInfo = CPLGetXMLNode(TCI, "LODInfos.LODInfo"); double res = 0; @@ -332,6 +362,58 @@ CPLErr ECDataset::Initialize(CPLXMLNode *CacheInfo) return error; } +static std::unique_ptr +CreateSRS(const CPLJSONObject &oSRSRoot) +{ + auto poSRS = std::make_unique(); + + bool bSuccess = false; + const int nCode = oSRSRoot.GetInteger("wkid"); + // The concept of LatestWKID is explained in + // https://support.esri.com/en/technical-article/000013950 + const int nLatestCode = oSRSRoot.GetInteger("latestWkid"); + + // Try first with nLatestWKID as there is a higher chance it is a + // EPSG code and not an ESRI one. + if (nLatestCode > 0) + { + if (nLatestCode > 32767) + { + if (poSRS->SetFromUserInput(CPLSPrintf("ESRI:%d", nLatestCode)) == + OGRERR_NONE) + { + bSuccess = true; + } + } + else if (poSRS->importFromEPSG(nLatestCode) == OGRERR_NONE) + { + bSuccess = true; + } + } + if (!bSuccess && nCode > 0) + { + if (nCode > 32767) + { + if (poSRS->SetFromUserInput(CPLSPrintf("ESRI:%d", nCode)) == + OGRERR_NONE) + { + bSuccess = true; + } + } + else if (poSRS->importFromEPSG(nCode) == OGRERR_NONE) + { + bSuccess = true; + } + } + if (!bSuccess) + { + return nullptr; + } + + poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); + return poSRS; +} + CPLErr ECDataset::InitializeFromJSON(const CPLJSONObject &oRoot) { CPLErr error = CE_None; @@ -347,20 +429,23 @@ CPLErr ECDataset::InitializeFromJSON(const CPLJSONObject &oRoot) TSZ = oRoot.GetInteger("tileInfo/rows"); if (TSZ != oRoot.GetInteger("tileInfo/cols")) throw CPLString("Non-square tiles are not supported"); + if (TSZ < 0 || TSZ > 8192) + throw CPLString("Unsupported tileInfo/rows value"); - auto oLODs = oRoot.GetArray("tileInfo/lods"); + const auto oLODs = oRoot.GetArray("tileInfo/lods"); double res = 0; // we need to skip levels that don't have bundle files - int minLOD = oRoot.GetInteger("minLOD"); - int maxLOD = oRoot.GetInteger("maxLOD"); - int level = 0; + m_nMinLOD = oRoot.GetInteger("minLOD"); + if (m_nMinLOD < 0 || m_nMinLOD >= 31) + throw CPLString("Invalid minLOD"); + const int maxLOD = std::min(oRoot.GetInteger("maxLOD"), 31); for (const auto &oLOD : oLODs) { res = oLOD.GetDouble("resolution"); if (!(res > 0)) throw CPLString("Can't parse resolution for LOD"); - level = oLOD.GetInteger("level"); - if (level >= minLOD && level <= maxLOD) + const int level = oLOD.GetInteger("level"); + if (level >= m_nMinLOD && level <= maxLOD) { resolutions.push_back(res); } @@ -369,50 +454,14 @@ CPLErr ECDataset::InitializeFromJSON(const CPLJSONObject &oRoot) if (resolutions.empty()) throw CPLString("Can't parse lods"); - bool bSuccess = false; - const int nCode = oRoot.GetInteger("spatialReference/wkid"); - // The concept of LatestWKID is explained in - // https://support.esri.com/en/technical-article/000013950 - const int nLatestCode = oRoot.GetInteger("spatialReference/latestWkid"); - - // Try first with nLatestWKID as there is a higher chance it is a - // EPSG code and not an ESRI one. - if (nLatestCode > 0) - { - if (nLatestCode > 32767) - { - if (oSRS.SetFromUserInput(CPLSPrintf("ESRI:%d", nLatestCode)) == - OGRERR_NONE) - { - bSuccess = true; - } - } - else if (oSRS.importFromEPSG(nLatestCode) == OGRERR_NONE) - { - bSuccess = true; - } - } - if (!bSuccess && nCode > 0) { - if (nCode > 32767) - { - if (oSRS.SetFromUserInput(CPLSPrintf("ESRI:%d", nCode)) == - OGRERR_NONE) - { - bSuccess = true; - } - } - else if (oSRS.importFromEPSG(nCode) == OGRERR_NONE) + auto poSRS = CreateSRS(oRoot.GetObj("spatialReference")); + if (!poSRS) { - bSuccess = true; + throw CPLString("Invalid Spatial Reference"); } + oSRS = std::move(*poSRS); } - if (!bSuccess) - { - throw CPLString("Invalid Spatial Reference"); - } - - oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); // resolution is the smallest figure res = resolutions[0]; @@ -440,6 +489,59 @@ CPLErr ECDataset::InitializeFromJSON(const CPLJSONObject &oRoot) compression = oRoot.GetString("tileImageInfo/format"); SetMetadataItem("COMPRESS", compression.c_str(), "IMAGE_STRUCTURE"); + auto oInitialExtent = oRoot.GetObj("initialExtent"); + if (oInitialExtent.IsValid() && + oInitialExtent.GetType() == CPLJSONObject::Type::Object) + { + m_sInitialExtent.MinX = oInitialExtent.GetDouble("xmin"); + m_sInitialExtent.MinY = oInitialExtent.GetDouble("ymin"); + m_sInitialExtent.MaxX = oInitialExtent.GetDouble("xmax"); + m_sInitialExtent.MaxY = oInitialExtent.GetDouble("ymax"); + auto oSRSRoot = oInitialExtent.GetObj("spatialReference"); + if (oSRSRoot.IsValid()) + { + auto poSRS = CreateSRS(oSRSRoot); + if (!poSRS) + { + throw CPLString( + "Invalid Spatial Reference in initialExtent"); + } + if (!poSRS->IsSame(&oSRS)) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Ignoring initialExtent, because its SRS is " + "different from the main one"); + m_sInitialExtent = OGREnvelope(); + } + } + } + + auto oFullExtent = oRoot.GetObj("fullExtent"); + if (oFullExtent.IsValid() && + oFullExtent.GetType() == CPLJSONObject::Type::Object) + { + m_sFullExtent.MinX = oFullExtent.GetDouble("xmin"); + m_sFullExtent.MinY = oFullExtent.GetDouble("ymin"); + m_sFullExtent.MaxX = oFullExtent.GetDouble("xmax"); + m_sFullExtent.MaxY = oFullExtent.GetDouble("ymax"); + auto oSRSRoot = oFullExtent.GetObj("spatialReference"); + if (oSRSRoot.IsValid()) + { + auto poSRS = CreateSRS(oSRSRoot); + if (!poSRS) + { + throw CPLString("Invalid Spatial Reference in fullExtent"); + } + if (!poSRS->IsSame(&oSRS)) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Ignoring fullExtent, because its SRS is " + "different from the main one"); + m_sFullExtent = OGREnvelope(); + } + } + } + nBands = EQUAL(compression, "JPEG") ? 3 : 4; for (int i = 1; i <= nBands; i++) { @@ -462,7 +564,71 @@ CPLErr ECDataset::InitializeFromJSON(const CPLJSONObject &oRoot) return error; } +class ESRICProxyRasterBand final : public GDALProxyRasterBand +{ + private: + GDALRasterBand *m_poUnderlyingBand = nullptr; + + protected: + GDALRasterBand *RefUnderlyingRasterBand(bool /*bForceOpen*/) const override + { + return m_poUnderlyingBand; + } + + public: + explicit ESRICProxyRasterBand(GDALRasterBand *poUnderlyingBand) + : m_poUnderlyingBand(poUnderlyingBand) + { + nBand = poUnderlyingBand->GetBand(); + eDataType = poUnderlyingBand->GetRasterDataType(); + nRasterXSize = poUnderlyingBand->GetXSize(); + nRasterYSize = poUnderlyingBand->GetYSize(); + poUnderlyingBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + } +}; + +class ESRICProxyDataset final : public GDALProxyDataset +{ + private: + std::unique_ptr m_poUnderlyingDS{}; + CPLStringList m_aosFileList{}; + + protected: + GDALDataset *RefUnderlyingDataset() const override + { + return m_poUnderlyingDS.get(); + } + + public: + ESRICProxyDataset(GDALDataset *poUnderlyingDS, const char *pszDescription) + : m_poUnderlyingDS(poUnderlyingDS) + { + nRasterXSize = poUnderlyingDS->GetRasterXSize(); + nRasterYSize = poUnderlyingDS->GetRasterYSize(); + for (int i = 0; i < poUnderlyingDS->GetRasterCount(); ++i) + SetBand(i + 1, new ESRICProxyRasterBand( + poUnderlyingDS->GetRasterBand(i + 1))); + m_aosFileList.AddString(pszDescription); + } + + GDALDriver *GetDriver() override + { + return GDALDriver::FromHandle(GDALGetDriverByName("ESRIC")); + } + + char **GetFileList() override + { + return CSLDuplicate(m_aosFileList.List()); + } +}; + GDALDataset *ECDataset::Open(GDALOpenInfo *poOpenInfo) +{ + return Open(poOpenInfo, poOpenInfo->pszFilename); +} + +GDALDataset *ECDataset::Open(GDALOpenInfo *poOpenInfo, + const char *pszDescription) { if (IdentifyXML(poOpenInfo)) { @@ -503,10 +669,8 @@ GDALDataset *ECDataset::Open(GDALOpenInfo *poOpenInfo) poOpenInfo->pszFilename + "}/root.json") .c_str(), GA_ReadOnly); - auto poDS = Open(&oOpenInfo); - if (poDS) - poDS->SetDescription(poOpenInfo->pszFilename); - return poDS; + oOpenInfo.papszOpenOptions = poOpenInfo->papszOpenOptions; + return Open(&oOpenInfo, pszDescription); } CPLJSONDocument oJSONDocument; @@ -524,7 +688,7 @@ GDALDataset *ECDataset::Open(GDALOpenInfo *poOpenInfo) return nullptr; } - auto ds = new ECDataset(); + auto ds = std::make_unique(); auto tileBundlesPath = oRoot.GetString("tileBundlesPath"); // Strip leading relative path indicator (if present) if (tileBundlesPath.substr(0, 2) == "./") @@ -537,10 +701,76 @@ GDALDataset *ECDataset::Open(GDALOpenInfo *poOpenInfo) CPLErr error = ds->InitializeFromJSON(oRoot); if (CE_None != error) { - delete ds; - ds = nullptr; + return nullptr; } - return ds; + + const bool bIsFullExtentValid = + (ds->m_sFullExtent.IsInit() && + ds->m_sFullExtent.MinX < ds->m_sFullExtent.MaxX && + ds->m_sFullExtent.MinY < ds->m_sFullExtent.MaxY); + const char *pszExtentSource = + CSLFetchNameValue(poOpenInfo->papszOpenOptions, "EXTENT_SOURCE"); + + CPLStringList aosOptions; + if ((!pszExtentSource && bIsFullExtentValid) || + (pszExtentSource && EQUAL(pszExtentSource, "FULL_EXTENT"))) + { + if (!bIsFullExtentValid) + { + CPLError(CE_Failure, CPLE_AppDefined, + "fullExtent is not valid"); + return nullptr; + } + aosOptions.AddString("-projwin"); + aosOptions.AddString(CPLSPrintf("%.18g", ds->m_sFullExtent.MinX)); + aosOptions.AddString(CPLSPrintf("%.18g", ds->m_sFullExtent.MaxY)); + aosOptions.AddString(CPLSPrintf("%.18g", ds->m_sFullExtent.MaxX)); + aosOptions.AddString(CPLSPrintf("%.18g", ds->m_sFullExtent.MinY)); + } + else if (pszExtentSource && EQUAL(pszExtentSource, "INITIAL_EXTENT")) + { + const bool bIsInitialExtentValid = + (ds->m_sInitialExtent.IsInit() && + ds->m_sInitialExtent.MinX < ds->m_sInitialExtent.MaxX && + ds->m_sInitialExtent.MinY < ds->m_sInitialExtent.MaxY); + if (!bIsInitialExtentValid) + { + CPLError(CE_Failure, CPLE_AppDefined, + "initialExtent is not valid"); + return nullptr; + } + aosOptions.AddString("-projwin"); + aosOptions.AddString( + CPLSPrintf("%.18g", ds->m_sInitialExtent.MinX)); + aosOptions.AddString( + CPLSPrintf("%.18g", ds->m_sInitialExtent.MaxY)); + aosOptions.AddString( + CPLSPrintf("%.18g", ds->m_sInitialExtent.MaxX)); + aosOptions.AddString( + CPLSPrintf("%.18g", ds->m_sInitialExtent.MinY)); + } + + if (!aosOptions.empty()) + { + aosOptions.AddString("-of"); + aosOptions.AddString("VRT"); + aosOptions.AddString("-co"); + aosOptions.AddString(CPLSPrintf("BLOCKXSIZE=%d", ds->TSZ)); + aosOptions.AddString("-co"); + aosOptions.AddString(CPLSPrintf("BLOCKYSIZE=%d", ds->TSZ)); + auto psOptions = + GDALTranslateOptionsNew(aosOptions.List(), nullptr); + auto hDS = GDALTranslate("", GDALDataset::ToHandle(ds.release()), + psOptions, nullptr); + GDALTranslateOptionsFree(psOptions); + if (!hDS) + { + return nullptr; + } + return new ESRICProxyDataset(GDALDataset::FromHandle(hDS), + pszDescription); + } + return ds.release(); } return nullptr; } @@ -592,7 +822,7 @@ ECBand::ECBand(ECDataset *parent, int b, int level) double factor = parent->resolutions[0] / parent->resolutions[lvl]; nRasterXSize = static_cast(parent->nRasterXSize * factor + 0.5); nRasterYSize = static_cast(parent->nRasterYSize * factor + 0.5); - nBlockXSize = nBlockYSize = 256; + nBlockXSize = nBlockYSize = parent->TSZ; // Default color interpretation assert(b - 1 >= 0); @@ -632,7 +862,8 @@ CPLErr ECBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) buffer.resize(nBytes * parent->nBands); - int lxx = static_cast(parent->resolutions.size() - lvl - 1); + const int lxx = parent->m_nMinLOD + + static_cast(parent->resolutions.size() - lvl - 1); int bx, by; bx = (nBlockXOff / BSZ) * BSZ; by = (nBlockYOff / BSZ) * BSZ; @@ -823,6 +1054,17 @@ void CPL_DLL GDALRegister_ESRIC() poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "json tpkx"); + poDriver->SetMetadataItem( + GDAL_DMD_OPENOPTIONLIST, + "" + " " + ""); poDriver->pfnIdentify = ESRIC::Identify; poDriver->pfnOpen = ESRIC::ECDataset::Open; poDriver->pfnDelete = ESRIC::Delete; diff --git a/frmts/exr/CMakeLists.txt b/frmts/exr/CMakeLists.txt index 2c8b919d8d12..09646867b360 100644 --- a/frmts/exr/CMakeLists.txt +++ b/frmts/exr/CMakeLists.txt @@ -1,7 +1,8 @@ add_gdal_driver(TARGET gdal_EXR SOURCES exrdataset.cpp CORE_SOURCES exrdrivercore.cpp - PLUGIN_CAPABLE) + PLUGIN_CAPABLE + NO_SHARED_SYMBOL_WITH_CORE) if(NOT TARGET gdal_EXR) return() diff --git a/frmts/exr/exrdrivercore.h b/frmts/exr/exrdrivercore.h index bfbd5846a6f2..3a44264595a7 100644 --- a/frmts/exr/exrdrivercore.h +++ b/frmts/exr/exrdrivercore.h @@ -32,8 +32,12 @@ constexpr const char *DRIVER_NAME = "EXR"; -int CPL_DLL EXRDriverIdentify(GDALOpenInfo *poOpenInfo); +#define EXRDriverIdentify PLUGIN_SYMBOL_NAME(EXRDriverIdentify) +#define EXRDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(EXRDriverSetCommonMetadata) -void CPL_DLL EXRDriverSetCommonMetadata(GDALDriver *poDriver); +int EXRDriverIdentify(GDALOpenInfo *poOpenInfo); + +void EXRDriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/fits/CMakeLists.txt b/frmts/fits/CMakeLists.txt index 356b7b7357bb..8e723c0a1cde 100644 --- a/frmts/fits/CMakeLists.txt +++ b/frmts/fits/CMakeLists.txt @@ -1,7 +1,8 @@ add_gdal_driver(TARGET gdal_FITS SOURCES fitsdataset.cpp CORE_SOURCES fitsdrivercore.cpp - PLUGIN_CAPABLE) + PLUGIN_CAPABLE + NO_SHARED_SYMBOL_WITH_CORE) if(NOT TARGET gdal_FITS) return() diff --git a/frmts/fits/fitsdrivercore.h b/frmts/fits/fitsdrivercore.h index 1f3cee6e7ef1..651f82149631 100644 --- a/frmts/fits/fitsdrivercore.h +++ b/frmts/fits/fitsdrivercore.h @@ -35,8 +35,12 @@ constexpr const char *DRIVER_NAME = "FITS"; -int CPL_DLL FITSDriverIdentify(GDALOpenInfo *poOpenInfo); +#define FITSDriverIdentify PLUGIN_SYMBOL_NAME(FITSDriverIdentify) +#define FITSDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(FITSDriverSetCommonMetadata) -void CPL_DLL FITSDriverSetCommonMetadata(GDALDriver *poDriver); +int FITSDriverIdentify(GDALOpenInfo *poOpenInfo); + +void FITSDriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/georaster/CMakeLists.txt b/frmts/georaster/CMakeLists.txt index 0161ed1d508e..8f5eaa4a1d6e 100644 --- a/frmts/georaster/CMakeLists.txt +++ b/frmts/georaster/CMakeLists.txt @@ -3,7 +3,8 @@ add_gdal_driver( SOURCES georaster_dataset.cpp georaster_priv.h georaster_rasterband.cpp georaster_wrapper.cpp oci_wrapper.cpp cpl_vsil_ocilob.cpp CORE_SOURCES georasterdrivercore.cpp - DEF FRMT_georaster PLUGIN_CAPABLE_IF "NOT GDAL_USE_ZLIB_INTERNAL\\\;NOT GDAL_USE_JPEG_INTERNAL") + DEF FRMT_georaster PLUGIN_CAPABLE_IF "NOT GDAL_USE_ZLIB_INTERNAL\\\;NOT GDAL_USE_JPEG_INTERNAL" + NO_SHARED_SYMBOL_WITH_CORE) if(NOT TARGET gdal_GEOR) return() diff --git a/frmts/georaster/georasterdrivercore.h b/frmts/georaster/georasterdrivercore.h index f932f572c72a..ced8486dc3ac 100644 --- a/frmts/georaster/georasterdrivercore.h +++ b/frmts/georaster/georasterdrivercore.h @@ -35,8 +35,12 @@ constexpr const char *DRIVER_NAME = "GeoRaster"; -int CPL_DLL GEORDriverIdentify(GDALOpenInfo *poOpenInfo); +#define GEORDriverIdentify PLUGIN_SYMBOL_NAME(GEORDriverIdentify) +#define GEORDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(GEORDriverSetCommonMetadata) -void CPL_DLL GEORDriverSetCommonMetadata(GDALDriver *poDriver); +int GEORDriverIdentify(GDALOpenInfo *poOpenInfo); + +void GEORDriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/gif/CMakeLists.txt b/frmts/gif/CMakeLists.txt index 0900fb087330..486e0f992320 100644 --- a/frmts/gif/CMakeLists.txt +++ b/frmts/gif/CMakeLists.txt @@ -2,7 +2,8 @@ add_gdal_driver( TARGET gdal_GIF SOURCES gifabstractdataset.h gifabstractdataset.cpp biggifdataset.cpp gifdataset.cpp CORE_SOURCES gifdrivercore.cpp - PLUGIN_CAPABLE) + PLUGIN_CAPABLE + NO_SHARED_SYMBOL_WITH_CORE) if(NOT TARGET gdal_GIF) return() diff --git a/frmts/gif/gifdrivercore.h b/frmts/gif/gifdrivercore.h index 08b203bd3d3f..419ff379c314 100644 --- a/frmts/gif/gifdrivercore.h +++ b/frmts/gif/gifdrivercore.h @@ -36,10 +36,16 @@ constexpr const char *GIF_DRIVER_NAME = "GIF"; constexpr const char *BIGGIF_DRIVER_NAME = "BIGGIF"; -int CPL_DLL GIFDriverIdentify(GDALOpenInfo *poOpenInfo); +#define GIFDriverIdentify PLUGIN_SYMBOL_NAME(GIFDriverIdentify) +#define BIGGIFDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(BIGGIFDriverSetCommonMetadata) +#define GIFDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(GIFDriverSetCommonMetadata) -void CPL_DLL BIGGIFDriverSetCommonMetadata(GDALDriver *poDriver); +int GIFDriverIdentify(GDALOpenInfo *poOpenInfo); -void CPL_DLL GIFDriverSetCommonMetadata(GDALDriver *poDriver); +void BIGGIFDriverSetCommonMetadata(GDALDriver *poDriver); + +void GIFDriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/grib/CMakeLists.txt b/frmts/grib/CMakeLists.txt index 7bfb56ac8814..16023f73157a 100644 --- a/frmts/grib/CMakeLists.txt +++ b/frmts/grib/CMakeLists.txt @@ -50,7 +50,8 @@ add_gdal_driver( gribdrivercore.cpp PLUGIN_CAPABLE_IF "NOT GDAL_USE_PNG_INTERNAL\\\;NOT GDAL_USE_ZLIB_INTERNAL" - NO_DEPS) + NO_DEPS + NO_SHARED_SYMBOL_WITH_CORE) if(NOT TARGET gdal_GRIB) return() diff --git a/frmts/grib/gribdataset.cpp b/frmts/grib/gribdataset.cpp index 5b1a7db85a5b..7a9c4499beb6 100644 --- a/frmts/grib/gribdataset.cpp +++ b/frmts/grib/gribdataset.cpp @@ -922,11 +922,16 @@ char **GRIBRasterBand::GetMetadata(const char *pszDomain) const char *GRIBRasterBand::GetMetadataItem(const char *pszName, const char *pszDomain) { - FindMetaData(); - if (m_nGribVersion == 2 && - CPLTestBool(CPLGetConfigOption("GRIB_PDS_ALL_BANDS", "ON"))) + if (!((!pszDomain || pszDomain[0] == 0) && + (EQUAL(pszName, "STATISTICS_MINIMUM") || + EQUAL(pszName, "STATISTICS_MAXIMUM")))) { - FindPDSTemplateGRIB2(); + FindMetaData(); + if (m_nGribVersion == 2 && + CPLTestBool(CPLGetConfigOption("GRIB_PDS_ALL_BANDS", "ON"))) + { + FindPDSTemplateGRIB2(); + } } return GDALPamRasterBand::GetMetadataItem(pszName, pszDomain); } @@ -1156,7 +1161,8 @@ class InventoryWrapperGrib : public gdal::grib::InventoryWrapper class InventoryWrapperSidecar : public gdal::grib::InventoryWrapper { public: - explicit InventoryWrapperSidecar(VSILFILE *fp) + explicit InventoryWrapperSidecar(VSILFILE *fp, uint64_t nStartOffset, + int64_t nSize) : gdal::grib::InventoryWrapper() { result_ = -1; @@ -1164,26 +1170,25 @@ class InventoryWrapperSidecar : public gdal::grib::InventoryWrapper size_t length = static_cast(VSIFTellL(fp)); if (length > 4 * 1024 * 1024) return; - std::string psSidecar; - psSidecar.resize(length); + std::string osSidecar; + osSidecar.resize(length); VSIFSeekL(fp, 0, SEEK_SET); - if (VSIFReadL(&psSidecar[0], length, 1, fp) != 1) + if (VSIFReadL(&osSidecar[0], length, 1, fp) != 1) return; - CPLStringList aosMsgs( - CSLTokenizeString2(psSidecar.c_str(), "\n", + const CPLStringList aosMsgs( + CSLTokenizeString2(osSidecar.c_str(), "\n", CSLT_PRESERVEQUOTES | CSLT_STRIPLEADSPACES)); - inv_len_ = aosMsgs.size(); inv_ = static_cast( - CPLMalloc(inv_len_ * sizeof(inventoryType))); + CPLCalloc(aosMsgs.size(), sizeof(inventoryType))); - for (size_t i = 0; i < inv_len_; ++i) + for (const char *pszMsg : aosMsgs) { // We are parsing // "msgNum[.subgNum]:start:dontcare:name1:name2:name3" For NOMADS: // "msgNum[.subgNum]:start:reftime:var:level:time" - CPLStringList aosTokens(CSLTokenizeString2( - aosMsgs[i], ":", CSLT_PRESERVEQUOTES | CSLT_ALLOWEMPTYTOKENS)); + const CPLStringList aosTokens(CSLTokenizeString2( + pszMsg, ":", CSLT_PRESERVEQUOTES | CSLT_ALLOWEMPTYTOKENS)); CPLStringList aosNum; if (aosTokens.size() < 6) @@ -1200,7 +1205,7 @@ class InventoryWrapperSidecar : public gdal::grib::InventoryWrapper goto err_sidecar; if (aosNum.size() < 2) - inv_[i].subgNum = 0; + inv_[inv_len_].subgNum = 0; else { auto subgNum = strtol(aosNum[1], &endptr, 10); @@ -1211,21 +1216,29 @@ class InventoryWrapperSidecar : public gdal::grib::InventoryWrapper // .idx file use a 1-based indexing, whereas DEGRIB uses a // 0-based one subgNum--; - inv_[i].subgNum = static_cast(subgNum); + inv_[inv_len_].subgNum = static_cast(subgNum); } - inv_[i].start = strtoll(aosTokens[1], &endptr, 10); + inv_[inv_len_].start = strtoll(aosTokens[1], &endptr, 10); if (*endptr != 0) goto err_sidecar; - inv_[i].unitName = nullptr; - inv_[i].comment = nullptr; - inv_[i].element = nullptr; - inv_[i].shortFstLevel = nullptr; + if (inv_[inv_len_].start < nStartOffset) + continue; + if (nSize > 0 && inv_[inv_len_].start >= nStartOffset + nSize) + break; + + inv_[inv_len_].start -= nStartOffset; + + inv_[inv_len_].unitName = nullptr; + inv_[inv_len_].comment = nullptr; + inv_[inv_len_].element = nullptr; + inv_[inv_len_].shortFstLevel = nullptr; // This is going into the description field -> // the only one available before loading the metadata - inv_[i].longFstLevel = VSIStrdup(CPLSPrintf( + inv_[inv_len_].longFstLevel = VSIStrdup(CPLSPrintf( "%s:%s:%s", aosTokens[3], aosTokens[4], aosTokens[5])); + ++inv_len_; continue; @@ -1233,8 +1246,7 @@ class InventoryWrapperSidecar : public gdal::grib::InventoryWrapper CPLDebug("GRIB", "Failed parsing sidecar entry '%s', " "falling back to constructing an inventory", - aosMsgs[i]); - inv_len_ = static_cast(i); + pszMsg); return; } @@ -1309,22 +1321,43 @@ GRIBDataset::Inventory(VSILFILE *fp, GDALOpenInfo *poOpenInfo) std::unique_ptr pInventories; VSIFSeekL(fp, 0, SEEK_SET); - CPLString sSideCarFilename = CPLString(poOpenInfo->pszFilename) + ".idx"; + std::string osSideCarFilename(poOpenInfo->pszFilename); + uint64_t nStartOffset = 0; + int64_t nSize = -1; + if (STARTS_WITH(poOpenInfo->pszFilename, "/vsisubfile/")) + { + const char *pszPtr = poOpenInfo->pszFilename + strlen("/vsisubfile/"); + const char *pszComma = strchr(pszPtr, ','); + if (pszComma) + { + const CPLStringList aosTokens(CSLTokenizeString2( + std::string(pszPtr, pszComma - pszPtr).c_str(), "_", 0)); + if (aosTokens.size() == 2) + { + nStartOffset = std::strtoull(aosTokens[0], nullptr, 10); + nSize = std::strtoll(aosTokens[1], nullptr, 10); + osSideCarFilename = pszComma + 1; + } + } + } + osSideCarFilename += ".idx"; VSILFILE *fpSideCar = nullptr; if (CPLTestBool(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "USE_IDX", "YES")) && - ((fpSideCar = VSIFOpenL(sSideCarFilename, "rb")) != nullptr)) + ((fpSideCar = VSIFOpenL(osSideCarFilename.c_str(), "rb")) != nullptr)) { CPLDebug("GRIB", "Reading inventories from sidecar file %s", - sSideCarFilename.c_str()); + osSideCarFilename.c_str()); // Contains an GRIB2 message inventory of the file. - pInventories = std::make_unique(fpSideCar); + pInventories = std::make_unique( + fpSideCar, nStartOffset, nSize); if (pInventories->result() <= 0 || pInventories->length() == 0) pInventories = nullptr; VSIFCloseL(fpSideCar); } else - CPLDebug("GRIB", "Failed opening sidecar %s", sSideCarFilename.c_str()); + CPLDebug("GRIB", "Failed opening sidecar %s", + osSideCarFilename.c_str()); if (pInventories == nullptr) { @@ -2021,7 +2054,14 @@ void GRIBArray::Finalize(GRIBGroup *poGroup, inventoryType *psInv) attr->Write("validity_time"); } +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnull-dereference" +#endif m_dims.insert(m_dims.begin(), poDimTime); +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif if (m_poSRS) { auto mapping = m_poSRS->GetDataAxisToSRSAxisMapping(); diff --git a/frmts/grib/gribdrivercore.h b/frmts/grib/gribdrivercore.h index 98e0a820c882..d745bd6edc28 100644 --- a/frmts/grib/gribdrivercore.h +++ b/frmts/grib/gribdrivercore.h @@ -34,8 +34,12 @@ constexpr const char *DRIVER_NAME = "GRIB"; -int CPL_DLL GRIBDriverIdentify(GDALOpenInfo *poOpenInfo); +#define GRIBDriverIdentify PLUGIN_SYMBOL_NAME(GRIBDriverIdentify) +#define GRIBDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(GRIBDriverSetCommonMetadata) -void CPL_DLL GRIBDriverSetCommonMetadata(GDALDriver *poDriver); +int GRIBDriverIdentify(GDALOpenInfo *poOpenInfo); + +void GRIBDriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/gta/CMakeLists.txt b/frmts/gta/CMakeLists.txt index 7d3649a8ff08..68428bb757c5 100644 --- a/frmts/gta/CMakeLists.txt +++ b/frmts/gta/CMakeLists.txt @@ -1,7 +1,8 @@ add_gdal_driver(TARGET gdal_GTA SOURCES gta_headers.h gtadataset.cpp CORE_SOURCES gtadrivercore.cpp - PLUGIN_CAPABLE) + PLUGIN_CAPABLE + NO_SHARED_SYMBOL_WITH_CORE) if(NOT TARGET gdal_GTA) return() diff --git a/frmts/gta/gtadrivercore.h b/frmts/gta/gtadrivercore.h index 1d8336aa8edd..536fdec9ff83 100644 --- a/frmts/gta/gtadrivercore.h +++ b/frmts/gta/gtadrivercore.h @@ -34,8 +34,13 @@ constexpr const char *DRIVER_NAME = "GTA"; -int CPL_DLL GTADriverIdentify(GDALOpenInfo *poOpenInfo); +#define GTADriverIdentify PLUGIN_SYMBOL_NAME(GTADriverIdentify) -void CPL_DLL GTADriverSetCommonMetadata(GDALDriver *poDriver); +#define GTADriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(GTADriverSetCommonMetadata) + +int GTADriverIdentify(GDALOpenInfo *poOpenInfo); + +void GTADriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/gtiff/cogdriver.cpp b/frmts/gtiff/cogdriver.cpp index 063742b421d5..f5c5045ac212 100644 --- a/frmts/gtiff/cogdriver.cpp +++ b/frmts/gtiff/cogdriver.cpp @@ -737,19 +737,25 @@ GDALCOGCreator::~GDALCOGCreator() // may reference the later m_poRGBMaskDS.reset(); - if (m_poReprojectedDS) + // Config option just for testing purposes + const bool bDeleteTempFiles = + CPLTestBool(CPLGetConfigOption("COG_DELETE_TEMP_FILES", "YES")); + if (bDeleteTempFiles) { - CPLString osProjectedDSName(m_poReprojectedDS->GetDescription()); - m_poReprojectedDS.reset(); - VSIUnlink(osProjectedDSName); - } - if (!m_osTmpOverviewFilename.empty()) - { - VSIUnlink(m_osTmpOverviewFilename); - } - if (!m_osTmpMskOverviewFilename.empty()) - { - VSIUnlink(m_osTmpMskOverviewFilename); + if (m_poReprojectedDS) + { + CPLString osProjectedDSName(m_poReprojectedDS->GetDescription()); + m_poReprojectedDS.reset(); + VSIUnlink(osProjectedDSName); + } + if (!m_osTmpOverviewFilename.empty()) + { + VSIUnlink(m_osTmpOverviewFilename); + } + if (!m_osTmpMskOverviewFilename.empty()) + { + VSIUnlink(m_osTmpMskOverviewFilename); + } } } diff --git a/frmts/gtiff/gt_overview.cpp b/frmts/gtiff/gt_overview.cpp index 38ca89e434c2..c125ab091d63 100644 --- a/frmts/gtiff/gt_overview.cpp +++ b/frmts/gtiff/gt_overview.cpp @@ -203,7 +203,8 @@ toff_t GTIFFWriteDirectory(TIFF *hTIFF, int nSubfileType, int nXSize, /************************************************************************/ void GTIFFBuildOverviewMetadata(const char *pszResampling, - GDALDataset *poBaseDS, CPLString &osMetadata) + GDALDataset *poBaseDS, bool bIsForMaskBand, + CPLString &osMetadata) { osMetadata = ""; @@ -216,7 +217,11 @@ void GTIFFBuildOverviewMetadata(const char *pszResampling, osMetadata += ""; } - if (poBaseDS->GetMetadataItem("INTERNAL_MASK_FLAGS_1")) + if (bIsForMaskBand) + { + osMetadata += "2"; + } + else if (poBaseDS->GetMetadataItem("INTERNAL_MASK_FLAGS_1")) { for (int iBand = 0; iBand < 200; iBand++) { @@ -771,7 +776,10 @@ CPLErr GTIFFBuildOverviewsEx(const char *pszFilename, int nBands, GDALDataset *poBaseDS = papoBandList[0]->GetDataset(); if (poBaseDS) { - GTIFFBuildOverviewMetadata(pszResampling, poBaseDS, osMetadata); + const bool bIsForMaskBand = + nBands == 1 && papoBandList[0]->IsMaskBand(); + GTIFFBuildOverviewMetadata(pszResampling, poBaseDS, bIsForMaskBand, + osMetadata); } if (poBaseDS != nullptr && poBaseDS->GetRasterCount() == nBands) diff --git a/frmts/gtiff/gt_overview.h b/frmts/gtiff/gt_overview.h index 9014662e67c7..5b56f4929ef2 100644 --- a/frmts/gtiff/gt_overview.h +++ b/frmts/gtiff/gt_overview.h @@ -53,7 +53,8 @@ toff_t GTIFFWriteDirectory(TIFF *hTIFF, int nSubfileType, int nXSize, bool bDeferStrileArrayWriting); void GTIFFBuildOverviewMetadata(const char *pszResampling, - GDALDataset *poBaseDS, CPLString &osMetadata); + GDALDataset *poBaseDS, bool bIsForMaskBand, + CPLString &osMetadata); CPLErr GTIFFBuildOverviewsEx(const char *pszFilename, int nBands, GDALRasterBand *const *papoBandList, diff --git a/frmts/gtiff/gt_wkt_srs.cpp b/frmts/gtiff/gt_wkt_srs.cpp index 75662539444a..919829f2d511 100644 --- a/frmts/gtiff/gt_wkt_srs.cpp +++ b/frmts/gtiff/gt_wkt_srs.cpp @@ -1397,6 +1397,43 @@ OGRSpatialReferenceH GTIFGetOGISDefnAsOSR(GTIF *hGTIF, GTIFDefn *psDefn) for (; i < 10; i++) adfParam[i] = 0.0; +#if LIBGEOTIFF_VERSION <= 1730 + // libgeotiff <= 1.7.3 is unfortunately inconsistent. When it synthetizes the + // projection parameters from the EPSG ProjectedCRS code, it returns + // them normalized in degrees. But when it gets them from + // ProjCoordTransGeoKey and other Proj....GeoKey's it return them in + // a raw way, that is in the units of GeogAngularUnitSizeGeoKey + // The below oSRS.SetXXXXX() methods assume the angular projection + // parameters to be in degrees, so convert them to degrees in that later case. + // From GDAL 3.0 to 3.9.0, we didn't do that conversion... + // And all versions of GDAL <= 3.9.0 when writing those geokeys, wrote + // them as degrees, hence this GTIFF_READ_ANGULAR_PARAMS_IN_DEGREE + // config option that can be set to YES to avoid that conversion and + // assume that the angular parameters have been written as degree. + if (GDALGTIFKeyGetSHORT(hGTIF, ProjCoordTransGeoKey, &tmp, 0, 1) && + !CPLTestBool(CPLGetConfigOption( + "GTIFF_READ_ANGULAR_PARAMS_IN_DEGREE", "NO"))) + { + adfParam[0] *= psDefn->UOMAngleInDegrees; + adfParam[1] *= psDefn->UOMAngleInDegrees; + adfParam[2] *= psDefn->UOMAngleInDegrees; + adfParam[3] *= psDefn->UOMAngleInDegrees; + } +#else + // If GTIFF_READ_ANGULAR_PARAMS_IN_DEGREE=YES (non-nominal case), undo + // the conversion to degrees, that has been done by libgeotiff > 1.7.3 + if (GDALGTIFKeyGetSHORT(hGTIF, ProjCoordTransGeoKey, &tmp, 0, 1) && + psDefn->UOMAngleInDegrees != 0 && psDefn->UOMAngleInDegrees != 1 && + CPLTestBool(CPLGetConfigOption( + "GTIFF_READ_ANGULAR_PARAMS_IN_DEGREE", "NO"))) + { + adfParam[0] /= psDefn->UOMAngleInDegrees; + adfParam[1] /= psDefn->UOMAngleInDegrees; + adfParam[2] /= psDefn->UOMAngleInDegrees; + adfParam[3] /= psDefn->UOMAngleInDegrees; + } +#endif + /* -------------------------------------------------------------------- */ /* Translation the fundamental projection. */ @@ -2150,6 +2187,31 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, } #endif + double dfAngUnitValue = 0; + std::string osAngUnitName; + if (bHasEllipsoid) + { + const char *angUnitNameTmp = ""; + dfAngUnitValue = poSRS->GetAngularUnits(&angUnitNameTmp); + osAngUnitName = angUnitNameTmp; + } + + // Convert angular projection parameters from its normalized value in degree + // to the units of GeogAngularUnitsGeoKey. + // Note: for GDAL <= 3.9.0, we always have written them in degrees ! + // We can set GTIFF_WRITE_ANGULAR_PARAMS_IN_DEGREE=YES to get that + // non-conformant behavior... + const auto ConvertAngularParam = [dfAngUnitValue](double dfValInDeg) + { + constexpr double DEG_TO_RAD = M_PI / 180.0; + return dfAngUnitValue != 0 && + std::fabs(dfAngUnitValue - DEG_TO_RAD) > 1e-10 && + !CPLTestBool(CPLGetConfigOption( + "GTIFF_WRITE_ANGULAR_PARAMS_IN_DEGREE", "NO")) + ? dfValInDeg * DEG_TO_RAD / dfAngUnitValue + : dfValInDeg; + }; + const char *pszProjection = poSRSCompatibleOfWKT1->GetAttrValue("PROJECTION"); if (nPCS != KvUserDefined) @@ -2191,20 +2253,20 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_AlbersEqualArea); GTIFKeySet(psGTIF, ProjStdParallelGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_STANDARD_PARALLEL_1, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_STANDARD_PARALLEL_1, 0.0))); GTIFKeySet(psGTIF, ProjStdParallel2GeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_STANDARD_PARALLEL_2, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_STANDARD_PARALLEL_2, 0.0))); GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_CENTER, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_CENTER, 0.0))); GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LONGITUDE_OF_CENTER, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LONGITUDE_OF_CENTER, 0.0))); GTIFKeySet( psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, @@ -2267,12 +2329,12 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_TransverseMercator); GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_CENTRAL_MERIDIAN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_CENTRAL_MERIDIAN, 0.0))); GTIFKeySet( psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1, @@ -2298,12 +2360,12 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_TransvMercator_SouthOriented); GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_CENTRAL_MERIDIAN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_CENTRAL_MERIDIAN, 0.0))); GTIFKeySet( psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1, @@ -2330,17 +2392,18 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, CT_Mercator); GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_CENTRAL_MERIDIAN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_CENTRAL_MERIDIAN, 0.0))); if (EQUAL(pszProjection, SRS_PT_MERCATOR_2SP)) - GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_STANDARD_PARALLEL_1, 0.0)); + GTIFKeySet( + psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1, + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_STANDARD_PARALLEL_1, 0.0))); else GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1, poSRSCompatibleOfWKT1->GetNormProjParm( @@ -2366,12 +2429,12 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_ObliqueStereographic); GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_CENTRAL_MERIDIAN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_CENTRAL_MERIDIAN, 0.0))); GTIFKeySet( psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1, @@ -2397,12 +2460,12 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_Stereographic); GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_CENTRAL_MERIDIAN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_CENTRAL_MERIDIAN, 0.0))); GTIFKeySet( psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1, @@ -2428,12 +2491,12 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_PolarStereographic); GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); GTIFKeySet(psGTIF, ProjStraightVertPoleLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_CENTRAL_MERIDIAN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_CENTRAL_MERIDIAN, 0.0))); GTIFKeySet( psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1, @@ -2459,19 +2522,19 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_ObliqueMercator); GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_CENTER, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_CENTER, 0.0))); GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LONGITUDE_OF_CENTER, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LONGITUDE_OF_CENTER, 0.0))); GTIFKeySet(psGTIF, ProjAzimuthAngleGeoKey, TYPE_DOUBLE, 1, poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_AZIMUTH, 0.0)); GTIFKeySet(psGTIF, ProjRectifiedGridAngleGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_RECTIFIED_GRID_ANGLE, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_RECTIFIED_GRID_ANGLE, 0.0))); GTIFKeySet( psGTIF, ProjScaleAtCenterGeoKey, TYPE_DOUBLE, 1, @@ -2498,19 +2561,19 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_HotineObliqueMercatorAzimuthCenter); GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_CENTER, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_CENTER, 0.0))); GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LONGITUDE_OF_CENTER, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LONGITUDE_OF_CENTER, 0.0))); GTIFKeySet(psGTIF, ProjAzimuthAngleGeoKey, TYPE_DOUBLE, 1, poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_AZIMUTH, 0.0)); GTIFKeySet(psGTIF, ProjRectifiedGridAngleGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_RECTIFIED_GRID_ANGLE, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_RECTIFIED_GRID_ANGLE, 0.0))); GTIFKeySet( psGTIF, ProjScaleAtCenterGeoKey, TYPE_DOUBLE, 1, @@ -2536,12 +2599,12 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_ObliqueMercator_Laborde); GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_CENTER, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_CENTER, 0.0))); GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LONGITUDE_OF_CENTER, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LONGITUDE_OF_CENTER, 0.0))); GTIFKeySet(psGTIF, ProjAzimuthAngleGeoKey, TYPE_DOUBLE, 1, poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_AZIMUTH, 0.0)); @@ -2570,12 +2633,12 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_CassiniSoldner); GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_CENTRAL_MERIDIAN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_CENTRAL_MERIDIAN, 0.0))); GTIFKeySet( psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, @@ -2597,20 +2660,20 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_EquidistantConic); GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_STANDARD_PARALLEL_1, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_STANDARD_PARALLEL_1, 0.0))); GTIFKeySet(psGTIF, ProjStdParallel2GeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_STANDARD_PARALLEL_2, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_STANDARD_PARALLEL_2, 0.0))); GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_CENTER, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_CENTER, 0.0))); GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LONGITUDE_OF_CENTER, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LONGITUDE_OF_CENTER, 0.0))); GTIFKeySet( psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, @@ -2662,12 +2725,12 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_AzimuthalEquidistant); GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_CENTER, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_CENTER, 0.0))); GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LONGITUDE_OF_CENTER, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LONGITUDE_OF_CENTER, 0.0))); GTIFKeySet( psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, @@ -2689,12 +2752,12 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_MillerCylindrical); GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_CENTER, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_CENTER, 0.0))); GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LONGITUDE_OF_CENTER, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LONGITUDE_OF_CENTER, 0.0))); GTIFKeySet( psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, @@ -2716,16 +2779,16 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_Equirectangular); GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_CENTRAL_MERIDIAN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_CENTRAL_MERIDIAN, 0.0))); GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_STANDARD_PARALLEL_1, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_STANDARD_PARALLEL_1, 0.0))); GTIFKeySet( psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, @@ -2746,12 +2809,12 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, CT_Gnomonic); GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_CENTRAL_MERIDIAN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_CENTRAL_MERIDIAN, 0.0))); GTIFKeySet( psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, @@ -2773,12 +2836,12 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_LambertAzimEqualArea); GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_CENTER, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_CENTER, 0.0))); GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LONGITUDE_OF_CENTER, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LONGITUDE_OF_CENTER, 0.0))); GTIFKeySet( psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, @@ -2800,12 +2863,12 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_Orthographic); GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_CENTRAL_MERIDIAN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_CENTRAL_MERIDIAN, 0.0))); GTIFKeySet( psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, @@ -2827,12 +2890,12 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_NewZealandMapGrid); GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_CENTRAL_MERIDIAN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_CENTRAL_MERIDIAN, 0.0))); GTIFKeySet( psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, @@ -2853,8 +2916,8 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, CT_Robinson); GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LONGITUDE_OF_CENTER, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LONGITUDE_OF_CENTER, 0.0))); GTIFKeySet( psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, @@ -2875,8 +2938,8 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, CT_Sinusoidal); GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LONGITUDE_OF_CENTER, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LONGITUDE_OF_CENTER, 0.0))); GTIFKeySet( psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, @@ -2898,8 +2961,8 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_VanDerGrinten); GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_CENTRAL_MERIDIAN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_CENTRAL_MERIDIAN, 0.0))); GTIFKeySet( psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, @@ -2921,20 +2984,20 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_LambertConfConic_2SP); GTIFKeySet(psGTIF, ProjFalseOriginLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); GTIFKeySet(psGTIF, ProjFalseOriginLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_CENTRAL_MERIDIAN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_CENTRAL_MERIDIAN, 0.0))); GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_STANDARD_PARALLEL_1, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_STANDARD_PARALLEL_1, 0.0))); GTIFKeySet(psGTIF, ProjStdParallel2GeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_STANDARD_PARALLEL_2, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_STANDARD_PARALLEL_2, 0.0))); GTIFKeySet( psGTIF, ProjFalseOriginEastingGeoKey, TYPE_DOUBLE, 1, @@ -2956,12 +3019,12 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_LambertConfConic_1SP); GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_CENTRAL_MERIDIAN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_CENTRAL_MERIDIAN, 0.0))); GTIFKeySet( psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1, @@ -2987,12 +3050,12 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, CT_CylindricalEqualArea); GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_CENTRAL_MERIDIAN, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_CENTRAL_MERIDIAN, 0.0))); GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1, - poSRSCompatibleOfWKT1->GetNormProjParm( - SRS_PP_STANDARD_PARALLEL_1, 0.0)); + ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm( + SRS_PP_STANDARD_PARALLEL_1, 0.0))); GTIFKeySet( psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, @@ -3133,38 +3196,36 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, /* Write angular units. */ /* -------------------------------------------------------------------- */ - const char *angUnitName = ""; if (bHasEllipsoid && (nGCS == KvUserDefined || eVersion == GEOTIFF_VERSION_1_0)) { - double angUnitValue = poSRS->GetAngularUnits(&angUnitName); - if (EQUAL(angUnitName, "Degree")) + if (EQUAL(osAngUnitName.c_str(), "Degree")) GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1, Angular_Degree); - else if (EQUAL(angUnitName, "arc-second")) + else if (EQUAL(osAngUnitName.c_str(), "arc-second")) GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1, Angular_Arc_Second); - else if (EQUAL(angUnitName, "arc-minute")) + else if (EQUAL(osAngUnitName.c_str(), "arc-minute")) GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1, Angular_Arc_Minute); - else if (EQUAL(angUnitName, "grad")) + else if (EQUAL(osAngUnitName.c_str(), "grad")) GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1, Angular_Grad); - else if (EQUAL(angUnitName, "gon")) + else if (EQUAL(osAngUnitName.c_str(), "gon")) GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1, Angular_Gon); - else if (EQUAL(angUnitName, "radian")) + else if (EQUAL(osAngUnitName.c_str(), "radian")) GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1, Angular_Radian); - // else if (EQUAL(angUnitName, "microradian")) + // else if (EQUAL(osAngUnitName.c_str(), "microradian")) // GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1, // 9109); else { // GeogCitationGeoKey may be rewritten if the gcs is user defined. - oMapAsciiKeys[GeogCitationGeoKey] = angUnitName; + oMapAsciiKeys[GeogCitationGeoKey] = osAngUnitName; GTIFKeySet(psGTIF, GeogAngularUnitSizeGeoKey, TYPE_DOUBLE, 1, - angUnitValue); + dfAngUnitValue); } } @@ -3275,8 +3336,8 @@ int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS, if (nGCS == KvUserDefined && CPLTestBool(CPLGetConfigOption("GTIFF_ESRI_CITATION", "YES"))) { - SetGeogCSCitation(psGTIF, oMapAsciiKeys, poSRS, angUnitName, - nDatum, nSpheroid); + SetGeogCSCitation(psGTIF, oMapAsciiKeys, poSRS, + osAngUnitName.c_str(), nDatum, nSpheroid); } } } diff --git a/frmts/gtiff/gtiffdataset_read.cpp b/frmts/gtiff/gtiffdataset_read.cpp index 9d2dcb8e2931..6838293b1340 100644 --- a/frmts/gtiff/gtiffdataset_read.cpp +++ b/frmts/gtiff/gtiffdataset_read.cpp @@ -750,11 +750,33 @@ static void CPL_STDCALL ThreadDecompressionFuncErrorHandler( psContext->nYCrbCrSubSampling1); } } - if (psContext->pExtraSamples) + if (poDS->m_nPlanarConfig == PLANARCONFIG_CONTIG) { - TIFFSetField(hTIFFTmp, TIFFTAG_EXTRASAMPLES, - psContext->nExtraSampleCount, - psContext->pExtraSamples); + if (psContext->pExtraSamples) + { + TIFFSetField(hTIFFTmp, TIFFTAG_EXTRASAMPLES, + psContext->nExtraSampleCount, + psContext->pExtraSamples); + } + else + { + const int nSamplesAccountedFor = + poDS->m_nPhotometric == PHOTOMETRIC_RGB ? 3 + : poDS->m_nPhotometric == PHOTOMETRIC_MINISBLACK ? 1 + : 0; + if (nSamplesAccountedFor > 0 && + poDS->m_nSamplesPerPixel > nSamplesAccountedFor) + { + // If the input image is not compliant regarndig ExtraSamples, + // generate a synthetic one to avoid gazillons of warnings + const auto nExtraSampleCount = static_cast( + poDS->m_nSamplesPerPixel - nSamplesAccountedFor); + std::vector anExtraSamples( + nExtraSampleCount, EXTRASAMPLE_UNSPECIFIED); + TIFFSetField(hTIFFTmp, TIFFTAG_EXTRASAMPLES, + nExtraSampleCount, anExtraSamples.data()); + } + } } TIFFWriteCheck(hTIFFTmp, FALSE, "ThreadDecompressionFunc"); TIFFWriteDirectory(hTIFFTmp); diff --git a/frmts/gtiff/gtiffdataset_write.cpp b/frmts/gtiff/gtiffdataset_write.cpp index 12867f2d77d1..a79d8ee8f2ac 100644 --- a/frmts/gtiff/gtiffdataset_write.cpp +++ b/frmts/gtiff/gtiffdataset_write.cpp @@ -73,6 +73,13 @@ static constexpr const char szPROFILE_BASELINE[] = "BASELINE"; static constexpr const char szPROFILE_GeoTIFF[] = "GeoTIFF"; static constexpr const char szPROFILE_GDALGeoTIFF[] = "GDALGeoTIFF"; +// Due to libgeotiff/xtiff.c declaring TIFFTAG_GEOTIEPOINTS with field_readcount +// and field_writecount == -1 == TIFF_VARIABLE, we are limited to writing +// 65535 values in that tag. That could potentially be overcome by changing the tag +// declaration to using TIFF_VARIABLE2 where the count is a uint32_t. +constexpr int knMAX_GCP_COUNT = + static_cast(std::numeric_limits::max() / 6); + enum { ENDIANNESS_NATIVE, @@ -1714,10 +1721,7 @@ static void DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes, int iBand, if (nBitsPerSample == 8 && nSampleFormat == SAMPLEFORMAT_UINT) { uint8_t nNoDataValue = 0; - if (bHasNoData && - dfNoDataValue >= std::numeric_limits::min() && - dfNoDataValue <= std::numeric_limits::max() && - dfNoDataValue == static_cast(dfNoDataValue)) + if (bHasNoData && GDALIsValueExactAs(dfNoDataValue)) { nNoDataValue = static_cast(dfNoDataValue); } @@ -1799,9 +1803,7 @@ static void DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes, int iBand, else if (nBitsPerSample == 8 && nSampleFormat == SAMPLEFORMAT_INT) { int8_t nNoDataValue = 0; - if (bHasNoData && dfNoDataValue >= std::numeric_limits::min() && - dfNoDataValue <= std::numeric_limits::max() && - dfNoDataValue == static_cast(dfNoDataValue)) + if (bHasNoData && GDALIsValueExactAs(dfNoDataValue)) { nNoDataValue = static_cast(dfNoDataValue); } @@ -1816,10 +1818,7 @@ static void DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes, int iBand, else if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_INT) { int16_t nNoDataValue = 0; - if (bHasNoData && - dfNoDataValue >= std::numeric_limits::min() && - dfNoDataValue <= std::numeric_limits::max() && - dfNoDataValue == static_cast(dfNoDataValue)) + if (bHasNoData && GDALIsValueExactAs(dfNoDataValue)) { nNoDataValue = static_cast(dfNoDataValue); } @@ -1834,10 +1833,7 @@ static void DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes, int iBand, else if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_UINT) { uint16_t nNoDataValue = 0; - if (bHasNoData && - dfNoDataValue >= std::numeric_limits::min() && - dfNoDataValue <= std::numeric_limits::max() && - dfNoDataValue == static_cast(dfNoDataValue)) + if (bHasNoData && GDALIsValueExactAs(dfNoDataValue)) { nNoDataValue = static_cast(dfNoDataValue); } @@ -1852,10 +1848,7 @@ static void DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes, int iBand, else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_INT) { int32_t nNoDataValue = 0; - if (bHasNoData && - dfNoDataValue >= std::numeric_limits::min() && - dfNoDataValue <= std::numeric_limits::max() && - dfNoDataValue == static_cast(dfNoDataValue)) + if (bHasNoData && GDALIsValueExactAs(dfNoDataValue)) { nNoDataValue = static_cast(dfNoDataValue); } @@ -1870,10 +1863,7 @@ static void DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes, int iBand, else if (nBitsPerSample == 32 && nSampleFormat == SAMPLEFORMAT_UINT) { uint32_t nNoDataValue = 0; - if (bHasNoData && - dfNoDataValue >= std::numeric_limits::min() && - dfNoDataValue <= std::numeric_limits::max() && - dfNoDataValue == static_cast(dfNoDataValue)) + if (bHasNoData && GDALIsValueExactAs(dfNoDataValue)) { nNoDataValue = static_cast(dfNoDataValue); } @@ -1890,13 +1880,7 @@ static void DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes, int iBand, // FIXME: we should not rely on dfNoDataValue when we support native // data type for nodata int64_t nNoDataValue = 0; - if (bHasNoData && - dfNoDataValue >= - static_cast(std::numeric_limits::min()) && - dfNoDataValue <= - static_cast(std::numeric_limits::max()) && - dfNoDataValue == - static_cast(static_cast(dfNoDataValue))) + if (bHasNoData && GDALIsValueExactAs(dfNoDataValue)) { nNoDataValue = static_cast(dfNoDataValue); } @@ -1913,13 +1897,7 @@ static void DiscardLsb(GByte *pabyBuffer, GPtrDiff_t nBytes, int iBand, // FIXME: we should not rely on dfNoDataValue when we support native // data type for nodata uint64_t nNoDataValue = 0; - if (bHasNoData && - dfNoDataValue >= - static_cast(std::numeric_limits::min()) && - dfNoDataValue <= - static_cast(std::numeric_limits::max()) && - dfNoDataValue == - static_cast(static_cast(dfNoDataValue))) + if (bHasNoData && GDALIsValueExactAs(dfNoDataValue)) { nNoDataValue = static_cast(dfNoDataValue); } @@ -2821,7 +2799,7 @@ CPLErr GTiffDataset::CreateOverviewsFromSrcOverviews(GDALDataset *poSrcDS, /* -------------------------------------------------------------------- */ CPLString osMetadata; - GTIFFBuildOverviewMetadata("NONE", this, osMetadata); + GTIFFBuildOverviewMetadata("NONE", this, false, osMetadata); int nCompression; uint16_t nPlanarConfig; @@ -3108,7 +3086,8 @@ CPLErr GTiffDataset::IBuildOverviews(const char *pszResampling, int nOverviews, /* -------------------------------------------------------------------- */ CPLString osMetadata; - GTIFFBuildOverviewMetadata(pszResampling, this, osMetadata); + const bool bIsForMaskBand = nBands == 1 && GetRasterBand(1)->IsMaskBand(); + GTIFFBuildOverviewMetadata(pszResampling, this, bIsForMaskBand, osMetadata); int nCompression; uint16_t nPlanarConfig; @@ -3695,7 +3674,8 @@ void GTiffDataset::WriteGeoTIFFInfo() else if (CPLFetchBool(m_papszCreationOptions, "WORLDFILE", false)) GDALWriteWorldFile(m_pszFilename, "wld", m_adfGeoTransform); } - else if (GetGCPCount() > 0) + else if (GetGCPCount() > 0 && GetGCPCount() <= knMAX_GCP_COUNT && + m_eProfile != GTiffProfile::BASELINE) { m_bNeedsRewrite = true; @@ -3719,9 +3699,8 @@ void GTiffDataset::WriteGeoTIFFInfo() } } - if (m_eProfile != GTiffProfile::BASELINE) - TIFFSetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS, 6 * GetGCPCount(), - padfTiePoints); + TIFFSetField(m_hTIFF, TIFFTAG_GEOTIEPOINTS, 6 * GetGCPCount(), + padfTiePoints); CPLFree(padfTiePoints); } @@ -8463,7 +8442,6 @@ CPLErr GTiffDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn, m_bGeoTransformValid = false; m_bForceUnsetGTOrGCPs = true; } - if ((m_eProfile == GTiffProfile::BASELINE) && (GetPamFlags() & GPF_DISABLED) == 0) { @@ -8471,7 +8449,21 @@ CPLErr GTiffDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn, } else { - if (GDALPamDataset::GetGCPCount() > 0) + if (nGCPCountIn > knMAX_GCP_COUNT) + { + if (GDALPamDataset::GetGCPCount() == 0 && !m_aoGCPs.empty()) + { + m_bForceUnsetGTOrGCPs = true; + } + ReportError(CE_Warning, CPLE_AppDefined, + "Trying to write %d GCPs, whereas the maximum " + "supported in GeoTIFF tag is %d. " + "Falling back to writing them to PAM", + nGCPCountIn, knMAX_GCP_COUNT); + eErr = GDALPamDataset::SetGCPs(nGCPCountIn, pasGCPListIn, + poGCPSRS); + } + else if (GDALPamDataset::GetGCPCount() > 0) { // Cancel any existing GCPs from PAM file. GDALPamDataset::SetGCPs( diff --git a/frmts/gtiff/gtiffrasterband_write.cpp b/frmts/gtiff/gtiffrasterband_write.cpp index de8d09e6934e..0b7ee7dc8663 100644 --- a/frmts/gtiff/gtiffrasterband_write.cpp +++ b/frmts/gtiff/gtiffrasterband_write.cpp @@ -34,6 +34,7 @@ #include #include "cpl_vsi_virtual.h" +#include "gdal_priv_templates.hpp" #include "gtiff.h" #include "tifvsi.h" @@ -705,6 +706,33 @@ CPLErr GTiffRasterBand::SetColorTable(GDALColorTable *poCT) CPLErr GTiffRasterBand::SetNoDataValue(double dfNoData) { + const auto SetNoDataMembers = [this, dfNoData]() + { + m_bNoDataSet = true; + m_dfNoDataValue = dfNoData; + + m_poGDS->m_bNoDataSet = true; + m_poGDS->m_dfNoDataValue = dfNoData; + + if (eDataType == GDT_Int64 && GDALIsValueExactAs(dfNoData)) + { + m_bNoDataSetAsInt64 = true; + m_nNoDataValueInt64 = static_cast(dfNoData); + + m_poGDS->m_bNoDataSetAsInt64 = true; + m_poGDS->m_nNoDataValueInt64 = static_cast(dfNoData); + } + else if (eDataType == GDT_UInt64 && + GDALIsValueExactAs(dfNoData)) + { + m_bNoDataSetAsUInt64 = true; + m_nNoDataValueUInt64 = static_cast(dfNoData); + + m_poGDS->m_bNoDataSetAsUInt64 = true; + m_poGDS->m_nNoDataValueUInt64 = static_cast(dfNoData); + } + }; + m_poGDS->LoadGeoreferencingAndPamIfNeeded(); if (m_poGDS->m_bNoDataSet && @@ -713,8 +741,7 @@ CPLErr GTiffRasterBand::SetNoDataValue(double dfNoData) { ResetNoDataValues(false); - m_bNoDataSet = true; - m_dfNoDataValue = dfNoData; + SetNoDataMembers(); return CE_None; } @@ -767,11 +794,7 @@ CPLErr GTiffRasterBand::SetNoDataValue(double dfNoData) { ResetNoDataValues(true); - m_poGDS->m_bNoDataSet = true; - m_poGDS->m_dfNoDataValue = dfNoData; - - m_bNoDataSet = true; - m_dfNoDataValue = dfNoData; + SetNoDataMembers(); } return eErr; diff --git a/frmts/gtiff/libgeotiff/geo_normalize.c b/frmts/gtiff/libgeotiff/geo_normalize.c index 11517462c761..f16b67be4d9d 100644 --- a/frmts/gtiff/libgeotiff/geo_normalize.c +++ b/frmts/gtiff/libgeotiff/geo_normalize.c @@ -2241,15 +2241,16 @@ static void GTIFFetchProjParms( GTIF * psGTIF, GTIFDefn * psDefn ) break; } + for( int iParam = 0; iParam < psDefn->nParms; iParam++ ) + { + switch( psDefn->ProjParmId[iParam] ) + { + /* -------------------------------------------------------------------- */ /* Normalize any linear parameters into meters. In GeoTIFF */ /* the linear projection parameter tags are normally in the */ /* units of the coordinate system described. */ /* -------------------------------------------------------------------- */ - for( int iParam = 0; iParam < psDefn->nParms; iParam++ ) - { - switch( psDefn->ProjParmId[iParam] ) - { case ProjFalseEastingGeoKey: case ProjFalseNorthingGeoKey: case ProjFalseOriginEastingGeoKey: @@ -2263,6 +2264,30 @@ static void GTIFFetchProjParms( GTIF * psGTIF, GTIFDefn * psDefn ) } break; +/* -------------------------------------------------------------------- */ +/* Normalize any angular parameters into degrees. In GeoTIFF */ +/* the angular projection parameter tags are normally in the */ +/* units of GeogAngularUnit. Note: this conversion is only done */ +/* since libgeotiff 1.7.4 */ +/* -------------------------------------------------------------------- */ + + case ProjStdParallel1GeoKey: + case ProjStdParallel2GeoKey: + case ProjNatOriginLongGeoKey: + case ProjNatOriginLatGeoKey: + case ProjFalseOriginLongGeoKey: + case ProjFalseOriginLatGeoKey: + case ProjCenterLongGeoKey: + case ProjCenterLatGeoKey: + case ProjStraightVertPoleLongGeoKey: + case ProjRectifiedGridAngleGeoKey: + if( psDefn->UOMAngleInDegrees != 0 + && psDefn->UOMAngleInDegrees != 1.0 ) + { + psDefn->ProjParm[iParam] *= psDefn->UOMAngleInDegrees; + } + break; + default: break; } diff --git a/frmts/gtiff/libgeotiff/geo_normalize.h b/frmts/gtiff/libgeotiff/geo_normalize.h index 87dd22c7e7df..79f588c813b9 100644 --- a/frmts/gtiff/libgeotiff/geo_normalize.h +++ b/frmts/gtiff/libgeotiff/geo_normalize.h @@ -121,9 +121,15 @@ typedef struct { int nParms; /** Projection parameter value. The identify of this parameter - is established from the corresponding entry in ProjParmId. The - value will be measured in meters, or decimal degrees if it is a - linear or angular measure. */ + is established from the corresponding entry in ProjParmId. + In GeoTIFF keys, the values of the projection parameters are expressed + in the units of ProjLinearUnitsGeoKey (for linear measures) or + GeogAngularUnitsGeoKey (for angular measures). + However, the value returned in ProjParam[] will be normalized to meters + or decimal degrees. + Note: until libgeotiff 1.7.3, the conversion to degrees for angular + measures was *not* done when ProjCoordTransGeoKey is present. + */ double ProjParm[MAX_GTIF_PROJPARMS]; /** Projection parameter identifier. For example ProjFalseEastingGeoKey. diff --git a/frmts/gtiff/libgeotiff/geotiff.h b/frmts/gtiff/libgeotiff/geotiff.h index 29f22917f4c4..75de593c8a0e 100644 --- a/frmts/gtiff/libgeotiff/geotiff.h +++ b/frmts/gtiff/libgeotiff/geotiff.h @@ -47,7 +47,7 @@ #define GEOTIFF_SPEC_1_1_MINOR_REVISION 1 /* Library version */ -#define LIBGEOTIFF_VERSION 1710 +#define LIBGEOTIFF_VERSION 1740 #include "geo_config.h" #include "geokeys.h" diff --git a/frmts/gtiff/libgeotiff/geotiff_proj4.c b/frmts/gtiff/libgeotiff/geotiff_proj4.c index 8e3812059973..9d25a7c1a7fc 100644 --- a/frmts/gtiff/libgeotiff/geotiff_proj4.c +++ b/frmts/gtiff/libgeotiff/geotiff_proj4.c @@ -85,7 +85,7 @@ static char **OSRProj4Tokenize( const char *pszFull ) if( pszFull == NULL ) return NULL; - char **papszTokens = (char **) calloc(sizeof(char*),nMaxTokens); + char **papszTokens = (char **) calloc(nMaxTokens, sizeof(char*)); char *pszFullWrk = CPLStrdup(pszFull); diff --git a/frmts/gtiff/libtiff/tif_hash_set.c b/frmts/gtiff/libtiff/tif_hash_set.c index 9792c63f47d0..81dea3fcf26a 100644 --- a/frmts/gtiff/libtiff/tif_hash_set.c +++ b/frmts/gtiff/libtiff/tif_hash_set.c @@ -146,7 +146,7 @@ TIFFHashSet *TIFFHashSetNew(TIFFHashSetHashFunc fnHashFunc, set->fnEqualFunc = fnEqualFunc ? fnEqualFunc : TIFFHashSetEqualPointer; set->fnFreeEltFunc = fnFreeEltFunc; set->nSize = 0; - set->tabList = (TIFFList **)(calloc(sizeof(TIFFList *), 53)); + set->tabList = (TIFFList **)(calloc(53, sizeof(TIFFList *))); if (set->tabList == NULL) { free(set); @@ -367,7 +367,7 @@ static bool TIFFHashSetRehash(TIFFHashSet *set) { int nNewAllocatedSize = anPrimes[set->nIndiceAllocatedSize]; TIFFList **newTabList = - (TIFFList **)(calloc(sizeof(TIFFList *), nNewAllocatedSize)); + (TIFFList **)(calloc(nNewAllocatedSize, sizeof(TIFFList *))); if (newTabList == NULL) return false; #ifdef HASH_DEBUG diff --git a/frmts/hdf4/CMakeLists.txt b/frmts/hdf4/CMakeLists.txt index fccc9213890f..3dc9521e2493 100644 --- a/frmts/hdf4/CMakeLists.txt +++ b/frmts/hdf4/CMakeLists.txt @@ -18,7 +18,8 @@ set_property(SOURCE hdf-eos/EHapi.c hdf-eos/GDapi.c hdf-eos/SWapi.c PROPERTY SKI add_gdal_driver(TARGET gdal_HDF4 SOURCES ${_SOURCES} CORE_SOURCES hdf4drivercore.cpp - PLUGIN_CAPABLE) + PLUGIN_CAPABLE + NO_SHARED_SYMBOL_WITH_CORE) unset(_SOURCES) if(NOT TARGET gdal_HDF4) diff --git a/frmts/hdf4/hdf4drivercore.h b/frmts/hdf4/hdf4drivercore.h index 798be0263cb1..eb99e6fdfd5e 100644 --- a/frmts/hdf4/hdf4drivercore.h +++ b/frmts/hdf4/hdf4drivercore.h @@ -35,12 +35,22 @@ constexpr const char *HDF4_DRIVER_NAME = "HDF4"; constexpr const char *HDF4_IMAGE_DRIVER_NAME = "HDF4Image"; -int CPL_DLL HDF4DatasetIdentify(GDALOpenInfo *poOpenInfo); +#define HDF4DatasetIdentify PLUGIN_SYMBOL_NAME(HDF4DatasetIdentify) -int CPL_DLL HDF4ImageDatasetIdentify(GDALOpenInfo *poOpenInfo); +#define HDF4ImageDatasetIdentify PLUGIN_SYMBOL_NAME(HDF4ImageDatasetIdentify) -void CPL_DLL HDF4DriverSetCommonMetadata(GDALDriver *poDriver); +#define HDF4DriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(HDF4DriverSetCommonMetadata) -void CPL_DLL HDF4ImageDriverSetCommonMetadata(GDALDriver *poDriver); +#define HDF4ImageDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(HDF4ImageDriverSetCommonMetadata) + +int HDF4DatasetIdentify(GDALOpenInfo *poOpenInfo); + +int HDF4ImageDatasetIdentify(GDALOpenInfo *poOpenInfo); + +void HDF4DriverSetCommonMetadata(GDALDriver *poDriver); + +void HDF4ImageDriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/hdf5/CMakeLists.txt b/frmts/hdf5/CMakeLists.txt index 85bf48585032..284ae9c8d148 100644 --- a/frmts/hdf5/CMakeLists.txt +++ b/frmts/hdf5/CMakeLists.txt @@ -21,7 +21,8 @@ set(SOURCE add_gdal_driver(TARGET gdal_HDF5 SOURCES ${SOURCE} CORE_SOURCES hdf5drivercore.cpp - PLUGIN_CAPABLE) + PLUGIN_CAPABLE + NO_SHARED_SYMBOL_WITH_CORE) set(GDAL_DATA_FILES ${CMAKE_CURRENT_SOURCE_DIR}/data/bag_template.xml diff --git a/frmts/hdf5/gh5_convenience.cpp b/frmts/hdf5/gh5_convenience.cpp index 304032e08e3f..e48b5c630d21 100644 --- a/frmts/hdf5/gh5_convenience.cpp +++ b/frmts/hdf5/gh5_convenience.cpp @@ -26,6 +26,7 @@ * DEALINGS IN THE SOFTWARE. ****************************************************************************/ +#include "cpl_float.h" #include "gh5_convenience.h" /************************************************************************/ @@ -209,6 +210,16 @@ bool GH5_FetchAttribute(hid_t loc_id, const char *pszAttrName, double &dfResult, pszAttrName, static_cast(nVal), dfResult); } } +#ifdef HDF5_HAVE_FLOAT16 + else if (H5Tequal(H5T_NATIVE_FLOAT16, hAttrNativeType)) + { + const uint16_t nVal16 = *((uint16_t *)buf); + const uint32_t nVal32 = CPLHalfToFloat(nVal16); + float fVal; + memcpy(&fVal, &nVal32, sizeof(fVal)); + dfResult = fVal; + } +#endif else if (H5Tequal(H5T_NATIVE_FLOAT, hAttrNativeType)) dfResult = *((float *)buf); else if (H5Tequal(H5T_NATIVE_DOUBLE, hAttrNativeType)) diff --git a/frmts/hdf5/hdf5_api.h b/frmts/hdf5/hdf5_api.h index 013534310488..0b3110d2cc2e 100644 --- a/frmts/hdf5/hdf5_api.h +++ b/frmts/hdf5/hdf5_api.h @@ -46,6 +46,10 @@ #include "hdf5.h" +#if defined(H5T_NATIVE_FLOAT16) && defined(H5_HAVE__FLOAT16) +#define HDF5_HAVE_FLOAT16 +#endif + #ifdef _MSC_VER #pragma warning(pop) #endif diff --git a/frmts/hdf5/hdf5dataset.cpp b/frmts/hdf5/hdf5dataset.cpp index 499b8a86c877..64c1fea2e92b 100644 --- a/frmts/hdf5/hdf5dataset.cpp +++ b/frmts/hdf5/hdf5dataset.cpp @@ -44,6 +44,7 @@ #include "cpl_conv.h" #include "cpl_error.h" +#include "cpl_float.h" #include "cpl_string.h" #include "gdal.h" #include "gdal_frmts.h" @@ -201,6 +202,10 @@ GDALDataType HDF5Dataset::GetDataType(hid_t TypeID) return GDT_Unknown; #endif } +#ifdef HDF5_HAVE_FLOAT16 + else if (H5Tequal(H5T_NATIVE_FLOAT16, TypeID)) + return GDT_Float32; +#endif else if (H5Tequal(H5T_NATIVE_FLOAT, TypeID)) return GDT_Float32; else if (H5Tequal(H5T_NATIVE_DOUBLE, TypeID)) @@ -258,6 +263,10 @@ GDALDataType HDF5Dataset::GetDataType(hid_t TypeID) eDataType = GDT_Unknown; #endif } +#ifdef HDF5_HAVE_FLOAT16 + else if (H5Tequal(H5T_NATIVE_FLOAT16, ElemTypeID)) + eDataType = GDT_CFloat32; +#endif else if (H5Tequal(H5T_NATIVE_FLOAT, ElemTypeID)) eDataType = GDT_CFloat32; else if (H5Tequal(H5T_NATIVE_DOUBLE, ElemTypeID)) @@ -272,6 +281,32 @@ GDALDataType HDF5Dataset::GetDataType(hid_t TypeID) return GDT_Unknown; } +/************************************************************************/ +/* IsNativeCFloat16() */ +/************************************************************************/ + +/* static*/ bool HDF5Dataset::IsNativeCFloat16(hid_t hDataType) +{ +#ifdef HDF5_HAVE_FLOAT16 + // For complex the compound type must contain 2 elements + if (H5Tget_class(hDataType) != H5T_COMPOUND || + H5Tget_nmembers(hDataType) != 2) + return false; + + // For complex the native types of both elements should be the same + hid_t ElemTypeID = H5Tget_member_type(hDataType, 0); + hid_t Elem2TypeID = H5Tget_member_type(hDataType, 1); + const bool bRet = H5Tequal(ElemTypeID, H5T_NATIVE_FLOAT16) > 0 && + H5Tequal(Elem2TypeID, H5T_NATIVE_FLOAT16) > 0; + H5Tclose(ElemTypeID); + H5Tclose(Elem2TypeID); + return bRet; +#else + CPL_IGNORE_RET_VAL(hDataType); + return false; +#endif +} + /************************************************************************/ /* GetDataTypeName() */ /* */ @@ -304,6 +339,10 @@ const char *HDF5Dataset::GetDataTypeName(hid_t TypeID) return "32/64-bit integer"; else if (H5Tequal(H5T_NATIVE_ULONG, TypeID)) return "32/64-bit unsigned integer"; +#ifdef HDF5_HAVE_FLOAT16 + else if (H5Tequal(H5T_NATIVE_FLOAT16, TypeID)) + return "16-bit floating-point"; +#endif else if (H5Tequal(H5T_NATIVE_FLOAT, TypeID)) return "32-bit floating-point"; else if (H5Tequal(H5T_NATIVE_DOUBLE, TypeID)) @@ -348,6 +387,13 @@ const char *HDF5Dataset::GetDataTypeName(hid_t TypeID) H5Tclose(ElemTypeID); return "complex, 32/64-bit integer"; } +#ifdef HDF5_HAVE_FLOAT16 + else if (H5Tequal(H5T_NATIVE_FLOAT16, ElemTypeID)) + { + H5Tclose(ElemTypeID); + return "complex, 16-bit floating-point"; + } +#endif else if (H5Tequal(H5T_NATIVE_FLOAT, ElemTypeID)) { H5Tclose(ElemTypeID); @@ -1132,6 +1178,28 @@ static herr_t HDF5AttrIterate(hid_t hH5ObjID, const char *pszAttrName, psContext->m_osValue += szData; } } +#ifdef HDF5_HAVE_FLOAT16 + else if (H5Tequal(H5T_NATIVE_FLOAT16, hAttrNativeType) > 0) + { + for (hsize_t i = 0; i < nAttrElmts; i++) + { + const uint16_t nVal16 = static_cast(buf)[i]; + const uint32_t nVal32 = CPLHalfToFloat(nVal16); + float fVal; + memcpy(&fVal, &nVal32, sizeof(fVal)); + CPLsnprintf(szData, nDataLen, "%.8g", fVal); + if (psContext->m_osValue.size() > MAX_METADATA_LEN) + { + CPLError(CE_Warning, CPLE_OutOfMemory, + "Header data too long. Truncated"); + break; + } + if (i > 0) + psContext->m_osValue += ' '; + psContext->m_osValue += szData; + } + } +#endif else if (H5Tequal(H5T_NATIVE_FLOAT, hAttrNativeType) > 0) { for (hsize_t i = 0; i < nAttrElmts; i++) diff --git a/frmts/hdf5/hdf5dataset.h b/frmts/hdf5/hdf5dataset.h index dc9245b4ca31..646f7c6b6297 100644 --- a/frmts/hdf5/hdf5dataset.h +++ b/frmts/hdf5/hdf5dataset.h @@ -240,8 +240,6 @@ class HDF5Dataset CPL_NON_FINAL : public GDALPamDataset char *CreatePath(HDF5GroupObjects *); static void DestroyH5Objects(HDF5GroupObjects *); - static const char *GetDataTypeName(hid_t); - /** * Reads an array of double attributes from the HDF5 metadata. * It reads the attributes directly on its binary form directly, @@ -275,6 +273,8 @@ class HDF5Dataset CPL_NON_FINAL : public GDALPamDataset static std::shared_ptr OpenGroup( const std::shared_ptr &poSharedResources); + static bool IsNativeCFloat16(hid_t hDataType); + static const char *GetDataTypeName(hid_t); static GDALDataType GetDataType(hid_t); }; diff --git a/frmts/hdf5/hdf5drivercore.h b/frmts/hdf5/hdf5drivercore.h index 7490b394b89f..f779ba1544ce 100644 --- a/frmts/hdf5/hdf5drivercore.h +++ b/frmts/hdf5/hdf5drivercore.h @@ -43,28 +43,47 @@ constexpr const char *S104_DRIVER_NAME = "S104"; constexpr const char *S111_DRIVER_NAME = "S111"; -int CPL_DLL HDF5DatasetIdentify(GDALOpenInfo *poOpenInfo); +#define HDF5DatasetIdentify PLUGIN_SYMBOL_NAME(HDF5DatasetIdentify) +#define HDF5ImageDatasetIdentify PLUGIN_SYMBOL_NAME(HDF5ImageDatasetIdentify) +#define BAGDatasetIdentify PLUGIN_SYMBOL_NAME(BAGDatasetIdentify) +#define S102DatasetIdentify PLUGIN_SYMBOL_NAME(S102DatasetIdentify) +#define S104DatasetIdentify PLUGIN_SYMBOL_NAME(S104DatasetIdentify) +#define S111DatasetIdentify PLUGIN_SYMBOL_NAME(S111DatasetIdentify) +#define HDF5DriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(HDF5DriverSetCommonMetadata) +#define HDF5ImageDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(HDF5ImageDriverSetCommonMetadata) +#define BAGDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(BAGDriverSetCommonMetadata) +#define S102DriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(S102DriverSetCommonMetadata) +#define S104DriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(S104DriverSetCommonMetadata) +#define S111DriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(S111DriverSetCommonMetadata) -int CPL_DLL HDF5ImageDatasetIdentify(GDALOpenInfo *poOpenInfo); +int HDF5DatasetIdentify(GDALOpenInfo *poOpenInfo); -int CPL_DLL BAGDatasetIdentify(GDALOpenInfo *poOpenInfo); +int HDF5ImageDatasetIdentify(GDALOpenInfo *poOpenInfo); -int CPL_DLL S102DatasetIdentify(GDALOpenInfo *poOpenInfo); +int BAGDatasetIdentify(GDALOpenInfo *poOpenInfo); -int CPL_DLL S104DatasetIdentify(GDALOpenInfo *poOpenInfo); +int S102DatasetIdentify(GDALOpenInfo *poOpenInfo); -int CPL_DLL S111DatasetIdentify(GDALOpenInfo *poOpenInfo); +int S104DatasetIdentify(GDALOpenInfo *poOpenInfo); -void CPL_DLL HDF5DriverSetCommonMetadata(GDALDriver *poDriver); +int S111DatasetIdentify(GDALOpenInfo *poOpenInfo); -void CPL_DLL HDF5ImageDriverSetCommonMetadata(GDALDriver *poDriver); +void HDF5DriverSetCommonMetadata(GDALDriver *poDriver); -void CPL_DLL BAGDriverSetCommonMetadata(GDALDriver *poDriver); +void HDF5ImageDriverSetCommonMetadata(GDALDriver *poDriver); -void CPL_DLL S102DriverSetCommonMetadata(GDALDriver *poDriver); +void BAGDriverSetCommonMetadata(GDALDriver *poDriver); -void CPL_DLL S104DriverSetCommonMetadata(GDALDriver *poDriver); +void S102DriverSetCommonMetadata(GDALDriver *poDriver); -void CPL_DLL S111DriverSetCommonMetadata(GDALDriver *poDriver); +void S104DriverSetCommonMetadata(GDALDriver *poDriver); + +void S111DriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/hdf5/hdf5imagedataset.cpp b/frmts/hdf5/hdf5imagedataset.cpp index f25d1ea2d846..37364c3beab6 100644 --- a/frmts/hdf5/hdf5imagedataset.cpp +++ b/frmts/hdf5/hdf5imagedataset.cpp @@ -29,6 +29,7 @@ #include "hdf5_api.h" +#include "cpl_float.h" #include "cpl_string.h" #include "gdal_frmts.h" #include "gdal_pam.h" @@ -72,9 +73,10 @@ class HDF5ImageDataset final : public HDF5Dataset int dimensions; hid_t dataset_id; hid_t dataspace_id; - hsize_t size; - hid_t datatype; hid_t native; +#ifdef HDF5_HAVE_FLOAT16 + bool m_bConvertFromFloat16 = false; +#endif Hdf5ProductType iSubdatasetType; HDF5CSKProductEnum iCSKProductType; double adfGeoTransform[6]; @@ -209,9 +211,9 @@ class HDF5ImageDataset final : public HDF5Dataset /************************************************************************/ HDF5ImageDataset::HDF5ImageDataset() : dims(nullptr), maxdims(nullptr), poH5Objects(nullptr), ndims(0), - dimensions(0), dataset_id(-1), dataspace_id(-1), size(0), datatype(-1), - native(-1), iSubdatasetType(UNKNOWN_PRODUCT), - iCSKProductType(PROD_UNKNOWN), bHasGeoTransform(false) + dimensions(0), dataset_id(-1), dataspace_id(-1), native(-1), + iSubdatasetType(UNKNOWN_PRODUCT), iCSKProductType(PROD_UNKNOWN), + bHasGeoTransform(false) { m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); @@ -236,8 +238,6 @@ HDF5ImageDataset::~HDF5ImageDataset() H5Dclose(dataset_id); if (dataspace_id > 0) H5Sclose(dataspace_id); - if (datatype > 0) - H5Tclose(datatype); if (native > 0) H5Tclose(native); @@ -465,6 +465,42 @@ CPLErr HDF5ImageRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, return CE_Failure; } +#ifdef HDF5_HAVE_FLOAT16 + if (eDataType == GDT_Float32 && poGDS->m_bConvertFromFloat16) + { + for (size_t i = static_cast(nBlockXSize) * nBlockYSize; i > 0; + /* do nothing */) + { + --i; + uint16_t nVal16; + memcpy(&nVal16, static_cast(pImage) + i, + sizeof(nVal16)); + const uint32_t nVal32 = CPLHalfToFloat(nVal16); + float fVal; + memcpy(&fVal, &nVal32, sizeof(fVal)); + *(static_cast(pImage) + i) = fVal; + } + } + else if (eDataType == GDT_CFloat32 && poGDS->m_bConvertFromFloat16) + { + for (size_t i = static_cast(nBlockXSize) * nBlockYSize; i > 0; + /* do nothing */) + { + --i; + for (int j = 1; j >= 0; --j) + { + uint16_t nVal16; + memcpy(&nVal16, static_cast(pImage) + 2 * i + j, + sizeof(nVal16)); + const uint32_t nVal32 = CPLHalfToFloat(nVal16); + float fVal; + memcpy(&fVal, &nVal32, sizeof(fVal)); + *(static_cast(pImage) + 2 * i + j) = fVal; + } + } + } +#endif + return CE_None; } @@ -482,6 +518,15 @@ CPLErr HDF5ImageRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, { HDF5ImageDataset *poGDS = static_cast(poDS); +#ifdef HDF5_HAVE_FLOAT16 + if (poGDS->m_bConvertFromFloat16) + { + return GDALPamRasterBand::IRasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, + eBufType, nPixelSpace, nLineSpace, psExtraArg); + } +#endif + const bool bIsBandInterleavedData = poGDS->ndims == 3 && poGDS->m_nOtherDimIndex == 0 && poGDS->GetYIndex() == 1 && poGDS->GetXIndex() == 2; @@ -763,6 +808,16 @@ CPLErr HDF5ImageDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, GDALRasterIOExtraArg *psExtraArg) { +#ifdef HDF5_HAVE_FLOAT16 + if (m_bConvertFromFloat16) + { + return HDF5Dataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, nPixelSpace, + nLineSpace, nBandSpace, psExtraArg); + } +#endif + const auto IsConsecutiveBands = [](const int *panVals, int nCount) { for (int i = 1; i < nCount; ++i) @@ -1023,9 +1078,25 @@ GDALDataset *HDF5ImageDataset::Open(GDALOpenInfo *poOpenInfo) static_cast(CPLCalloc(poDS->ndims, sizeof(hsize_t))); poDS->dimensions = H5Sget_simple_extent_dims(poDS->dataspace_id, poDS->dims, poDS->maxdims); - poDS->datatype = H5Dget_type(poDS->dataset_id); - poDS->size = H5Tget_size(poDS->datatype); - poDS->native = H5Tget_native_type(poDS->datatype, H5T_DIR_ASCEND); + auto datatype = H5Dget_type(poDS->dataset_id); + poDS->native = H5Tget_native_type(datatype, H5T_DIR_ASCEND); + H5Tclose(datatype); + + const auto eGDALDataType = poDS->GetDataType(poDS->native); + if (eGDALDataType == GDT_Unknown) + { + CPLError(CE_Failure, CPLE_AppDefined, "Unhandled HDF5 data type"); + delete poDS; + return nullptr; + } + +#ifdef HDF5_HAVE_FLOAT16 + if (H5Tequal(H5T_NATIVE_FLOAT16, poDS->native) || + IsNativeCFloat16(poDS->native)) + { + poDS->m_bConvertFromFloat16 = true; + } +#endif // CSK code in IdentifyProductType() and CreateProjections() // uses dataset metadata. @@ -1155,34 +1226,53 @@ GDALDataset *HDF5ImageDataset::Open(GDALOpenInfo *poOpenInfo) { HDF5Dataset::CreateMetadata(poDS->m_hHDF5, poDS->poH5Objects, H5G_DATASET, false, aosMetadata); - if (nBands > 1) + if (nBands > 1 && poDS->nRasterXSize != nBands && + poDS->nRasterYSize != nBands) { - // Logic specific of Planet data cubes with per-band metadata items - static const struct - { - const char *pszSrcName; - const char *pszDstName; - } asItems[] = { - {"calibration_coefficients", "calibration_coefficient"}, - {"center_wavelengths", "center_wavelength"}, - {"fwhm", "fwhm"}, - {"bad_band_list", "bad_band"}, - }; - - for (const auto &sItem : asItems) + // Heuristics to detect non-scalar attributes, that are intended + // to be attached to a specific band. + const CPLStringList aosMetadataDup(aosMetadata); + for (const auto &[pszKey, pszValue] : + cpl::IterateNameValue(aosMetadataDup)) { - const char *pszVal = - aosMetadata.FetchNameValue(sItem.pszSrcName); - if (pszVal) + const hid_t hAttrID = H5Aopen_name(poDS->dataset_id, pszKey); + const hid_t hAttrSpace = H5Aget_space(hAttrID); + if (H5Sget_simple_extent_ndims(hAttrSpace) == 1 && + H5Sget_simple_extent_npoints(hAttrSpace) == nBands) { - CPLStringList aosTokens(CSLTokenizeString2(pszVal, " ", 0)); + CPLStringList aosTokens( + CSLTokenizeString2(pszValue, " ", 0)); if (aosTokens.size() == nBands) { - oMapBandSpecificMetadata[sItem.pszDstName] = + std::string osAttrName(pszKey); + if (osAttrName.size() > strlen("_coefficients") && + osAttrName.substr(osAttrName.size() - + strlen("_coefficients")) == + "_coefficients") + { + osAttrName.pop_back(); + } + else if (osAttrName.size() > strlen("_wavelengths") && + osAttrName.substr(osAttrName.size() - + strlen("_wavelengths")) == + "_wavelengths") + { + osAttrName.pop_back(); + } + else if (osAttrName.size() > strlen("_list") && + osAttrName.substr(osAttrName.size() - + strlen("_list")) == "_list") + { + osAttrName.resize(osAttrName.size() - + strlen("_list")); + } + oMapBandSpecificMetadata[osAttrName] = std::move(aosTokens); - aosMetadata.SetNameValue(sItem.pszSrcName, nullptr); + aosMetadata.SetNameValue(pszKey, nullptr); } } + H5Sclose(hAttrSpace); + H5Aclose(hAttrID); } } } @@ -1241,8 +1331,8 @@ GDALDataset *HDF5ImageDataset::Open(GDALOpenInfo *poOpenInfo) for (int i = 0; i < nBands; i++) { - HDF5ImageRasterBand *const poBand = new HDF5ImageRasterBand( - poDS, i + 1, poDS->GetDataType(poDS->native)); + HDF5ImageRasterBand *const poBand = + new HDF5ImageRasterBand(poDS, i + 1, eGDALDataType); poDS->SetBand(i + 1, poBand); diff --git a/frmts/hdf5/hdf5multidim.cpp b/frmts/hdf5/hdf5multidim.cpp index fa27ff79a051..f5a0704c458f 100644 --- a/frmts/hdf5/hdf5multidim.cpp +++ b/frmts/hdf5/hdf5multidim.cpp @@ -29,6 +29,8 @@ #include "hdf5eosparser.h" #include "s100.h" +#include "cpl_float.h" + #include #include #include @@ -159,7 +161,16 @@ BuildDataType(hid_t hDataType, bool &bHasString, bool &bNonNativeDataType, const auto klass = H5Tget_class(hDataType); GDALDataType eDT = ::HDF5Dataset::GetDataType(hDataType); if (eDT != GDT_Unknown) + { +#ifdef HDF5_HAVE_FLOAT16 + if (H5Tequal(hDataType, H5T_NATIVE_FLOAT16) || + HDF5Dataset::IsNativeCFloat16(hDataType)) + { + bNonNativeDataType = true; + } +#endif return GDALExtendedDataType::Create(eDT); + } else if (klass == H5T_STRING) { bHasString = true; @@ -2326,9 +2337,44 @@ static void CopyValue(const GByte *pabySrcBuffer, hid_t hSrcDataType, { if (dstDataType.GetClass() != GEDTC_COMPOUND) { + const auto eSrcDataType = ::HDF5Dataset::GetDataType(hSrcDataType); // Typically source is complex data type - auto srcDataType(GDALExtendedDataType::Create( - ::HDF5Dataset::GetDataType(hSrcDataType))); +#ifdef HDF5_HAVE_FLOAT16 + if (eSrcDataType == GDT_CFloat32 && + ::HDF5Dataset::IsNativeCFloat16(hSrcDataType)) + { + if (dstDataType.GetNumericDataType() == GDT_CFloat32) + { + for (int j = 0; j <= 1; ++j) + { + uint16_t nVal16; + memcpy(&nVal16, pabySrcBuffer + j * sizeof(nVal16), + sizeof(nVal16)); + const uint32_t nVal32 = CPLHalfToFloat(nVal16); + memcpy(pabyDstBuffer + j * sizeof(float), &nVal32, + sizeof(nVal32)); + } + } + else if (dstDataType.GetNumericDataType() == GDT_CFloat64) + { + for (int j = 0; j <= 1; ++j) + { + uint16_t nVal16; + memcpy(&nVal16, pabySrcBuffer + j * sizeof(nVal16), + sizeof(nVal16)); + const uint32_t nVal32 = CPLHalfToFloat(nVal16); + float fVal; + memcpy(&fVal, &nVal32, sizeof(fVal)); + double dfVal = fVal; + memcpy(pabyDstBuffer + j * sizeof(double), &dfVal, + sizeof(dfVal)); + } + } + return; + } + +#endif + auto srcDataType(GDALExtendedDataType::Create(eSrcDataType)); if (srcDataType.GetClass() == GEDTC_NUMERIC && srcDataType.GetNumericDataType() != GDT_Unknown) { @@ -2364,6 +2410,19 @@ static void CopyValue(const GByte *pabySrcBuffer, hid_t hSrcDataType, CopyValue(pabySrcBuffer, hParent, pabyDstBuffer, dstDataType, {}); H5Tclose(hParent); } +#ifdef HDF5_HAVE_FLOAT16 + else if (H5Tequal(hSrcDataType, H5T_NATIVE_FLOAT16)) + { + uint16_t nVal16; + memcpy(&nVal16, pabySrcBuffer, sizeof(nVal16)); + const uint32_t nVal32 = CPLHalfToFloat(nVal16); + float fVal; + memcpy(&fVal, &nVal32, sizeof(fVal)); + GDALExtendedDataType::CopyValue( + &fVal, GDALExtendedDataType::Create(GDT_Float32), pabyDstBuffer, + dstDataType); + } +#endif else { GDALDataType eDT = ::HDF5Dataset::GetDataType(hSrcDataType); diff --git a/frmts/heif/CMakeLists.txt b/frmts/heif/CMakeLists.txt index 1947f06735d2..3ad5d9896423 100644 --- a/frmts/heif/CMakeLists.txt +++ b/frmts/heif/CMakeLists.txt @@ -1,7 +1,8 @@ add_gdal_driver(TARGET gdal_HEIF SOURCES heifdataset.cpp CORE_SOURCES heifdrivercore.cpp - PLUGIN_CAPABLE) + PLUGIN_CAPABLE + NO_SHARED_SYMBOL_WITH_CORE) if(TARGET gdal_HEIF_core) target_include_directories(gdal_HEIF_core PRIVATE $) diff --git a/frmts/heif/heifdrivercore.h b/frmts/heif/heifdrivercore.h index f15ebf06e237..1e1a8d437a0f 100644 --- a/frmts/heif/heifdrivercore.h +++ b/frmts/heif/heifdrivercore.h @@ -32,8 +32,13 @@ constexpr const char *DRIVER_NAME = "HEIF"; -int CPL_DLL HEIFDriverIdentifySimplified(GDALOpenInfo *poOpenInfo); +#define HEIFDriverIdentifySimplified \ + PLUGIN_SYMBOL_NAME(HEIFDriverIdentifySimplified) +#define HEIFDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(HEIFDriverSetCommonMetadata) -void CPL_DLL HEIFDriverSetCommonMetadata(GDALDriver *poDriver); +int HEIFDriverIdentifySimplified(GDALOpenInfo *poOpenInfo); + +void HEIFDriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/jp2kak/CMakeLists.txt b/frmts/jp2kak/CMakeLists.txt index ee7bcc7ccad7..fa95a832ab21 100644 --- a/frmts/jp2kak/CMakeLists.txt +++ b/frmts/jp2kak/CMakeLists.txt @@ -5,7 +5,8 @@ add_gdal_driver(TARGET gdal_JP2KAK subfile_source.h vsil_target.h CORE_SOURCES jp2kakdrivercore.cpp - PLUGIN_CAPABLE) + PLUGIN_CAPABLE + NO_SHARED_SYMBOL_WITH_CORE) if(TARGET gdal_JP2KAK_core) target_include_directories(gdal_JP2KAK_core PRIVATE $) diff --git a/frmts/jp2kak/jp2kakdrivercore.h b/frmts/jp2kak/jp2kakdrivercore.h index f7a1b4bfe5e6..79d6f2adb000 100644 --- a/frmts/jp2kak/jp2kakdrivercore.h +++ b/frmts/jp2kak/jp2kakdrivercore.h @@ -38,8 +38,12 @@ constexpr unsigned char jp2_header[] = {0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, constexpr unsigned char jpc_header[] = {0xff, 0x4f}; -int CPL_DLL JP2KAKDatasetIdentify(GDALOpenInfo *poOpenInfo); +#define JP2KAKDatasetIdentify PLUGIN_SYMBOL_NAME(JP2KAKDatasetIdentify) +#define JP2KAKDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(JP2KAKDriverSetCommonMetadata) -void CPL_DLL JP2KAKDriverSetCommonMetadata(GDALDriver *poDriver); +int JP2KAKDatasetIdentify(GDALOpenInfo *poOpenInfo); + +void JP2KAKDriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/jp2lura/CMakeLists.txt b/frmts/jp2lura/CMakeLists.txt index 1d90c39f01d9..d9241c70dfae 100644 --- a/frmts/jp2lura/CMakeLists.txt +++ b/frmts/jp2lura/CMakeLists.txt @@ -8,7 +8,8 @@ add_gdal_driver( jp2lurarasterband.cpp CORE_SOURCES jp2luradrivercore.cpp - PLUGIN_CAPABLE) + PLUGIN_CAPABLE + NO_SHARED_SYMBOL_WITH_CORE) if(NOT TARGET gdal_JP2Lura) return() diff --git a/frmts/jp2lura/jp2luradrivercore.h b/frmts/jp2lura/jp2luradrivercore.h index 4bea64ddebc6..bbe19452cfa7 100644 --- a/frmts/jp2lura/jp2luradrivercore.h +++ b/frmts/jp2lura/jp2luradrivercore.h @@ -38,8 +38,12 @@ constexpr unsigned char jpc_header[] = {0xff, 0x4f, 0xff, 0x51}; // SOC + RSIZ markers constexpr unsigned char jp2_box_jp[] = {0x6a, 0x50, 0x20, 0x20}; /* 'jP ' */ -int CPL_DLL JP2LuraDriverIdentify(GDALOpenInfo *poOpenInfo); +#define JP2LuraDriverIdentify PLUGIN_SYMBOL_NAME(JP2LuraDriverIdentify) +#define JP2LuraDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(JP2LuraDriverSetCommonMetadata) -void CPL_DLL JP2LuraDriverSetCommonMetadata(GDALDriver *poDriver); +int JP2LuraDriverIdentify(GDALOpenInfo *poOpenInfo); + +void JP2LuraDriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/jpeg/CMakeLists.txt b/frmts/jpeg/CMakeLists.txt index 95710a13b727..ae8127d4f07e 100644 --- a/frmts/jpeg/CMakeLists.txt +++ b/frmts/jpeg/CMakeLists.txt @@ -4,6 +4,7 @@ add_gdal_driver( CORE_SOURCES jpegdrivercore.cpp PLUGIN_CAPABLE_IF "NOT GDAL_USE_JPEG_INTERNAL\\\;NOT GDAL_USE_JPEG12_INTERNAL\\\;NOT GDAL_USE_ZLIB_INTERNAL" + NO_SHARED_SYMBOL_WITH_CORE ) if(TARGET gdal_JPEG_core) diff --git a/frmts/jpeg/jpegdrivercore.h b/frmts/jpeg/jpegdrivercore.h index 39570c89ace2..0dc3ccba000a 100644 --- a/frmts/jpeg/jpegdrivercore.h +++ b/frmts/jpeg/jpegdrivercore.h @@ -37,10 +37,15 @@ constexpr const char *DRIVER_NAME = "JPEG"; -bool CPL_DLL JPEGDatasetIsJPEGLS(GDALOpenInfo *poOpenInfo); +#define JPEGDatasetIsJPEGLS PLUGIN_SYMBOL_NAME(JPEGDatasetIsJPEGLS) +#define JPEGDriverIdentify PLUGIN_SYMBOL_NAME(JPEGDriverIdentify) +#define JPEGDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(JPEGDriverSetCommonMetadata) -int CPL_DLL JPEGDriverIdentify(GDALOpenInfo *poOpenInfo); +bool JPEGDatasetIsJPEGLS(GDALOpenInfo *poOpenInfo); -void CPL_DLL JPEGDriverSetCommonMetadata(GDALDriver *poDriver); +int JPEGDriverIdentify(GDALOpenInfo *poOpenInfo); + +void JPEGDriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/jpeg/jpgdataset.cpp b/frmts/jpeg/jpgdataset.cpp index ce0a87be09c2..9ee903afbb53 100644 --- a/frmts/jpeg/jpgdataset.cpp +++ b/frmts/jpeg/jpgdataset.cpp @@ -331,7 +331,7 @@ void JPGDatasetCommon::ReadXMPMetadata() // Not a marker if (abyChunkHeader[0] != 0xFF) - continue; + break; // Stop on Start of Scan if (abyChunkHeader[1] == 0xDA) @@ -406,17 +406,18 @@ void JPGDatasetCommon::ReadFLIRMetadata() 1) break; + const int nMarkerLength = + abyChunkHeader[2] * 256 + abyChunkHeader[3] - 2; + nChunkLoc += 4 + nMarkerLength; + // Not a marker if (abyChunkHeader[0] != 0xFF) - continue; + break; // Stop on Start of Scan if (abyChunkHeader[1] == 0xDA) break; - int nMarkerLength = abyChunkHeader[2] * 256 + abyChunkHeader[3] - 2; - nChunkLoc += 4 + nMarkerLength; - if (abyChunkHeader[1] == 0xe1 && memcmp(abyChunkHeader + 4, "FLIR\0", 5) == 0) { diff --git a/frmts/jpegxl/CMakeLists.txt b/frmts/jpegxl/CMakeLists.txt index 52f3216b50e6..a309745508e0 100644 --- a/frmts/jpegxl/CMakeLists.txt +++ b/frmts/jpegxl/CMakeLists.txt @@ -2,7 +2,8 @@ add_gdal_driver( TARGET gdal_JPEGXL SOURCES jpegxl.cpp CORE_SOURCES jpegxldrivercore.cpp - PLUGIN_CAPABLE) + PLUGIN_CAPABLE + NO_SHARED_SYMBOL_WITH_CORE) function(declare_def DEF) if(TARGET gdal_JPEGXL) diff --git a/frmts/jpegxl/jpegxl.cpp b/frmts/jpegxl/jpegxl.cpp index 6d02817e0e00..c71f8390c678 100644 --- a/frmts/jpegxl/jpegxl.cpp +++ b/frmts/jpegxl/jpegxl.cpp @@ -1838,7 +1838,8 @@ GDALDataset *JPEGXLDataset::CreateCopy(const char *pszFilename, nJPEGXLContent); size_t nInsertPos = 0; - if (abyData[0] == 0xff && abyData[1] == 0x0a) + if (abyData.size() >= 2 && abyData[0] == 0xff && + abyData[1] == 0x0a) { // If we get a "naked" codestream, insert it into a // ISOBMFF-based container diff --git a/frmts/jpegxl/jpegxldrivercore.h b/frmts/jpegxl/jpegxldrivercore.h index a1e702e4bd7d..b45543ae26d1 100644 --- a/frmts/jpegxl/jpegxldrivercore.h +++ b/frmts/jpegxl/jpegxldrivercore.h @@ -33,8 +33,12 @@ constexpr const char *DRIVER_NAME = "JPEGXL"; -bool CPL_DLL IsJPEGXLContainer(GDALOpenInfo *poOpenInfo); +#define IsJPEGXLContainer PLUGIN_SYMBOL_NAME(IsJPEGXLContainer) +#define JPEGXLDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(JPEGXLDriverSetCommonMetadata) -void CPL_DLL JPEGXLDriverSetCommonMetadata(GDALDriver *poDriver); +bool IsJPEGXLContainer(GDALOpenInfo *poOpenInfo); + +void JPEGXLDriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/jpipkak/CMakeLists.txt b/frmts/jpipkak/CMakeLists.txt index db0257ebb08d..1dbeab2befa3 100644 --- a/frmts/jpipkak/CMakeLists.txt +++ b/frmts/jpipkak/CMakeLists.txt @@ -3,7 +3,8 @@ add_gdal_driver(TARGET gdal_JPIPKAK jpipkakdataset.h jpipkakdataset.cpp CORE_SOURCES jpipkakdrivercore.cpp - PLUGIN_CAPABLE) + PLUGIN_CAPABLE + NO_SHARED_SYMBOL_WITH_CORE) if(NOT TARGET gdal_JPIPKAK) return() diff --git a/frmts/jpipkak/jpipkakdrivercore.h b/frmts/jpipkak/jpipkakdrivercore.h index 6e4cebe16214..4e8d71ae3e16 100644 --- a/frmts/jpipkak/jpipkakdrivercore.h +++ b/frmts/jpipkak/jpipkakdrivercore.h @@ -33,6 +33,9 @@ constexpr const char *DRIVER_NAME = "JPIPKAK"; -void CPL_DLL JPIPKAKDriverSetCommonMetadata(GDALDriver *poDriver); +#define JPIPKAKDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(JPIPKAKDriverSetCommonMetadata) + +void JPIPKAKDriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/kea/CMakeLists.txt b/frmts/kea/CMakeLists.txt index cee8da2f673d..7570588a15d6 100644 --- a/frmts/kea/CMakeLists.txt +++ b/frmts/kea/CMakeLists.txt @@ -16,7 +16,8 @@ add_gdal_driver( libkea_headers.h CORE_SOURCES keadrivercore.cpp - PLUGIN_CAPABLE) + PLUGIN_CAPABLE + NO_SHARED_SYMBOL_WITH_CORE) if(TARGET gdal_KEA_core) target_include_directories(gdal_KEA_core PRIVATE ${HDF5_INCLUDE_DIRS}) diff --git a/frmts/kea/keaband.cpp b/frmts/kea/keaband.cpp index 905ec37d03c3..5b452c74a401 100644 --- a/frmts/kea/keaband.cpp +++ b/frmts/kea/keaband.cpp @@ -399,7 +399,7 @@ void KEARasterBand::SetDescription(const char *pszDescription) try { this->m_pImageIO->setImageBandDescription(this->nBand, pszDescription); - GDALPamRasterBand::SetDescription(pszDescription); + GDALRasterBand::SetDescription(pszDescription); } catch (const kealib::KEAIOException &) { @@ -721,9 +721,9 @@ CPLErr KEARasterBand::GetDefaultHistogram(double *pdfMin, double *pdfMax, { if (bForce) { - return GDALPamRasterBand::GetDefaultHistogram(pdfMin, pdfMax, pnBuckets, - ppanHistogram, bForce, fn, - pProgressData); + return GDALRasterBand::GetDefaultHistogram(pdfMin, pdfMax, pnBuckets, + ppanHistogram, bForce, fn, + pProgressData); } else { @@ -1379,7 +1379,7 @@ GDALRasterBand *KEARasterBand::GetMaskBand() { // use the base class implementation - GDAL will delete // fprintf( stderr, "returning base GetMaskBand()\n" ); - m_pMaskBand = GDALPamRasterBand::GetMaskBand(); + m_pMaskBand = GDALRasterBand::GetMaskBand(); } } catch (const kealib::KEAException &) @@ -1399,7 +1399,7 @@ int KEARasterBand::GetMaskFlags() // need to return the base class one since we are using // the base class implementation of GetMaskBand() // fprintf( stderr, "returning base GetMaskFlags()\n" ); - return GDALPamRasterBand::GetMaskFlags(); + return GDALRasterBand::GetMaskFlags(); } } catch (const kealib::KEAException &) diff --git a/frmts/kea/keaband.h b/frmts/kea/keaband.h index 9ae18b5b30d7..882b5e528a71 100644 --- a/frmts/kea/keaband.h +++ b/frmts/kea/keaband.h @@ -31,14 +31,14 @@ #ifndef KEABAND_H #define KEABAND_H -#include "gdal_pam.h" +#include "gdal_priv.h" #include "keadataset.h" class KEAOverview; class KEAMaskBand; // Provides the implementation of a GDAL raster band -class KEARasterBand CPL_NON_FINAL : public GDALPamRasterBand +class KEARasterBand CPL_NON_FINAL : public GDALRasterBand { private: LockedRefCount *m_pRefCount = nullptr; // reference count of m_pImageIO diff --git a/frmts/kea/keadataset.h b/frmts/kea/keadataset.h index 63505b1826e5..5f56ba5a4691 100644 --- a/frmts/kea/keadataset.h +++ b/frmts/kea/keadataset.h @@ -31,14 +31,14 @@ #ifndef KEADATASET_H #define KEADATASET_H -#include "gdal_pam.h" +#include "gdal_priv.h" #include "cpl_multiproc.h" #include "libkea_headers.h" class LockedRefCount; // class that implements a GDAL dataset -class KEADataset final : public GDALPamDataset +class KEADataset final : public GDALDataset { static H5::H5File *CreateLL(const char *pszFilename, int nXSize, int nYSize, int nBands, GDALDataType eType, diff --git a/frmts/kea/keadrivercore.h b/frmts/kea/keadrivercore.h index 9db81e75c8df..fc208ff30eb2 100644 --- a/frmts/kea/keadrivercore.h +++ b/frmts/kea/keadrivercore.h @@ -31,8 +31,12 @@ constexpr const char *DRIVER_NAME = "KEA"; -int CPL_DLL KEADriverIdentify(GDALOpenInfo *poOpenInfo); +#define KEADriverIdentify PLUGIN_SYMBOL_NAME(KEADriverIdentify) +#define KEADriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(KEADriverSetCommonMetadata) -void CPL_DLL KEADriverSetCommonMetadata(GDALDriver *poDriver); +int KEADriverIdentify(GDALOpenInfo *poOpenInfo); + +void KEADriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/mrf/CMakeLists.txt b/frmts/mrf/CMakeLists.txt index 8a0f76f562ab..596381e70659 100644 --- a/frmts/mrf/CMakeLists.txt +++ b/frmts/mrf/CMakeLists.txt @@ -14,6 +14,7 @@ add_gdal_driver( mrfdrivercore.cpp PLUGIN_CAPABLE_IF "NOT GDAL_USE_JPEG_INTERNAL\\\;NOT GDAL_USE_JPEG12_INTERNAL\\\;NOT GDAL_USE_LERC_INTERNAL\\\;NOT GDAL_USE_PNG_INTERNAL\\\;NOT GDAL_USE_TIFF_INTERNAL\\\;NOT GDAL_USE_ZLIB_INTERNAL" + NO_SHARED_SYMBOL_WITH_CORE ) if(TARGET gdal_MRF_core) diff --git a/frmts/mrf/mrfdrivercore.h b/frmts/mrf/mrfdrivercore.h index 87035d7ec207..173cc8c9a3d8 100644 --- a/frmts/mrf/mrfdrivercore.h +++ b/frmts/mrf/mrfdrivercore.h @@ -54,8 +54,12 @@ constexpr const char *DRIVER_NAME = "MRF"; -int CPL_DLL MRFDriverIdentify(GDALOpenInfo *poOpenInfo); +#define MRFDriverIdentify PLUGIN_SYMBOL_NAME(MRFDriverIdentify) +#define MRFDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(MRFDriverSetCommonMetadata) -void CPL_DLL MRFDriverSetCommonMetadata(GDALDriver *poDriver); +int MRFDriverIdentify(GDALOpenInfo *poOpenInfo); + +void MRFDriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/mrsid/CMakeLists.txt b/frmts/mrsid/CMakeLists.txt index 05f17a8097f1..c91ab9d269ed 100644 --- a/frmts/mrsid/CMakeLists.txt +++ b/frmts/mrsid/CMakeLists.txt @@ -3,7 +3,8 @@ option(GDAL_ENABLE_DRIVER_JP2MRSID "Whether to enable JPEG2000 support with MrSI add_gdal_driver(TARGET gdal_MrSID SOURCES mrsidstream.h mrsidstream.cpp mrsiddataset.cpp CORE_SOURCES mrsiddrivercore.cpp - PLUGIN_CAPABLE_IF "NOT GDAL_USE_GEOTIFF_INTERNAL OR NOT GDAL_HIDE_INTERNAL_SYMBOLS") + PLUGIN_CAPABLE_IF "NOT GDAL_USE_GEOTIFF_INTERNAL OR NOT GDAL_HIDE_INTERNAL_SYMBOLS" + NO_SHARED_SYMBOL_WITH_CORE) if(TARGET gdal_MrSID_core) target_include_directories(gdal_MrSID_core PRIVATE $) diff --git a/frmts/mrsid/mrsiddrivercore.h b/frmts/mrsid/mrsiddrivercore.h index 6262c98bd93d..10b5e7e8dad6 100644 --- a/frmts/mrsid/mrsiddrivercore.h +++ b/frmts/mrsid/mrsiddrivercore.h @@ -35,12 +35,19 @@ constexpr const char *MRSID_DRIVER_NAME = "MrSID"; constexpr const char *JP2MRSID_DRIVER_NAME = "JP2MrSID"; -int CPL_DLL MrSIDIdentify(GDALOpenInfo *poOpenInfo); +#define MrSIDIdentify PLUGIN_SYMBOL_NAME(MrSIDIdentify) +#define MrSIDJP2Identify PLUGIN_SYMBOL_NAME(MrSIDJP2Identify) +#define MrSIDDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(MrSIDDriverSetCommonMetadata) +#define JP2MrSIDDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(JP2MrSIDDriverSetCommonMetadata) -int CPL_DLL MrSIDJP2Identify(GDALOpenInfo *poOpenInfo); +int MrSIDIdentify(GDALOpenInfo *poOpenInfo); -void CPL_DLL MrSIDDriverSetCommonMetadata(GDALDriver *poDriver); +int MrSIDJP2Identify(GDALOpenInfo *poOpenInfo); -void CPL_DLL JP2MrSIDDriverSetCommonMetadata(GDALDriver *poDriver); +void MrSIDDriverSetCommonMetadata(GDALDriver *poDriver); + +void JP2MrSIDDriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/msg/CMakeLists.txt b/frmts/msg/CMakeLists.txt index 2ef691eae9ed..414a3ad4ae5f 100644 --- a/frmts/msg/CMakeLists.txt +++ b/frmts/msg/CMakeLists.txt @@ -14,7 +14,8 @@ add_gdal_driver( xritheaderparser.h CORE_SOURCES msgdrivercore.cpp - PLUGIN_CAPABLE) + PLUGIN_CAPABLE + NO_SHARED_SYMBOL_WITH_CORE) if(NOT TARGET gdal_MSG) return() diff --git a/frmts/msg/msgdrivercore.h b/frmts/msg/msgdrivercore.h index 3a2f09aac1de..f4e1ab1cca2f 100644 --- a/frmts/msg/msgdrivercore.h +++ b/frmts/msg/msgdrivercore.h @@ -34,6 +34,9 @@ constexpr const char *DRIVER_NAME = "MSG"; -void CPL_DLL MSGDriverSetCommonMetadata(GDALDriver *poDriver); +#define MSGDriverSetCommonMetadata \ + PLUGIN_SYMBOL_NAME(MSGDriverSetCommonMetadata) + +void MSGDriverSetCommonMetadata(GDALDriver *poDriver); #endif diff --git a/frmts/netcdf/CMakeLists.txt b/frmts/netcdf/CMakeLists.txt index 892f34a81c75..506d905fd860 100644 --- a/frmts/netcdf/CMakeLists.txt +++ b/frmts/netcdf/CMakeLists.txt @@ -12,7 +12,8 @@ set(_SOURCES add_gdal_driver(TARGET gdal_netCDF SOURCES ${_SOURCES} CORE_SOURCES netcdfdrivercore.cpp - PLUGIN_CAPABLE) + PLUGIN_CAPABLE + NO_SHARED_SYMBOL_WITH_CORE) unset(_SOURCES) function(declare_def DEF) @@ -21,6 +22,11 @@ function(declare_def DEF) endif() if(TARGET gdal_netCDF_core) target_compile_definitions(gdal_netCDF_core PRIVATE ${DEF}) + if(NetCDF_TARGET AND TARGET ${NetCDF_TARGET}) + target_include_directories(gdal_netCDF_core PRIVATE $) + target_compile_definitions(gdal_netCDF_core PRIVATE $) + target_compile_definitions(gdal_netCDF_core PRIVATE HAS_NETCDF_H) + endif() endif() endfunction() @@ -52,4 +58,4 @@ endif() gdal_standard_includes(gdal_netCDF) gdal_target_link_libraries(gdal_netCDF PRIVATE ${NetCDF_TARGET}) - +target_compile_definitions(gdal_netCDF PRIVATE HAS_NETCDF_H) diff --git a/frmts/netcdf/netcdfdataset.cpp b/frmts/netcdf/netcdfdataset.cpp index 69e030de649c..2acb18e78bd0 100644 --- a/frmts/netcdf/netcdfdataset.cpp +++ b/frmts/netcdf/netcdfdataset.cpp @@ -70,6 +70,7 @@ #include "cpl_time.h" #include "gdal.h" #include "gdal_frmts.h" +#include "gdal_priv_templates.hpp" #include "ogr_core.h" #include "ogr_srs_api.h" @@ -759,22 +760,12 @@ netCDFRasterBand::netCDFRasterBand(const netCDFRasterBand::CONSTRUCTOR_OPEN &, #ifdef NCDF_DEBUG CPLDebug("GDAL_netCDF", "SetNoDataValue(%f) read", dfNoData); #endif - if (eDataType == GDT_Int64 && - dfNoData >= - static_cast(std::numeric_limits::min()) && - dfNoData <= - static_cast(std::numeric_limits::max()) && - dfNoData == static_cast(static_cast(dfNoData))) + if (eDataType == GDT_Int64 && GDALIsValueExactAs(dfNoData)) { SetNoDataValueNoUpdate(static_cast(dfNoData)); } else if (eDataType == GDT_UInt64 && - dfNoData >= static_cast( - std::numeric_limits::min()) && - dfNoData <= static_cast( - std::numeric_limits::max()) && - dfNoData == - static_cast(static_cast(dfNoData))) + GDALIsValueExactAs(dfNoData)) { SetNoDataValueNoUpdate(static_cast(dfNoData)); } @@ -3901,6 +3892,56 @@ void netCDFDataset::SetProjectionFromVar( double xMinMax[2] = {0.0, 0.0}; double yMinMax[2] = {0.0, 0.0}; + const auto RoundMinMaxForFloatVals = + [](double &dfMin, double &dfMax, int nIntervals) + { + // Helps for a case where longitudes range from + // -179.99 to 180.0 with a 0.01 degree spacing. + // However as this is encoded in a float array, + // -179.99 is actually read as -179.99000549316406 as + // a double. Try to detect that and correct the rounding + + const auto IsAlmostInteger = [](double dfVal) + { + constexpr double THRESHOLD_INTEGER = 1e-3; + return std::fabs(dfVal - std::round(dfVal)) <= + THRESHOLD_INTEGER; + }; + + const double dfSpacing = (dfMax - dfMin) / nIntervals; + if (dfSpacing > 0) + { + const double dfInvSpacing = 1.0 / dfSpacing; + if (IsAlmostInteger(dfInvSpacing)) + { + const double dfRoundedSpacing = + 1.0 / std::round(dfInvSpacing); + const double dfMinDivRoundedSpacing = + dfMin / dfRoundedSpacing; + const double dfMaxDivRoundedSpacing = + dfMax / dfRoundedSpacing; + if (IsAlmostInteger(dfMinDivRoundedSpacing) && + IsAlmostInteger(dfMaxDivRoundedSpacing)) + { + const double dfRoundedMin = + std::round(dfMinDivRoundedSpacing) * + dfRoundedSpacing; + const double dfRoundedMax = + std::round(dfMaxDivRoundedSpacing) * + dfRoundedSpacing; + if (static_cast(dfMin) == + static_cast(dfRoundedMin) && + static_cast(dfMax) == + static_cast(dfRoundedMax)) + { + dfMin = dfRoundedMin; + dfMax = dfRoundedMax; + } + } + } + } + }; + if (!nc_get_att_double(nGroupDimXID, nVarDimXID, "actual_range", adfActualRange)) { @@ -3920,6 +3961,12 @@ void netCDFDataset::SetProjectionFromVar( xMinMax[0] = pdfXCoord[0]; xMinMax[1] = pdfXCoord[xdim - 1]; node_offset = 0; + + if (nc_var_dimx_datatype == NC_FLOAT) + { + RoundMinMaxForFloatVals(xMinMax[0], xMinMax[1], + poDS->nRasterXSize - 1); + } } if (!nc_get_att_double(nGroupDimYID, nVarDimYID, "actual_range", @@ -3941,6 +3988,12 @@ void netCDFDataset::SetProjectionFromVar( yMinMax[0] = pdfYCoord[0]; yMinMax[1] = pdfYCoord[ydim - 1]; node_offset = 0; + + if (nc_var_dimy_datatype == NC_FLOAT) + { + RoundMinMaxForFloatVals(yMinMax[0], yMinMax[1], + poDS->nRasterYSize - 1); + } } double dfCoordOffset = 0.0; @@ -8310,13 +8363,12 @@ GDALDataset *netCDFDataset::Open(GDALOpenInfo *poOpenInfo) bool bHasSimpleGeometries = false; // but not necessarily valid if (poDS->nCFVersion >= 1.8) { - poDS->bSGSupport = true; bHasSimpleGeometries = poDS->DetectAndFillSGLayers(cdfid); - poDS->vcdf.enableFullVirtualMode(); - } - else - { - poDS->bSGSupport = false; + if (bHasSimpleGeometries) + { + poDS->bSGSupport = true; + poDS->vcdf.enableFullVirtualMode(); + } } char szConventions[NC_MAX_NAME + 1]; @@ -9285,9 +9337,12 @@ GDALDataset *netCDFDataset::Create(const char *pszFilename, int nXSize, // Add Conventions, GDAL info and history. if (poDS->cdfid >= 0) { - const char *CF_Vector_Conv = poDS->bSGSupport - ? NCDF_CONVENTIONS_CF_V1_8 - : NCDF_CONVENTIONS_CF_V1_6; + const char *CF_Vector_Conv = + poDS->bSGSupport || + // Use of variable length strings require CF-1.8 + EQUAL(aosOptions.FetchNameValueDef("FORMAT", ""), "NC4") + ? NCDF_CONVENTIONS_CF_V1_8 + : NCDF_CONVENTIONS_CF_V1_6; poDS->bWriteGDALVersion = CPLTestBool( CSLFetchNameValueDef(papszOptions, "WRITE_GDAL_VERSION", "YES")); poDS->bWriteGDALHistory = CPLTestBool( diff --git a/frmts/netcdf/netcdfdrivercore.cpp b/frmts/netcdf/netcdfdrivercore.cpp index 9715a08e5160..bb339c6ad85b 100644 --- a/frmts/netcdf/netcdfdrivercore.cpp +++ b/frmts/netcdf/netcdfdrivercore.cpp @@ -35,6 +35,19 @@ #include #include +#ifdef HAS_NETCDF_H +#include "netcdfdataset.h" +#ifdef NETCDF_HAS_NC2 +#define NETCDF_CORE_HAS_NC2 1 +#endif +#else +// We don't have an easy way to guess that without accessing netcdf.h, so +// assume it is present +#ifndef NETCDF_CORE_HAS_NC2 +#define NETCDF_CORE_HAS_NC2 1 +#endif +#endif + /************************************************************************/ /* netCDFIdentifyFormat() */ /************************************************************************/ @@ -377,7 +390,7 @@ void netCDFDriverSetCommonMetadata(GDALDriver *poDriver) "" "