diff --git a/.flake8 b/.flake8
index ecea7b82a0b..0a48712c808 100644
--- a/.flake8
+++ b/.flake8
@@ -24,31 +24,10 @@ per-file-ignores =
imagery/i.atcorr/create_iwave.py: F632, F821, W293
doc/python/m.distance.py: E501
doc/gui/wxpython/example/dialogs.py: F401
- locale/grass_po_stats.py: E122, E128, E231, E401, E722
gui/scripts/d.wms.py: E501
- gui/wxpython/core/gconsole.py: E722
- gui/wxpython/core/render.py: E722
- gui/wxpython/core/settings.py: E722
- gui/wxpython/core/toolboxes.py: E722
- gui/wxpython/core/utils.py: E722
- gui/wxpython/core/workspace.py: E722
- gui/wxpython/datacatalog/tree.py: E731, E402
- gui/wxpython/dbmgr/base.py: E722
- gui/wxpython/dbmgr/dialogs.py: E722
- gui/wxpython/dbmgr/sqlbuilder.py: E722
- gui/wxpython/dbmgr/manager.py: E722
- gui/wxpython/docs/wxgui_sphinx/conf.py: E402, W291
- gui/wxpython/gcp/manager.py: E722
- gui/wxpython/gui_core/*: E266, E722
- gui/wxpython/gui_core/dialogs.py: E722
- gui/wxpython/gui_core/forms.py: E722
- gui/wxpython/gui_core/ghelp.py: E722
- gui/wxpython/gui_core/gselect.py: E266, E722
- gui/wxpython/gui_core/preferences.py: E266
- gui/wxpython/gui_core/widgets.py: E722, E266
- gui/wxpython/image2target/*: F841, E722, E265
- gui/wxpython/image2target/g.gui.image2target.py: E501, E265, F841
- gui/wxpython/iscatt/*: F841, E722, F405, F403
+ gui/wxpython/image2target/*: F841, E722
+ gui/wxpython/image2target/g.gui.image2target.py: E501, F841
+ gui/wxpython/iscatt/*: F841
gui/wxpython/lmgr/frame.py: F841, E722
# layertree still includes some formatting issues (it is ignored by Black)
gui/wxpython/lmgr/layertree.py: E722, E266, W504, E225
diff --git a/.github/workflows/additional_checks.yml b/.github/workflows/additional_checks.yml
index d25cbb49194..1f8e355b487 100644
--- a/.github/workflows/additional_checks.yml
+++ b/.github/workflows/additional_checks.yml
@@ -24,7 +24,7 @@ jobs:
steps:
- name: Checkout repository contents
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
with:
fetch-depth: 31
diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml
index 12b0b070ec9..f68483dea20 100644
--- a/.github/workflows/clang-format-check.yml
+++ b/.github/workflows/clang-format-check.yml
@@ -16,7 +16,7 @@ jobs:
name: Formatting Check
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
with:
persist-credentials: false
- uses: DoozyX/clang-format-lint-action@c71d0bf4e21876ebec3e5647491186f8797fde31 # v0.18.2
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index b4fb254d11d..acafe77daea 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -40,7 +40,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Set up Python
uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
with:
@@ -52,11 +52,11 @@ jobs:
sudo apt-get install -y wget git gawk findutils
xargs -a <(awk '! /^ *(#|$)/' ".github/workflows/apt.txt") -r -- \
sudo apt-get install -y --no-install-recommends --no-install-suggests
- - uses: rui314/setup-mold@0bf4f07ef9048ec62a45f9dbf2f098afa49695f0 # v1
+ - uses: rui314/setup-mold@c49f92ee787f4e2bba3330d01755ef8c7221699a # v1
if: ${{ matrix.language == 'c-cpp' }}
- name: Initialize CodeQL
- uses: github/codeql-action/init@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8
+ uses: github/codeql-action/init@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10
with:
languages: ${{ matrix.language }}
config-file: ./.github/codeql/codeql-config.yml
@@ -81,6 +81,6 @@ jobs:
run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install"
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8
+ uses: github/codeql-action/analyze@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10
with:
category: "/language:${{matrix.language}}"
diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml
index 9f1fc5d843c..24c98c96601 100644
--- a/.github/workflows/coverity.yml
+++ b/.github/workflows/coverity.yml
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-22.04
if: github.repository == 'OSGeo/grass'
steps:
- - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Get dependencies
run: |
diff --git a/.github/workflows/create_release_draft.yml b/.github/workflows/create_release_draft.yml
index 09da8ad1317..4751c3c43a0 100644
--- a/.github/workflows/create_release_draft.yml
+++ b/.github/workflows/create_release_draft.yml
@@ -30,7 +30,7 @@ jobs:
contents: write
steps:
- name: Checks-out repository
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
with:
ref: ${{ github.ref }}
fetch-depth: 0
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 1f60ed4c6d7..4056ed63ea4 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -49,7 +49,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
with:
fetch-depth: 0
- name: Docker meta
@@ -68,7 +68,7 @@ jobs:
- name: Set up QEMU
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1
+ uses: docker/setup-buildx-action@8026d2bc3645ea78b0d2544766a1225eb5691f89 # v3.7.0
- name: Login to DockerHub
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
with:
@@ -76,7 +76,7 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
id: docker_build
- uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0
+ uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0
with:
push: true
pull: true
diff --git a/.github/workflows/gcc.yml b/.github/workflows/gcc.yml
index e3e23f43c04..24b9dcb4043 100644
--- a/.github/workflows/gcc.yml
+++ b/.github/workflows/gcc.yml
@@ -26,7 +26,7 @@ jobs:
fail-fast: false
steps:
- - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Get dependencies
run: |
sudo apt-get update -y
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index 4fad9d315a7..e1166cdacc2 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -44,13 +44,13 @@ jobs:
-mindepth 1 -maxdepth 1 -type f -print -delete
# Rehash to forget about the deleted files
hash -r
- - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Get current date cache key segment
id: date
# Year and week of year so cache key changes weekly
run: echo "date=$(date +%Y-%U)" >> "${GITHUB_OUTPUT}"
- name: Setup Mamba
- uses: mamba-org/setup-micromamba@f8b8a1e23a26f60a44c853292711bacfd3eac822 # v1.9.0
+ uses: mamba-org/setup-micromamba@59b11321ffd9186cd5165633a02c5bba47de6d13 # v1.10.0
with:
init-shell: bash
environment-file: .github/workflows/macos_dependencies.txt
diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml
index 79a44937755..d9c67cc37c9 100644
--- a/.github/workflows/osgeo4w.yml
+++ b/.github/workflows/osgeo4w.yml
@@ -31,7 +31,7 @@ jobs:
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- uses: msys2/setup-msys2@ddf331adaebd714795f1042345e6ca57bd66cea8 # v2.24.1
with:
path-type: inherit
@@ -66,9 +66,13 @@ jobs:
pdal-devel
pdcurses
proj-devel
+ python3-core
+ python3-jupyter
python3-matplotlib
python3-numpy
+ python3-pip
python3-ply
+ python3-pytest
python3-pywin32
python3-wxpython
regex-devel
@@ -98,6 +102,19 @@ jobs:
shell: msys2 {0}
run: .github/workflows/test_simple.sh
+ - name: Install pytest plugins
+ run: python -m pip install pytest-timeout
+ shell: cmd /D /E:ON /V:OFF /S /C "CALL C:/OSGeo4W/OSGeo4W.bat "{0}""
+ - name: Run pytest with a single worker
+ run: |
+ call %OSGEO4W_ROOT%\opt\grass\etc\env.bat
+ set PYTHONPATH=%GISBASE%\etc\python;%PYTHONPATH%
+ path %GISBASE%\lib;%GISBASE%\bin;%PATH%
+ pytest --verbose --color=yes ^
+ --durations=0 --durations-min=0.5 ^
+ -ra .
+ shell: cmd /D /E:ON /V:OFF /S /C "CALL C:/OSGeo4W/OSGeo4W.bat "{0}""
+
- name: Run tests
run: .github/workflows/test_thorough.bat 'C:\OSGeo4W\opt\grass\grass85.bat' 'C:\OSGeo4W\bin\python3'
diff --git a/.github/workflows/osgeo4w_gunittest.cfg b/.github/workflows/osgeo4w_gunittest.cfg
new file mode 100644
index 00000000000..b9252cf49ef
--- /dev/null
+++ b/.github/workflows/osgeo4w_gunittest.cfg
@@ -0,0 +1,32 @@
+[gunittest]
+
+# Files (or wildcard patterns) to exclude from testing separated by newline or
+# space. This would be ideally empty or it would include just special cases,
+# but it includes mainly tests which can (and should) be fixed.
+exclude =
+ gui/wxpython/core/testsuite/toolboxes.sh
+ lib/init/testsuite/test_grass_tmp_mapset.py
+ python/grass/gunittest/testsuite/test_gunitest_doctests.py
+ python/grass/pygrass/raster/testsuite/test_pygrass_raster_doctests.py
+ python/grass/pygrass/rpc/testsuite/test_pygrass_rpc_doctests.py
+ python/grass/script/testsuite/test_script_doctests.py
+ python/grass/temporal/testsuite/unittests_temporal_raster_conditionals_complement_else.py
+ temporal/t.info/testsuite/test.t.info.sh
+ temporal/t.rast.accdetect/testsuite/test.t.rast.accdetect.reverse.sh
+ temporal/t.rast.accdetect/testsuite/test.t.rast.accdetect.sh
+ temporal/t.rast.aggregate/testsuite/test_aggregation_relative.py
+ vector/v.edit/testsuite/select_all_flag.sh
+ vector/v.in.lidar/testsuite/decimation_test.py
+ vector/v.in.lidar/testsuite/mask_test.py
+ vector/v.in.lidar/testsuite/test_v_in_lidar_basic.py
+ vector/v.in.lidar/testsuite/test_v_in_lidar_filter.py
+ vector/v.what.rast3/testsuite/test.v.what.rast3.sh
+ vector/v.what/testsuite/test_vwhat_layers.py
+ vector/v.what/testsuite/test_vwhat_ncspm.py
+
+# Maximum time for execution of one test file (not a test function)
+# after which test is terminated (which may not terminate child processes
+# from that test).
+# Using 5 minutes as maximum (average test time should be much shorter,
+# couple seconds per file, median should be around one second).
+timeout = 300
diff --git a/.github/workflows/periodic_update.yml b/.github/workflows/periodic_update.yml
index bea3b53af4a..f5441297250 100644
--- a/.github/workflows/periodic_update.yml
+++ b/.github/workflows/periodic_update.yml
@@ -21,7 +21,7 @@ jobs:
- name: Create URL to the run output
id: vars
run: echo "run-url=https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" >> $GITHUB_OUTPUT
- - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: "Check that autoconf scripts are up-to-date:"
run: |
rm -f config.guess config.sub
diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml
index 8e3b2286b1c..cb724086ea0 100644
--- a/.github/workflows/pytest.yml
+++ b/.github/workflows/pytest.yml
@@ -32,7 +32,7 @@ jobs:
PYTHONWARNINGS: always
steps:
- - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Set up Python
uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
@@ -48,7 +48,7 @@ jobs:
xargs -a <(awk '! /^ *(#|$)/' ".github/workflows/apt.txt") -r -- \
sudo apt-get install -y --no-install-recommends --no-install-suggests
- - uses: rui314/setup-mold@0bf4f07ef9048ec62a45f9dbf2f098afa49695f0 # v1
+ - uses: rui314/setup-mold@c49f92ee787f4e2bba3330d01755ef8c7221699a # v1
- name: Install Python dependencies
run: |
@@ -108,7 +108,7 @@ jobs:
coverage html
- name: Upload coverage reports to Codecov
- uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0
+ uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
with:
verbose: true
flags: pytest-python-${{ matrix.python-version }}
diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml
index 92ef7d788b2..92c647875b0 100644
--- a/.github/workflows/python-code-quality.yml
+++ b/.github/workflows/python-code-quality.yml
@@ -34,9 +34,9 @@ jobs:
# renovate: datasource=pypi depName=pylint
PYLINT_VERSION: "2.12.2"
# renovate: datasource=pypi depName=bandit
- BANDIT_VERSION: "1.7.9"
+ BANDIT_VERSION: "1.7.10"
# renovate: datasource=pypi depName=ruff
- RUFF_VERSION: "0.6.7"
+ RUFF_VERSION: "0.6.8"
runs-on: ${{ matrix.os }}
permissions:
@@ -54,7 +54,7 @@ jobs:
echo Bandit: ${{ env.BANDIT_VERSION }}
echo Ruff: ${{ env.RUFF_VERSION }}
- - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Set up Python
uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
@@ -135,7 +135,7 @@ jobs:
path: bandit.sarif
- name: Upload SARIF File into Security Tab
- uses: github/codeql-action/upload-sarif@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8
+ uses: github/codeql-action/upload-sarif@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10
with:
sarif_file: bandit.sarif
@@ -147,7 +147,7 @@ jobs:
run: |
echo "MAKEFLAGS=-j$(nproc)" >> $GITHUB_ENV
- - uses: rui314/setup-mold@0bf4f07ef9048ec62a45f9dbf2f098afa49695f0 # v1
+ - uses: rui314/setup-mold@c49f92ee787f4e2bba3330d01755ef8c7221699a # v1
- name: Build
run: .github/workflows/build_${{ matrix.os }}.sh $HOME/install
diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml
index 44da8d9acb7..117f5cd43fb 100644
--- a/.github/workflows/super-linter.yml
+++ b/.github/workflows/super-linter.yml
@@ -25,7 +25,7 @@ jobs:
statuses: write
steps:
- - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
with:
# super-linter needs the full git history to get the
# list of files that changed across commits
diff --git a/.github/workflows/test-nix.yml b/.github/workflows/test-nix.yml
index 22fa4f0061f..03f767730a9 100644
--- a/.github/workflows/test-nix.yml
+++ b/.github/workflows/test-nix.yml
@@ -28,7 +28,7 @@ jobs:
contents: read
steps:
- - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Install nix
uses: DeterminateSystems/nix-installer-action@da36cb69b1c3247ad7a1f931ebfd954a1105ef14 # v14
diff --git a/.github/workflows/test_thorough.bat b/.github/workflows/test_thorough.bat
index 40df9534a20..843adb4c4f0 100644
--- a/.github/workflows/test_thorough.bat
+++ b/.github/workflows/test_thorough.bat
@@ -2,4 +2,4 @@ set grass=%1
set python=%2
call %grass% --tmp-project XY --exec g.download.project url=https://grass.osgeo.org/sampledata/north_carolina/nc_spm_full_v2alpha2.tar.gz path=%USERPROFILE%
-call %grass% --tmp-project XY --exec %python% -m grass.gunittest.main --grassdata %USERPROFILE% --location nc_spm_full_v2alpha2 --location-type nc --min-success 86
+call %grass% --tmp-project XY --exec %python% -m grass.gunittest.main --grassdata %USERPROFILE% --location nc_spm_full_v2alpha2 --location-type nc --min-success 96 --config .github\workflows\osgeo4w_gunittest.cfg
diff --git a/.github/workflows/titles.yml b/.github/workflows/titles.yml
index b8e33c2fb37..f90e7275a2e 100644
--- a/.github/workflows/titles.yml
+++ b/.github/workflows/titles.yml
@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout base repository (doesn't include the PR changes)
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Call PR title validation function
run: python utils/generate_release_notes.py check "${PR_TITLE}" "" ""
env:
diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml
index 57b0821eec4..25ce2afdd67 100644
--- a/.github/workflows/ubuntu.yml
+++ b/.github/workflows/ubuntu.yml
@@ -59,7 +59,7 @@ jobs:
fail-fast: false
steps:
- - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Invert inclusion list to an exclusion list
id: get-exclude
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index e1f98c7a2a5..b7a517fba37 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -37,13 +37,13 @@ repos:
)
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
- rev: v0.6.7
+ rev: v0.6.8
hooks:
# Run the linter.
- id: ruff
args: [--fix, --preview]
- repo: https://github.com/igorshubovych/markdownlint-cli
- rev: v0.41.0
+ rev: v0.42.0
hooks:
- id: markdownlint-fix
# Using this mirror lets us use mypyc-compiled black, which is about 2x faster
diff --git a/Dockerfile b/Dockerfile
index c6366eeb873..41fb3264c07 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -19,8 +19,9 @@ WORKDIR /tmp
ARG GUI
# Todo: re-consider required dev packages for addons (~400MB in dev packages)
-ARG GRASS_RUN_PACKAGES="build-essential \
+ARG GRASS_RUN_PACKAGES="\
bison \
+ build-essential \
bzip2 \
curl \
flex \
@@ -28,8 +29,6 @@ ARG GRASS_RUN_PACKAGES="build-essential \
gcc \
gdal-bin \
geos-bin \
- proj-bin \
- netcdf-bin \
git \
language-pack-en-base \
libcairo2 \
@@ -38,28 +37,29 @@ ARG GRASS_RUN_PACKAGES="build-essential \
libfftw3-dev \
libfreetype6 \
libgdal-dev \
+ libgeos-dev \
+ libgsl-dev \
libgsl27 \
libjpeg-turbo8 \
libjsoncpp-dev \
- libmagic1 \
+ liblapacke-dev \
libmagic-mgc \
+ libmagic1 \
libncurses5 \
- libopenblas-dev \
+ libomp-dev \
+ libomp5 \
libopenblas-base \
+ libopenblas-dev \
libopenjp2-7 \
- libomp5 \
- libomp-dev \
- libgeos-dev \
- libpdal-dev \
- libproj-dev \
- libpq-dev \
- libgsl-dev \
libpdal-base13 \
+ libpdal-dev \
libpdal-plugin-hdf \
libpdal-plugins \
libpdal-util13 \
libpnglite0 \
+ libpq-dev \
libpq5 \
+ libproj-dev \
libpython3-all-dev \
libreadline8 \
libsqlite3-0 \
@@ -70,7 +70,9 @@ ARG GRASS_RUN_PACKAGES="build-essential \
mesa-utils \
moreutils \
ncurses-bin \
+ netcdf-bin \
pdal \
+ proj-bin \
proj-data \
python-is-python3 \
python3 \
@@ -86,56 +88,58 @@ ARG GRASS_RUN_PACKAGES="build-essential \
ENV GRASS_RUN_PACKAGES=${GRASS_RUN_PACKAGES}
# Define build packages
-ARG GRASS_BUILD_PACKAGES="cmake \
+ARG GRASS_BUILD_PACKAGES="\
+ cmake \
libbz2-dev \
libcairo2-dev \
libfreetype6-dev \
- zlib1g-dev \
+ libjpeg-dev \
+ libncurses5-dev \
libnetcdf-dev \
libopenjp2-7-dev \
- libreadline-dev \
- libjpeg-dev \
libpnglite-dev \
+ libreadline-dev \
libsqlite3-dev \
libtiff-dev \
libzstd-dev \
- libncurses5-dev \
mesa-common-dev \
zlib1g-dev \
"
ENV GRASS_BUILD_PACKAGES=${GRASS_BUILD_PACKAGES}
-ARG GRASS_CONFIG="--with-cxx \
+ARG GRASS_CONFIG="\
--enable-largefile \
- --with-proj-share=/usr/share/proj \
- --with-gdal=/usr/bin/gdal-config \
- --with-geos \
- --with-sqlite \
+ --with-blas \
+ --with-bzlib \
--with-cairo --with-cairo-ldflags=-lfontconfig \
- --with-freetype --with-freetype-includes=/usr/include/freetype2/ \
+ --with-cxx \
--with-fftw \
- --with-postgres --with-postgres-includes=/usr/include/postgresql \
- --with-netcdf \
- --with-zstd \
- --with-bzlib \
- --with-pdal \
- --without-mysql \
- --with-blas \
+ --with-freetype --with-freetype-includes=/usr/include/freetype2/ \
+ --with-gdal=/usr/bin/gdal-config \
+ --with-geos \
--with-lapack \
- --with-readline \
+ --with-netcdf \
--with-odbc \
--with-openmp \
+ --with-pdal \
+ --with-postgres --with-postgres-includes=/usr/include/postgresql \
+ --with-proj-share=/usr/share/proj \
+ --with-readline \
+ --with-sqlite \
+ --with-zstd \
+ --without-mysql \
"
-ARG GRASS_PYTHON_PACKAGES="pip \
- setuptools \
- python-dateutil \
- python-magic \
- numpy \
+ARG GRASS_PYTHON_PACKAGES="\
Pillow \
- ply \
matplotlib \
+ numpy \
+ pip \
+ ply \
psycopg2 \
+ python-dateutil \
+ python-magic \
+ setuptools \
"
ENV GRASS_PYTHON_PACKAGES=${GRASS_PYTHON_PACKAGES}
@@ -147,31 +151,33 @@ ENV GRASS_CONFIG=${GRASS_CONFIG}
FROM common_start as grass_with_gui
-ARG GRASS_RUN_PACKAGES="${GRASS_RUN_PACKAGES} adwaita-icon-theme-full \
- libglu1-mesa \
- libgtk-3-0 \
- libnotify4 \
- libsdl2-2.0-0 \
- libxtst6 \
- librsvg2-common \
- gettext \
+ARG GRASS_RUN_PACKAGES="${GRASS_RUN_PACKAGES} \
+ adwaita-icon-theme-full \
freeglut3 \
+ gettext \
+ libglu1-mesa \
libgstreamer-plugins-base1.0 \
+ libgtk-3-0 \
libjpeg8 \
+ libnotify4 \
libpng16-16 \
+ librsvg2-common \
+ libsdl2-2.0-0 \
libsm6 \
libtiff5 \
libwebkit2gtk-4.0 \
+ libxtst6 \
"
# librsvg2-common \
# (fix error (wxgui.py:7782): Gtk-WARNING **: 19:53:09.774:
# Could not load a pixbuf from /org/gtk/libgtk/theme/Adwaita/assets/check-symbolic.svg.
# This may indicate that pixbuf loaders or the mime database could not be found.)
-ARG GRASS_BUILD_PACKAGES="${GRASS_BUILD_PACKAGES} adwaita-icon-theme-full \
+ARG GRASS_BUILD_PACKAGES="${GRASS_BUILD_PACKAGES} \
+ adwaita-icon-theme-full \
+ freeglut3-dev \
libgl1-mesa-dev \
libglu1-mesa-dev \
- freeglut3-dev \
libgstreamer-plugins-base1.0-dev \
libgtk-3-dev \
libjpeg-dev \
@@ -184,10 +190,11 @@ ARG GRASS_BUILD_PACKAGES="${GRASS_BUILD_PACKAGES} adwaita-icon-theme-full \
libxtst-dev \
"
-ARG GRASS_CONFIG="${GRASS_CONFIG} --with-opengl \
- --with-x \
+ARG GRASS_CONFIG="${GRASS_CONFIG} \
--with-nls \
+ --with-opengl \
--with-readline \
+ --with-x \
"
ARG GRASS_PYTHON_PACKAGES="${GRASS_PYTHON_PACKAGES} wxPython"
# If you do not use any Gnome Accessibility features, to suppress warning
diff --git a/display/d.mon/render_cmd.py b/display/d.mon/render_cmd.py
index 7678a1442e4..cf153c0b017 100644
--- a/display/d.mon/render_cmd.py
+++ b/display/d.mon/render_cmd.py
@@ -39,7 +39,7 @@ def remove_mapfile(mapfile):
# read environment variables from file
def read_env_file(env_file):
width = height = legfile = None
- fd = open(env_file, "r")
+ fd = open(env_file)
if fd is None:
grass.fatal("Unable to open file '{0}'".format(env_file))
lines = fd.readlines()
diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile
index a099617759c..b6dbebf6666 100644
--- a/docker/alpine/Dockerfile
+++ b/docker/alpine/Dockerfile
@@ -11,9 +11,9 @@ ARG PYTHON_VERSION=3
# List of packages to be installed (proj-data omitted: 570.04 MB)
ENV GRASS_RUN_PACKAGES="\
attr \
- build-base \
bash \
bison \
+ build-base \
bzip2 \
cairo \
curl \
@@ -29,15 +29,15 @@ ENV GRASS_RUN_PACKAGES="\
gdal-driver-JP2OpenJPEG \
gdal-driver-LIBKML \
gdal-driver-MSSQLSpatial \
- gdal-driver-netCDF \
gdal-driver-ODBC \
gdal-driver-PG \
gdal-driver-PNG \
gdal-driver-WMS \
+ gdal-driver-netCDF \
gdal-tools \
- gettext \
geos \
geos-dev \
+ gettext \
git \
gnutls \
jsoncpp \
@@ -52,15 +52,15 @@ ENV GRASS_RUN_PACKAGES="\
musl \
musl-utils \
ncurses \
- openjpeg \
openblas \
- py3-numpy \
- py3-pillow \
- python3 \
+ openjpeg \
pdal \
pdal-dev \
postgresql15-client \
proj-util \
+ py3-numpy \
+ py3-pillow \
+ python3 \
sqlite \
sqlite-libs \
subversion \
@@ -89,23 +89,23 @@ FROM common as build
# set configuration options, without wxGUI
ENV GRASS_CONFIG="\
--enable-largefile \
+ --with-bzlib \
+ --with-cairo --with-cairo-ldflags=-lfontconfig \
--with-cxx \
- --with-proj-share=/usr/share/proj \
+ --with-fftw \
--with-gdal \
- --with-pdal \
--with-geos \
+ --with-openmp \
+ --with-pdal \
+ --with-postgres --with-postgres-includes=/usr/include/postgresql \
+ --with-proj-share=/usr/share/proj \
--with-sqlite \
- --with-bzlib \
--with-zstd \
- --with-cairo --with-cairo-ldflags=-lfontconfig \
- --with-fftw \
- --with-postgres --with-postgres-includes=/usr/include/postgresql \
- --with-openmp \
--without-freetype \
- --without-opengl \
- --without-nls \
--without-mysql \
+ --without-nls \
--without-odbc \
+ --without-opengl \
"
# Set environmental variables for GRASS GIS compilation, without debug symbols
@@ -132,13 +132,13 @@ ENV GRASS_BUILD_PACKAGES="\
libjpeg-turbo-dev \
libpng-dev \
libpq-dev \
- openjpeg-dev \
openblas-dev \
+ openjpeg-dev \
pdal \
pdal-dev \
proj-dev \
- python3-dev \
py3-numpy-dev \
+ python3-dev \
sqlite-dev \
tar \
tiff-dev \
diff --git a/docker/debian/Dockerfile b/docker/debian/Dockerfile
index 2ed59e1482e..8021ccf409e 100644
--- a/docker/debian/Dockerfile
+++ b/docker/debian/Dockerfile
@@ -18,8 +18,8 @@ WORKDIR /tmp
RUN apt-get update && apt-get upgrade -y && \
apt-get install -y --no-install-recommends --no-install-suggests \
- build-essential \
bison \
+ build-essential \
bzip2 \
cmake \
curl \
@@ -40,8 +40,8 @@ RUN apt-get update && apt-get upgrade -y && \
libgsl0-dev \
libjpeg-dev \
libjsoncpp-dev \
- libnetcdf-dev \
libncurses-dev \
+ libnetcdf-dev \
libopenblas-dev \
libopenjp2-7 \
libopenjp2-7-dev \
@@ -153,24 +153,24 @@ ENV CXXFLAGS "$MYCXXFLAGS"
ENV NUMTHREADS=4
RUN make distclean || echo "nothing to clean"
RUN /src/grass_build/configure \
- --with-cxx \
--enable-largefile \
- --with-proj-share=/usr/share/proj \
- --with-gdal=/usr/bin/gdal-config \
- --with-geos \
- --with-sqlite \
+ --with-bzlib \
--with-cairo --with-cairo-ldflags=-lfontconfig \
- --with-freetype --with-freetype-includes="/usr/include/freetype2/" \
+ --with-cxx \
--with-fftw \
- --with-postgres --with-postgres-includes="/usr/include/postgresql" \
+ --with-freetype --with-freetype-includes="/usr/include/freetype2/" \
+ --with-gdal=/usr/bin/gdal-config \
+ --with-geos \
--with-netcdf \
- --with-zstd \
- --with-bzlib \
--with-pdal \
+ --with-postgres --with-postgres-includes="/usr/include/postgresql" \
+ --with-proj-share=/usr/share/proj \
+ --with-sqlite \
+ --with-zstd \
--without-mysql \
--without-odbc \
- --without-openmp \
--without-opengl \
+ --without-openmp \
&& make -j $NUMTHREADS \
&& make install && ldconfig
diff --git a/docker/ubuntu/Dockerfile b/docker/ubuntu/Dockerfile
index c6366eeb873..41fb3264c07 100644
--- a/docker/ubuntu/Dockerfile
+++ b/docker/ubuntu/Dockerfile
@@ -19,8 +19,9 @@ WORKDIR /tmp
ARG GUI
# Todo: re-consider required dev packages for addons (~400MB in dev packages)
-ARG GRASS_RUN_PACKAGES="build-essential \
+ARG GRASS_RUN_PACKAGES="\
bison \
+ build-essential \
bzip2 \
curl \
flex \
@@ -28,8 +29,6 @@ ARG GRASS_RUN_PACKAGES="build-essential \
gcc \
gdal-bin \
geos-bin \
- proj-bin \
- netcdf-bin \
git \
language-pack-en-base \
libcairo2 \
@@ -38,28 +37,29 @@ ARG GRASS_RUN_PACKAGES="build-essential \
libfftw3-dev \
libfreetype6 \
libgdal-dev \
+ libgeos-dev \
+ libgsl-dev \
libgsl27 \
libjpeg-turbo8 \
libjsoncpp-dev \
- libmagic1 \
+ liblapacke-dev \
libmagic-mgc \
+ libmagic1 \
libncurses5 \
- libopenblas-dev \
+ libomp-dev \
+ libomp5 \
libopenblas-base \
+ libopenblas-dev \
libopenjp2-7 \
- libomp5 \
- libomp-dev \
- libgeos-dev \
- libpdal-dev \
- libproj-dev \
- libpq-dev \
- libgsl-dev \
libpdal-base13 \
+ libpdal-dev \
libpdal-plugin-hdf \
libpdal-plugins \
libpdal-util13 \
libpnglite0 \
+ libpq-dev \
libpq5 \
+ libproj-dev \
libpython3-all-dev \
libreadline8 \
libsqlite3-0 \
@@ -70,7 +70,9 @@ ARG GRASS_RUN_PACKAGES="build-essential \
mesa-utils \
moreutils \
ncurses-bin \
+ netcdf-bin \
pdal \
+ proj-bin \
proj-data \
python-is-python3 \
python3 \
@@ -86,56 +88,58 @@ ARG GRASS_RUN_PACKAGES="build-essential \
ENV GRASS_RUN_PACKAGES=${GRASS_RUN_PACKAGES}
# Define build packages
-ARG GRASS_BUILD_PACKAGES="cmake \
+ARG GRASS_BUILD_PACKAGES="\
+ cmake \
libbz2-dev \
libcairo2-dev \
libfreetype6-dev \
- zlib1g-dev \
+ libjpeg-dev \
+ libncurses5-dev \
libnetcdf-dev \
libopenjp2-7-dev \
- libreadline-dev \
- libjpeg-dev \
libpnglite-dev \
+ libreadline-dev \
libsqlite3-dev \
libtiff-dev \
libzstd-dev \
- libncurses5-dev \
mesa-common-dev \
zlib1g-dev \
"
ENV GRASS_BUILD_PACKAGES=${GRASS_BUILD_PACKAGES}
-ARG GRASS_CONFIG="--with-cxx \
+ARG GRASS_CONFIG="\
--enable-largefile \
- --with-proj-share=/usr/share/proj \
- --with-gdal=/usr/bin/gdal-config \
- --with-geos \
- --with-sqlite \
+ --with-blas \
+ --with-bzlib \
--with-cairo --with-cairo-ldflags=-lfontconfig \
- --with-freetype --with-freetype-includes=/usr/include/freetype2/ \
+ --with-cxx \
--with-fftw \
- --with-postgres --with-postgres-includes=/usr/include/postgresql \
- --with-netcdf \
- --with-zstd \
- --with-bzlib \
- --with-pdal \
- --without-mysql \
- --with-blas \
+ --with-freetype --with-freetype-includes=/usr/include/freetype2/ \
+ --with-gdal=/usr/bin/gdal-config \
+ --with-geos \
--with-lapack \
- --with-readline \
+ --with-netcdf \
--with-odbc \
--with-openmp \
+ --with-pdal \
+ --with-postgres --with-postgres-includes=/usr/include/postgresql \
+ --with-proj-share=/usr/share/proj \
+ --with-readline \
+ --with-sqlite \
+ --with-zstd \
+ --without-mysql \
"
-ARG GRASS_PYTHON_PACKAGES="pip \
- setuptools \
- python-dateutil \
- python-magic \
- numpy \
+ARG GRASS_PYTHON_PACKAGES="\
Pillow \
- ply \
matplotlib \
+ numpy \
+ pip \
+ ply \
psycopg2 \
+ python-dateutil \
+ python-magic \
+ setuptools \
"
ENV GRASS_PYTHON_PACKAGES=${GRASS_PYTHON_PACKAGES}
@@ -147,31 +151,33 @@ ENV GRASS_CONFIG=${GRASS_CONFIG}
FROM common_start as grass_with_gui
-ARG GRASS_RUN_PACKAGES="${GRASS_RUN_PACKAGES} adwaita-icon-theme-full \
- libglu1-mesa \
- libgtk-3-0 \
- libnotify4 \
- libsdl2-2.0-0 \
- libxtst6 \
- librsvg2-common \
- gettext \
+ARG GRASS_RUN_PACKAGES="${GRASS_RUN_PACKAGES} \
+ adwaita-icon-theme-full \
freeglut3 \
+ gettext \
+ libglu1-mesa \
libgstreamer-plugins-base1.0 \
+ libgtk-3-0 \
libjpeg8 \
+ libnotify4 \
libpng16-16 \
+ librsvg2-common \
+ libsdl2-2.0-0 \
libsm6 \
libtiff5 \
libwebkit2gtk-4.0 \
+ libxtst6 \
"
# librsvg2-common \
# (fix error (wxgui.py:7782): Gtk-WARNING **: 19:53:09.774:
# Could not load a pixbuf from /org/gtk/libgtk/theme/Adwaita/assets/check-symbolic.svg.
# This may indicate that pixbuf loaders or the mime database could not be found.)
-ARG GRASS_BUILD_PACKAGES="${GRASS_BUILD_PACKAGES} adwaita-icon-theme-full \
+ARG GRASS_BUILD_PACKAGES="${GRASS_BUILD_PACKAGES} \
+ adwaita-icon-theme-full \
+ freeglut3-dev \
libgl1-mesa-dev \
libglu1-mesa-dev \
- freeglut3-dev \
libgstreamer-plugins-base1.0-dev \
libgtk-3-dev \
libjpeg-dev \
@@ -184,10 +190,11 @@ ARG GRASS_BUILD_PACKAGES="${GRASS_BUILD_PACKAGES} adwaita-icon-theme-full \
libxtst-dev \
"
-ARG GRASS_CONFIG="${GRASS_CONFIG} --with-opengl \
- --with-x \
+ARG GRASS_CONFIG="${GRASS_CONFIG} \
--with-nls \
+ --with-opengl \
--with-readline \
+ --with-x \
"
ARG GRASS_PYTHON_PACKAGES="${GRASS_PYTHON_PACKAGES} wxPython"
# If you do not use any Gnome Accessibility features, to suppress warning
diff --git a/docker/ubuntu_wxgui/Dockerfile b/docker/ubuntu_wxgui/Dockerfile
index 36d33ffeb20..e15d5dcf478 100644
--- a/docker/ubuntu_wxgui/Dockerfile
+++ b/docker/ubuntu_wxgui/Dockerfile
@@ -29,8 +29,8 @@ WORKDIR /tmp
RUN apt-get update && apt-get upgrade -y && \
apt-get install -y --no-install-recommends --no-install-suggests \
adwaita-icon-theme-full \
- build-essential \
bison \
+ build-essential \
bzip2 \
cmake \
curl \
@@ -48,17 +48,17 @@ RUN apt-get update && apt-get upgrade -y && \
libfftw3-bin \
libfftw3-dev \
libfreetype6-dev \
- libgl1-mesa-dev \
libgdal-dev \
libgeos-dev \
+ libgl1-mesa-dev \
libglu1-mesa-dev \
libgsl0-dev \
libgtk-3-0 \
libgtk-3-dev \
libjpeg-dev \
libjsoncpp-dev \
- libnetcdf-dev \
libncurses5-dev \
+ libnetcdf-dev \
libnotify4 \
libopenblas-base \
libopenblas-dev \
@@ -68,8 +68,8 @@ RUN apt-get update && apt-get upgrade -y && \
libpq-dev \
libproj-dev \
libpython3-all-dev \
- librsvg2-common \
libreadline-dev \
+ librsvg2-common \
libsdl2-2.0-0 \
libsqlite3-dev \
libtiff-dev \
@@ -191,26 +191,26 @@ ENV CXXFLAGS "$MYCXXFLAGS"
ENV NUMTHREADS=4
RUN make distclean || echo "nothing to clean"
RUN /src/grass_build/configure \
- --with-cxx \
--enable-largefile \
- --with-proj-share=/usr/share/proj \
- --with-gdal=/usr/bin/gdal-config \
- --with-geos \
- --with-sqlite \
+ --with-bzlib \
--with-cairo --with-cairo-ldflags=-lfontconfig \
- --with-freetype --with-freetype-includes="/usr/include/freetype2/" \
+ --with-cxx \
--with-fftw \
- --with-postgres --with-postgres-includes="/usr/include/postgresql" \
+ --with-freetype --with-freetype-includes="/usr/include/freetype2/" \
+ --with-gdal=/usr/bin/gdal-config \
+ --with-geos \
--with-netcdf \
- --with-zstd \
- --with-bzlib \
+ --with-nls \
--with-pdal \
+ --with-postgres --with-postgres-includes="/usr/include/postgresql" \
+ --with-proj-share=/usr/share/proj \
+ --with-readline \
+ --with-sqlite \
+ --with-x \
+ --with-zstd \
--without-mysql \
--without-odbc \
--without-openmp \
- --with-x \
- --with-nls \
- --with-readline \
&& make -j $NUMTHREADS \
&& make install && ldconfig
diff --git a/flake.lock b/flake.lock
index 257ec2317b7..51f61fb86f9 100644
--- a/flake.lock
+++ b/flake.lock
@@ -5,11 +5,11 @@
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
- "lastModified": 1725234343,
- "narHash": "sha256-+ebgonl3NbiKD2UD0x4BszCZQ6sTfL4xioaM49o5B3Y=",
+ "lastModified": 1726153070,
+ "narHash": "sha256-HO4zgY0ekfwO5bX0QH/3kJ/h4KvUDFZg8YpkNwIbg1U=",
"owner": "hercules-ci",
"repo": "flake-parts",
- "rev": "567b938d64d4b4112ee253b9274472dc3a346eb6",
+ "rev": "bcef6817a8b2aa20a5a6dbb19b43e63c5bf8619a",
"type": "github"
},
"original": {
@@ -19,11 +19,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1725194671,
- "narHash": "sha256-tLGCFEFTB5TaOKkpfw3iYT9dnk4awTP/q4w+ROpMfuw=",
+ "lastModified": 1727648392,
+ "narHash": "sha256-VTlVv1nSxImFxY6RPQpNZxvEOQ0u5s1wBFDgixySNDo=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "b833ff01a0d694b910daca6e2ff4a3f26dee478c",
+ "rev": "4e0c36e4dd53f35d5a6385bdae88895ec5832f70",
"type": "github"
},
"original": {
diff --git a/general/g.mapsets/tests/g_mapsets_list_format_test.py b/general/g.mapsets/tests/g_mapsets_list_format_test.py
index 79555085bb0..05fe946a6ae 100644
--- a/general/g.mapsets/tests/g_mapsets_list_format_test.py
+++ b/general/g.mapsets/tests/g_mapsets_list_format_test.py
@@ -14,6 +14,7 @@
"""Test parsing and structure of CSV and JSON outputs from g.mapsets"""
import json
+import sys
import pytest
import grass.script as gs
from grass.script import utils as gutils
@@ -30,6 +31,10 @@ def _check_parsed_list(mapsets, text, sep="|"):
assert text == sep.join(mapsets) + "\n"
+@pytest.mark.xfail(
+ sys.platform == "win32",
+ reason="universal_newlines or text subprocess option not used",
+)
@pytest.mark.parametrize("separator", SEPARATORS)
def test_plain_list_output(simple_dataset, separator):
"""Test that the separators are properly applied with list flag"""
@@ -38,6 +43,10 @@ def test_plain_list_output(simple_dataset, separator):
_check_parsed_list(mapsets, text, gutils.separator(separator))
+@pytest.mark.xfail(
+ sys.platform == "win32",
+ reason="universal_newlines or text subprocess option not used",
+)
@pytest.mark.parametrize("separator", SEPARATORS)
def test_plain_print_output(simple_dataset, separator):
"""Test that the separators are properly applied with print flag"""
diff --git a/general/g.version/tests/conftest.py b/general/g.version/tests/conftest.py
new file mode 100644
index 00000000000..e4357b2f672
--- /dev/null
+++ b/general/g.version/tests/conftest.py
@@ -0,0 +1,17 @@
+import os
+import pytest
+import grass.script as gs
+
+
+@pytest.fixture(scope="module")
+def session(tmp_path_factory):
+ """Set up a GRASS session for the tests."""
+ tmp_path = tmp_path_factory.mktemp("grass_session")
+ project = "test_project"
+
+ # Create a test location
+ gs.create_project(tmp_path, project)
+
+ # Initialize the GRASS session
+ with gs.setup.init(tmp_path / project, env=os.environ.copy()) as session:
+ yield session
diff --git a/general/g.version/tests/g_version_test.py b/general/g.version/tests/g_version_test.py
new file mode 100644
index 00000000000..805518382a2
--- /dev/null
+++ b/general/g.version/tests/g_version_test.py
@@ -0,0 +1,83 @@
+import grass.script as gs
+
+
+def test_g_version_no_flag(session):
+ """Test that g.version output contains the word 'GRASS'."""
+ output = gs.read_command("g.version", env=session.env).strip()
+ assert (
+ "GRASS" in output
+ ), "Expected 'GRASS' in g.version output, but it was not found."
+
+
+def test_c_flag(session):
+ """Test the output of g.version -c for Copyright and License Statement."""
+ expected_text = "Copyright and License Statement"
+ output = gs.read_command("g.version", flags="c", env=session.env).strip()
+ assert (
+ expected_text in output
+ ), f"Expected '{expected_text}' in g.version -c output, but got: '{output}'"
+
+
+def test_e_flag(session):
+ """Test that g.version -e contains the expected keys."""
+ expected_keys = ["PROJ:", "GDAL/OGR:", "SQLite:"]
+ output = gs.read_command("g.version", flags="e", env=session.env).strip()
+ for key in expected_keys:
+ assert (
+ key in output
+ ), f"Expected key '{key}' in g.version -e output, but it was not found."
+
+
+def test_b_flag(session):
+ """Test that g.version -b output contains the word 'GRASS'."""
+ output = gs.read_command("g.version", flags="b", env=session.env).strip()
+ assert (
+ "GRASS" in output
+ ), "Expected 'GRASS' in g.version -b output, but it was not found."
+
+
+def test_g_flag(session):
+ """Test that g.version -g contains the expected keys."""
+ expected_keys = [
+ "version",
+ "date",
+ "revision",
+ "build_date",
+ "build_platform",
+ "build_off_t_size",
+ ]
+ output = gs.parse_command("g.version", flags="g", env=session.env)
+ for key in expected_keys:
+ assert (
+ key in output
+ ), f"Expected key '{key}' in g.version -g output, but it was not found."
+
+
+def test_r_flag(session):
+ """Test that g.version -r contains the expected keys."""
+ expected_texts = ["libgis revision:", "libgis date:"]
+ output = gs.read_command("g.version", flags="r", env=session.env).strip()
+ for text in expected_texts:
+ assert (
+ text in output
+ ), f"Expected key '{text}' in g.version -r output, but it was not found."
+
+
+def test_x_flag(session):
+ """Test that g.version -x output has paired curly brackets."""
+ output = gs.read_command("g.version", flags="x", env=session.env).strip()
+
+ def curly_brackets_paired(text):
+ counter = 0
+ for character in text:
+ if character == "{":
+ counter += 1
+ elif character == "}":
+ counter -= 1
+ if counter < 0:
+ return False
+ return counter == 0
+
+ assert curly_brackets_paired(
+ output
+ ), "Curly brackets are not properly paired in the g.version -x output."
diff --git a/gui/wxpython/animation/controller.py b/gui/wxpython/animation/controller.py
index 1a920f77de7..1a8cb1fe9cc 100644
--- a/gui/wxpython/animation/controller.py
+++ b/gui/wxpython/animation/controller.py
@@ -465,9 +465,7 @@ def EvaluateInput(self, animationData):
mapCount.add(len(layer.maps))
windowIndex.append(anim.windowIndex)
- if maps and stds:
- temporalMode = TemporalMode.NONTEMPORAL
- elif maps:
+ if (maps and stds) or maps:
temporalMode = TemporalMode.NONTEMPORAL
elif stds:
temporalMode = TemporalMode.TEMPORAL
diff --git a/gui/wxpython/animation/dialogs.py b/gui/wxpython/animation/dialogs.py
index 3d70dbd2639..f6e4db341c5 100644
--- a/gui/wxpython/animation/dialogs.py
+++ b/gui/wxpython/animation/dialogs.py
@@ -24,6 +24,9 @@
import wx
import copy
import datetime
+
+from pathlib import Path
+
import wx.lib.filebrowsebutton as filebrowse
import wx.lib.scrolledpanel as SP
import wx.lib.colourselect as csel
@@ -488,7 +491,7 @@ def _create3DPanel(self, parent):
labelText=_("Workspace file:"),
dialogTitle=_("Choose workspace file to import 3D view parameters"),
buttonText=_("Browse"),
- startDirectory=os.getcwd(),
+ startDirectory=str(Path.cwd()),
fileMode=0,
fileMask="GRASS Workspace File (*.gxw)|*.gxw",
)
@@ -1089,7 +1092,7 @@ def _createDecorationsProperties(self, panel):
labelText=_("Image file:"),
dialogTitle=_("Choose image file"),
buttonText=_("Browse"),
- startDirectory=os.getcwd(),
+ startDirectory=str(Path.cwd()),
fileMode=wx.FD_OPEN,
changeCallback=self.OnSetImage,
)
@@ -1191,7 +1194,7 @@ def _createExportFormatPanel(self, notebook):
labelText=_("Directory:"),
dialogTitle=_("Choose directory for export"),
buttonText=_("Browse"),
- startDirectory=os.getcwd(),
+ startDirectory=str(Path.cwd()),
)
dirGridSizer = wx.GridBagSizer(hgap=5, vgap=5)
@@ -1219,7 +1222,7 @@ def _createExportFormatPanel(self, notebook):
labelText=_("GIF file:"),
dialogTitle=_("Choose file to save animation"),
buttonText=_("Browse"),
- startDirectory=os.getcwd(),
+ startDirectory=str(Path.cwd()),
fileMode=wx.FD_SAVE,
)
gifGridSizer = wx.GridBagSizer(hgap=5, vgap=5)
@@ -1242,7 +1245,7 @@ def _createExportFormatPanel(self, notebook):
labelText=_("SWF file:"),
dialogTitle=_("Choose file to save animation"),
buttonText=_("Browse"),
- startDirectory=os.getcwd(),
+ startDirectory=str(Path.cwd()),
fileMode=wx.FD_SAVE,
)
swfGridSizer = wx.GridBagSizer(hgap=5, vgap=5)
@@ -1273,7 +1276,7 @@ def _createExportFormatPanel(self, notebook):
labelText=_("AVI file:"),
dialogTitle=_("Choose file to save animation"),
buttonText=_("Browse"),
- startDirectory=os.getcwd(),
+ startDirectory=str(Path.cwd()),
fileMode=wx.FD_SAVE,
)
encodingLabel = StaticText(
@@ -1572,7 +1575,7 @@ def _export_file_validation(self, filebrowsebtn, file_path, file_postfix):
caption=_("Overwrite?"),
style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION,
)
- if not overwrite_dlg.ShowModal() == wx.ID_YES:
+ if overwrite_dlg.ShowModal() != wx.ID_YES:
overwrite_dlg.Destroy()
return False
overwrite_dlg.Destroy()
diff --git a/gui/wxpython/animation/temporal_manager.py b/gui/wxpython/animation/temporal_manager.py
index 150eeab510f..ef782543089 100644
--- a/gui/wxpython/animation/temporal_manager.py
+++ b/gui/wxpython/animation/temporal_manager.py
@@ -18,6 +18,7 @@
"""
import datetime
+from operator import itemgetter
import grass.script as gs
import grass.temporal as tgis
@@ -195,9 +196,9 @@ def GetLabelsAndMaps(self):
# by a temporary dataset, I don't know how it would work with point
# data
if self.temporalType == TemporalType.ABSOLUTE:
- timestamps = sorted(list(labelListSet), key=lambda x: x[0])
+ timestamps = sorted(list(labelListSet), key=itemgetter(0))
else:
- timestamps = sorted(list(labelListSet), key=lambda x: x[0])
+ timestamps = sorted(list(labelListSet), key=itemgetter(0))
newMapLists = []
for mapList, labelList in zip(mapLists, labelLists):
diff --git a/gui/wxpython/core/gcmd.py b/gui/wxpython/core/gcmd.py
index b93789f22f7..eb40ec57103 100644
--- a/gui/wxpython/core/gcmd.py
+++ b/gui/wxpython/core/gcmd.py
@@ -645,11 +645,11 @@ def _formatMsg(text):
for line in text.splitlines():
if len(line) == 0:
continue
- elif "GRASS_INFO_MESSAGE" in line:
- message += line.split(":", 1)[1].strip() + "\n"
- elif "GRASS_INFO_WARNING" in line:
- message += line.split(":", 1)[1].strip() + "\n"
- elif "GRASS_INFO_ERROR" in line:
+ elif (
+ "GRASS_INFO_MESSAGE" in line
+ or "GRASS_INFO_WARNING" in line
+ or "GRASS_INFO_ERROR" in line
+ ):
message += line.split(":", 1)[1].strip() + "\n"
elif "GRASS_INFO_END" in line:
return message
diff --git a/gui/wxpython/core/gconsole.py b/gui/wxpython/core/gconsole.py
index bad4aac29cd..092f9bc704a 100644
--- a/gui/wxpython/core/gconsole.py
+++ b/gui/wxpython/core/gconsole.py
@@ -678,7 +678,7 @@ def load_source(modname, filename):
skipInterface = True
if os.path.splitext(command[0])[1] in {".py", ".sh"}:
try:
- with open(command[0], "r") as sfile:
+ with open(command[0]) as sfile:
for line in sfile:
if len(line) < 3:
continue
@@ -691,7 +691,7 @@ def load_source(modname, filename):
if len(command) == 1 and not skipInterface:
try:
task = gtask.parse_interface(command[0])
- except:
+ except Exception:
task = None
else:
task = None
diff --git a/gui/wxpython/core/render.py b/gui/wxpython/core/render.py
index b98e4323578..1d4ed6f5386 100644
--- a/gui/wxpython/core/render.py
+++ b/gui/wxpython/core/render.py
@@ -896,7 +896,7 @@ def GetWindow(self):
env["GISDBASE"], env["LOCATION_NAME"], env["MAPSET"], "WIND"
)
try:
- windfile = open(filename, "r")
+ windfile = open(filename)
except OSError as e:
sys.exit(
_("Error: Unable to open '%(file)s'. Reason: %(ret)s. wxGUI exited.\n")
@@ -1237,7 +1237,7 @@ def SetRegion(self, windres=False, windres3=False):
return grass_region
- except:
+ except Exception:
return None
def GetListOfLayers(
diff --git a/gui/wxpython/core/settings.py b/gui/wxpython/core/settings.py
index 45620220aed..ef3a3031cee 100644
--- a/gui/wxpython/core/settings.py
+++ b/gui/wxpython/core/settings.py
@@ -110,7 +110,7 @@ def _generateLocale(self):
self.locs.sort()
# Add a default choice to not override system locale
self.locs.insert(0, "system")
- except:
+ except Exception:
# No NLS
self.locs = ["system"]
@@ -922,7 +922,7 @@ def update_nested_dict_by_dict(dictionary, update):
return dictionary
try:
- with open(self.filePath, "r") as f:
+ with open(self.filePath) as f:
update = json.load(f, object_hook=settings_JSON_decode_hook)
update_nested_dict_by_dict(settings, update)
except json.JSONDecodeError as e:
@@ -942,7 +942,7 @@ def _readLegacyFile(self, settings=None):
settings = self.userSettings
try:
- fd = open(self.legacyFilePath, "r")
+ fd = open(self.legacyFilePath)
except OSError:
sys.stderr.write(
_("Unable to read settings file <%s>\n") % self.legacyFilePath
@@ -992,7 +992,7 @@ def SaveToFile(self, settings=None):
if not os.path.exists(dirPath):
try:
os.mkdir(dirPath)
- except:
+ except OSError:
GError(_("Unable to create settings directory"))
return
try:
diff --git a/gui/wxpython/core/testsuite/test_gcmd.py b/gui/wxpython/core/testsuite/test_gcmd.py
index 121ede99b77..75d3bf5932b 100644
--- a/gui/wxpython/core/testsuite/test_gcmd.py
+++ b/gui/wxpython/core/testsuite/test_gcmd.py
@@ -1,5 +1,6 @@
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
+from grass.gunittest.utils import xfail_windows
class Rcv:
@@ -16,6 +17,8 @@ def recv(self):
class Recv_SomeTest(TestCase):
+
+ @xfail_windows
def test_decode(self):
"""
Multibyte chars should not be split
diff --git a/gui/wxpython/core/toolboxes.py b/gui/wxpython/core/toolboxes.py
index ae6b1b6a0a6..e77010ff213 100644
--- a/gui/wxpython/core/toolboxes.py
+++ b/gui/wxpython/core/toolboxes.py
@@ -209,7 +209,7 @@ def getMenudataFile(userRootFile, newFile, fallback):
fh.write(xml)
fh.close()
return menudataFile
- except:
+ except Exception:
_debug(
2,
(
diff --git a/gui/wxpython/core/utils.py b/gui/wxpython/core/utils.py
index 10d6dca2759..4be6c18e2a2 100644
--- a/gui/wxpython/core/utils.py
+++ b/gui/wxpython/core/utils.py
@@ -77,7 +77,7 @@ def GetTempfile(pref=None):
return os.path.join(pref, file)
else:
return tempfile
- except:
+ except Exception:
return None
@@ -255,7 +255,7 @@ def ListOfCatsToRange(cats):
try:
cats = list(map(int, cats))
- except:
+ except ValueError:
return catstr
i = 0
@@ -579,7 +579,7 @@ def GetListOfLocations(dbase):
os.path.join(location, "*")
):
listOfLocations.append(os.path.basename(location))
- except:
+ except OSError:
pass
ListSortLower(listOfLocations)
@@ -632,7 +632,7 @@ def _getGDALFormats():
"""Get dictionary of available GDAL drivers"""
try:
ret = grass.read_command("r.in.gdal", quiet=True, flags="f")
- except:
+ except grass.CalledModuleError:
ret = None
return _parseFormats(ret), _parseFormats(ret, writableOnly=True)
@@ -642,7 +642,7 @@ def _getOGRFormats():
"""Get dictionary of available OGR drivers"""
try:
ret = grass.read_command("v.in.ogr", quiet=True, flags="f")
- except:
+ except grass.CalledModuleError:
ret = None
return _parseFormats(ret), _parseFormats(ret, writableOnly=True)
diff --git a/gui/wxpython/core/watchdog.py b/gui/wxpython/core/watchdog.py
index 652bc1d755d..76302aeea0a 100644
--- a/gui/wxpython/core/watchdog.py
+++ b/gui/wxpython/core/watchdog.py
@@ -19,6 +19,9 @@
import os
import time
+
+from pathlib import Path
+
import wx
from wx.lib.newevent import NewEvent
@@ -59,13 +62,13 @@ def on_modified(self, event):
not event.is_directory
and os.path.basename(event.src_path) == self.rcfile_name
):
- timestamp = os.stat(event.src_path).st_mtime
+ timestamp = Path(event.src_path).stat().st_mtime
if timestamp - self.modified_time < 0.5:
return
self.modified_time = timestamp
# wait to make sure file writing is done
time.sleep(0.1)
- with open(event.src_path, "r") as f:
+ with open(event.src_path) as f:
gisrc = {}
for line in f:
key, val = line.split(":")
diff --git a/gui/wxpython/core/workspace.py b/gui/wxpython/core/workspace.py
index 63f8b91cb89..be4eb1fda37 100644
--- a/gui/wxpython/core/workspace.py
+++ b/gui/wxpython/core/workspace.py
@@ -123,7 +123,7 @@ def __processFile(self):
try:
self.layerManager["pos"] = (posVal[0], posVal[1])
self.layerManager["size"] = (posVal[2], posVal[3])
- except:
+ except IndexError:
pass
# current working directory
cwdPath = self.__getNodeText(node_lm, "cwd")
@@ -155,7 +155,7 @@ def __processFile(self):
try:
pos = (posVal[0], posVal[1])
size = (posVal[2], posVal[3])
- except:
+ except IndexError:
pos = None
size = None
# this happens on Windows when mapwindow is minimized when
@@ -1742,7 +1742,7 @@ def read(self, parent):
:return: list of map layers
"""
try:
- file = open(self.filename, "r")
+ file = open(self.filename)
except OSError:
wx.MessageBox(
parent=parent,
@@ -2019,7 +2019,7 @@ def _get_value(self, line):
"""Get value of element"""
try:
return line.strip(" ").split(" ")[1].strip(" ")
- except:
+ except IndexError:
return ""
def _get_element(self, line):
diff --git a/gui/wxpython/datacatalog/catalog.py b/gui/wxpython/datacatalog/catalog.py
index 880190b0943..4835061d968 100644
--- a/gui/wxpython/datacatalog/catalog.py
+++ b/gui/wxpython/datacatalog/catalog.py
@@ -19,6 +19,8 @@
import wx
import os
+from pathlib import Path
+
from core.debug import Debug
from datacatalog.tree import DataCatalogTree
from datacatalog.toolbars import DataCatalogToolbar, DataCatalogSearch
@@ -200,7 +202,10 @@ def OnReloadCurrentMapset(self, event):
def OnAddGrassDB(self, event):
"""Add grass database"""
dlg = wx.DirDialog(
- self, _("Choose GRASS data directory:"), os.getcwd(), wx.DD_DEFAULT_STYLE
+ self,
+ _("Choose GRASS data directory:"),
+ str(Path.cwd()),
+ wx.DD_DEFAULT_STYLE,
)
if dlg.ShowModal() == wx.ID_OK:
grassdatabase = dlg.GetPath()
diff --git a/gui/wxpython/datacatalog/tree.py b/gui/wxpython/datacatalog/tree.py
index faa0a6aac73..9a460d05f53 100644
--- a/gui/wxpython/datacatalog/tree.py
+++ b/gui/wxpython/datacatalog/tree.py
@@ -1378,15 +1378,20 @@ def OnPasteMap(self, event):
)
if not new_name:
continue
- callback = lambda gisrc2=gisrc2, gisrc=gisrc, cLayer=self.copy_layer[
- i
- ], cMapset=self.copy_mapset[
- i
- ], cMode=self.copy_mode, sMapset=self.selected_mapset[
- 0
- ], name=new_name: self._onDoneReprojection(
- env2, gisrc2, gisrc, cLayer, cMapset, cMode, sMapset, name
- )
+
+ def callback(
+ gisrc2=gisrc2,
+ gisrc=gisrc,
+ cLayer=self.copy_layer[i],
+ cMapset=self.copy_mapset[i],
+ cMode=self.copy_mode,
+ sMapset=self.selected_mapset[0],
+ name=new_name,
+ ):
+ self._onDoneReprojection(
+ env2, gisrc2, gisrc, cLayer, cMapset, cMode, sMapset, name
+ )
+
dlg = CatalogReprojectionDialog(
self,
self._giface,
diff --git a/gui/wxpython/dbmgr/base.py b/gui/wxpython/dbmgr/base.py
index 66321fcafda..d7ea818da4c 100644
--- a/gui/wxpython/dbmgr/base.py
+++ b/gui/wxpython/dbmgr/base.py
@@ -208,7 +208,7 @@ def LoadData(self, layer, columns=None, where=None, sql=None):
try:
# for maps connected via v.external
keyId = columns.index(keyColumn)
- except:
+ except ValueError:
keyId = -1
# read data
@@ -961,7 +961,7 @@ def OnLayerPageChanged(self, event):
self.layerPage[self.selLayer]["data"]
).GetItemCount()
)
- except:
+ except Exception:
pass
if idCol:
@@ -1640,7 +1640,7 @@ def OnDataItemAdd(self, event):
if dlg.ShowModal() == wx.ID_OK:
try: # get category number
cat = int(dlg.GetValues(columns=[keyColumn])[0])
- except:
+ except ValueError:
cat = -1
try:
@@ -1672,7 +1672,7 @@ def OnDataItemAdd(self, event):
values[i] = int(float(values[i]))
elif tlist.columns[columnName[i]]["ctype"] == float:
values[i] = float(values[i])
- except:
+ except ValueError:
raise ValueError(
_("Value '%(value)s' needs to be entered as %(type)s.")
% {
@@ -1680,6 +1680,13 @@ def OnDataItemAdd(self, event):
"type": tlist.columns[columnName[i]]["type"],
}
)
+ except KeyError:
+ raise KeyError(
+ _("Column '%(column)s' does not exist.")
+ % {
+ "column": columnName[i],
+ }
+ )
columnsString += "%s," % columnName[i]
if tlist.columns[columnName[i]]["ctype"] == str:
@@ -3809,7 +3816,7 @@ def OnDeleteLayer(self, event):
"""Delete layer"""
try:
layer = int(self.deleteLayer.GetValue())
- except:
+ except ValueError:
return
RunCommand(
@@ -3856,10 +3863,10 @@ def OnChangeLayer(self, event):
"""Layer number of layer to be deleted is changed"""
try:
layer = int(event.GetString())
- except:
+ except ValueError:
try:
- layer = self.mapDBInfo.layers.keys()[0]
- except:
+ layer = list(self.mapDBInfo.layers.keys())[0]
+ except IndexError:
return
if self.GetCurrentPage() == self.modifyPanel:
diff --git a/gui/wxpython/dbmgr/dialogs.py b/gui/wxpython/dbmgr/dialogs.py
index 26e6d876371..0d7c6e9e523 100644
--- a/gui/wxpython/dbmgr/dialogs.py
+++ b/gui/wxpython/dbmgr/dialogs.py
@@ -222,10 +222,11 @@ def GetSQLString(self, updateValues=False):
ctype = columns[name]["ctype"]
value = columns[name]["values"][idx]
id = columns[name]["ids"][idx]
+ widget = self.FindWindowById(id)
try:
- newvalue = self.FindWindowById(id).GetValue()
- except:
- newvalue = self.FindWindowById(id).GetLabel()
+ newvalue = widget.GetValue()
+ except AttributeError:
+ newvalue = widget.GetLabel()
if newvalue:
try:
diff --git a/gui/wxpython/dbmgr/manager.py b/gui/wxpython/dbmgr/manager.py
index c866bd55650..b1cbd93ab71 100644
--- a/gui/wxpython/dbmgr/manager.py
+++ b/gui/wxpython/dbmgr/manager.py
@@ -70,7 +70,7 @@ def __init__(
self.parent = parent
try:
mapdisplay = self.parent.GetMapDisplay()
- except:
+ except AttributeError:
mapdisplay = None
DbMgrBase.__init__(
diff --git a/gui/wxpython/dbmgr/sqlbuilder.py b/gui/wxpython/dbmgr/sqlbuilder.py
index 36827698efc..5e6fe0e7706 100644
--- a/gui/wxpython/dbmgr/sqlbuilder.py
+++ b/gui/wxpython/dbmgr/sqlbuilder.py
@@ -359,7 +359,7 @@ def OnUniqueValues(self, event, justsample=False):
try:
idx = self.list_columns.GetSelections()[0]
column = self.list_columns.GetString(idx)
- except:
+ except IndexError:
self.list_values.Clear()
return
diff --git a/gui/wxpython/docs/wxgui_sphinx/conf.py b/gui/wxpython/docs/wxgui_sphinx/conf.py
index 3641c2aab48..f0b61a31996 100644
--- a/gui/wxpython/docs/wxgui_sphinx/conf.py
+++ b/gui/wxpython/docs/wxgui_sphinx/conf.py
@@ -10,21 +10,9 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-import sys
-import os
from datetime import date
import string
from shutil import copy
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-if not os.getenv("GISBASE"):
- sys.exit("GISBASE not defined")
-sys.path.insert(
- 0, os.path.abspath(os.path.join(os.environ["GISBASE"], "etc", "python", "grass"))
-)
-
from grass.script import core
footer_tmpl = string.Template(
diff --git a/gui/wxpython/gcp/manager.py b/gui/wxpython/gcp/manager.py
index e5e0dd37471..3b6dd398b51 100644
--- a/gui/wxpython/gcp/manager.py
+++ b/gui/wxpython/gcp/manager.py
@@ -119,7 +119,7 @@ def __init__(self, parent, giface):
self.target_gisrc = os.environ["GISRC"]
self.gisrc_dict = {}
try:
- f = open(self.target_gisrc, "r")
+ f = open(self.target_gisrc)
for line in f:
line = line.replace("\n", "").strip()
if len(line) < 1:
@@ -880,20 +880,6 @@ def OnSrcSelection(self, event):
else:
wx.FindWindowById(wx.ID_FORWARD).Enable(True)
- try:
- # set computational region to match selected map and zoom display
- # to region
- if maptype == "raster":
- p = RunCommand("g.region", "raster=src_map")
- elif maptype == "vector":
- p = RunCommand("g.region", "vector=src_map")
-
- if p.returncode == 0:
- print("returncode = ", str(p.returncode))
- self.parent.Map.region = self.parent.Map.GetRegion()
- except:
- pass
-
def OnTgtRastSelection(self, event):
"""Source map to display selected"""
global tgt_map
@@ -1603,7 +1589,7 @@ def ReadGCPs(self):
GError(parent=self, message=_("target mapwin not defined"))
try:
- f = open(self.file["points"], "r")
+ f = open(self.file["points"])
GCPcnt = 0
for line in f:
@@ -1810,7 +1796,7 @@ def OnGeorect(self, event):
overwrite=self.overwrite,
)
if overwrite_dlg:
- if not overwrite_dlg.ShowModal() == wx.ID_YES:
+ if overwrite_dlg.ShowModal() != wx.ID_YES:
overwrite_dlg.Destroy()
return
overwrite_dlg.Destroy()
@@ -1870,7 +1856,7 @@ def OnGeorect(self, event):
overwrite=self.overwrite,
)
if overwrite_dlg:
- if not overwrite_dlg.ShowModal() == wx.ID_YES:
+ if overwrite_dlg.ShowModal() != wx.ID_YES:
overwrite_dlg.Destroy()
return
overwrite_dlg.Destroy()
@@ -2300,7 +2286,7 @@ def AdjustMap(self, newreg):
def OnZoomToSource(self, event):
"""Set target map window to match extents of source map window"""
- if not self.MapWindow == self.TgtMapWindow:
+ if self.MapWindow != self.TgtMapWindow:
self.MapWindow = self.TgtMapWindow
self.Map = self.TgtMap
self.UpdateActive(self.TgtMapWindow)
@@ -2313,7 +2299,7 @@ def OnZoomToSource(self, event):
def OnZoomToTarget(self, event):
"""Set source map window to match extents of target map window"""
- if not self.MapWindow == self.SrcMapWindow:
+ if self.MapWindow != self.SrcMapWindow:
self.MapWindow = self.SrcMapWindow
self.Map = self.SrcMap
self.UpdateActive(self.SrcMapWindow)
@@ -3323,7 +3309,7 @@ def OnSrcSelection(self, event):
tmp_map = self.srcselection.GetValue()
- if not tmp_map == "" and not tmp_map == src_map:
+ if tmp_map not in ("", src_map):
self.new_src_map = tmp_map
def OnTgtRastSelection(self, event):
diff --git a/gui/wxpython/gcp/mapdisplay.py b/gui/wxpython/gcp/mapdisplay.py
index 66daa70d2ca..0681b03e4e8 100644
--- a/gui/wxpython/gcp/mapdisplay.py
+++ b/gui/wxpython/gcp/mapdisplay.py
@@ -575,7 +575,7 @@ def GetMapToolbar(self):
return self.toolbars["gcpdisp"]
def _setActiveMapWindow(self, mapWindow):
- if not self.MapWindow == mapWindow:
+ if self.MapWindow != mapWindow:
self.MapWindow = mapWindow
self.Map = mapWindow.Map
self.UpdateActive(mapWindow)
diff --git a/gui/wxpython/gmodeler/canvas.py b/gui/wxpython/gmodeler/canvas.py
index 7c604ec6624..bfe5f7a90af 100644
--- a/gui/wxpython/gmodeler/canvas.py
+++ b/gui/wxpython/gmodeler/canvas.py
@@ -320,19 +320,19 @@ def OnRightClick(self, x, y, keys=0, attachment=0):
popupMenu = Menu()
popupMenu.Append(self.popupID["remove"], _("Remove"))
self.frame.Bind(wx.EVT_MENU, self.OnRemove, id=self.popupID["remove"])
- if isinstance(shape, ModelAction) or isinstance(shape, ModelLoop):
+ if isinstance(shape, (ModelAction, ModelLoop)):
if shape.IsEnabled():
popupMenu.Append(self.popupID["enable"], _("Disable"))
self.frame.Bind(wx.EVT_MENU, self.OnDisable, id=self.popupID["enable"])
else:
popupMenu.Append(self.popupID["enable"], _("Enable"))
self.frame.Bind(wx.EVT_MENU, self.OnEnable, id=self.popupID["enable"])
- if isinstance(shape, ModelAction) or isinstance(shape, ModelComment):
+ if isinstance(shape, (ModelAction, ModelComment)):
popupMenu.AppendSeparator()
if isinstance(shape, ModelAction):
popupMenu.Append(self.popupID["label"], _("Set label"))
self.frame.Bind(wx.EVT_MENU, self.OnSetLabel, id=self.popupID["label"])
- if isinstance(shape, ModelAction) or isinstance(shape, ModelComment):
+ if isinstance(shape, (ModelAction, ModelComment)):
popupMenu.Append(self.popupID["comment"], _("Set comment"))
self.frame.Bind(wx.EVT_MENU, self.OnSetComment, id=self.popupID["comment"])
@@ -377,11 +377,7 @@ def OnRightClick(self, x, y, keys=0, attachment=0):
if self.GetShape().IsIntermediate():
popupMenu.Enable(self.popupID["display"], False)
- if (
- isinstance(shape, ModelData)
- or isinstance(shape, ModelAction)
- or isinstance(shape, ModelLoop)
- ):
+ if isinstance(shape, (ModelData, ModelAction, ModelLoop)):
popupMenu.AppendSeparator()
popupMenu.Append(self.popupID["props"], _("Properties"))
self.frame.Bind(wx.EVT_MENU, self.OnProperties, id=self.popupID["props"])
diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py
index a609a1c8bd8..98a7204abbe 100644
--- a/gui/wxpython/gmodeler/model.py
+++ b/gui/wxpython/gmodeler/model.py
@@ -546,7 +546,7 @@ def _substituteFile(self, item, params=None, checkOnly=False):
for finput in self.fileInput:
# read lines
- fd = open(finput, "r")
+ fd = open(finput)
try:
data = self.fileInput[finput] = fd.read()
finally:
@@ -2641,7 +2641,7 @@ def _writeItem(self, item, ignoreBlock=True, variables={}):
self._writePythonAction(
item, variables, self.model.GetIntermediateData()[:3]
)
- elif isinstance(item, ModelLoop) or isinstance(item, ModelCondition):
+ elif isinstance(item, (ModelLoop, ModelCondition)):
# substitute condition
cond = item.GetLabel()
for variable in self.model.GetVariables():
diff --git a/gui/wxpython/gmodeler/panels.py b/gui/wxpython/gmodeler/panels.py
index 93ef770efe1..416a9b85592 100644
--- a/gui/wxpython/gmodeler/panels.py
+++ b/gui/wxpython/gmodeler/panels.py
@@ -25,6 +25,8 @@
import random
import math
+from pathlib import Path
+
import wx
from wx.lib import ogl
@@ -834,7 +836,7 @@ def OnModelOpen(self, event):
dlg = wx.FileDialog(
parent=self,
message=_("Choose model file"),
- defaultDir=os.getcwd(),
+ defaultDir=str(Path.cwd()),
wildcard=_("GRASS Model File (*.gxm)|*.gxm"),
)
if dlg.ShowModal() == wx.ID_OK:
@@ -892,7 +894,7 @@ def OnModelSaveAs(self, event=None):
dlg = wx.FileDialog(
parent=self,
message=_("Choose file to save current model"),
- defaultDir=os.getcwd(),
+ defaultDir=str(Path.cwd()),
wildcard=_("GRASS Model File (*.gxm)|*.gxm"),
style=wx.FD_SAVE,
)
@@ -1740,7 +1742,7 @@ def SaveAs(self, force=False):
parent=self,
message=_("Choose file to save"),
defaultFile=os.path.basename(self.parent.GetModelFile(ext=False)),
- defaultDir=os.getcwd(),
+ defaultDir=str(Path.cwd()),
wildcard=fn_wildcard,
style=wx.FD_SAVE,
)
diff --git a/gui/wxpython/gui_core/dialogs.py b/gui/wxpython/gui_core/dialogs.py
index f7c7711cadf..cd2a8dcc24d 100644
--- a/gui/wxpython/gui_core/dialogs.py
+++ b/gui/wxpython/gui_core/dialogs.py
@@ -1213,7 +1213,7 @@ def _filter(self, data):
try:
if re.compile(self.flt_pattern).search(dt):
flt_data.append(dt)
- except:
+ except re.error:
pass
return flt_data
@@ -1656,7 +1656,7 @@ def OnFilter(self, event):
try:
if re.compile(event.GetString()).search(layer):
list.append(layer)
- except:
+ except re.error:
pass
list = naturally_sorted(list)
diff --git a/gui/wxpython/gui_core/forms.py b/gui/wxpython/gui_core/forms.py
index 84fd849908c..c1123497abf 100644
--- a/gui/wxpython/gui_core/forms.py
+++ b/gui/wxpython/gui_core/forms.py
@@ -58,6 +58,7 @@
import codecs
from threading import Thread
+from pathlib import Path
import wx
@@ -2015,7 +2016,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar
# check wildcard
try:
fExt = os.path.splitext(p.get("key_desc", ["*.*"])[0])[1]
- except:
+ except IndexError:
fExt = None
if not fExt:
fMask = "*"
@@ -2034,7 +2035,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar
dialogTitle=_("Choose %s")
% p.get("description", _("file")).lower(),
buttonText=_("Browse"),
- startDirectory=os.getcwd(),
+ startDirectory=str(Path.cwd()),
fileMode=fmode,
changeCallback=self.OnSetValue,
)
@@ -2145,7 +2146,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar
dialogTitle=_("Choose %s")
% p.get("description", _("Directory")),
buttonText=_("Browse"),
- startDirectory=os.getcwd(),
+ startDirectory=str(Path.cwd()),
newDirectory=True,
changeCallback=self.OnSetValue,
)
@@ -2587,7 +2588,7 @@ def OnFileLoad(self, event):
data = ""
try:
- f = open(path, "r")
+ f = open(path)
except OSError as e:
gcmd.GError(
parent=self,
@@ -2624,7 +2625,7 @@ def OnFileSave(self, event):
dlg = wx.FileDialog(
parent=self,
message=_("Save input as..."),
- defaultDir=os.getcwd(),
+ defaultDir=str(Path.cwd()),
style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
)
diff --git a/gui/wxpython/gui_core/ghelp.py b/gui/wxpython/gui_core/ghelp.py
index 93879998133..0c4f6cdadfb 100644
--- a/gui/wxpython/gui_core/ghelp.py
+++ b/gui/wxpython/gui_core/ghelp.py
@@ -25,6 +25,7 @@
import sys
import wx
from wx.html import HtmlWindow
+from operator import itemgetter
try:
from wx.lib.agw.hyperlink import HyperLinkCtrl
@@ -273,7 +274,7 @@ def _pageCopyright(self):
"""Copyright information"""
copyfile = os.path.join(os.getenv("GISBASE"), "COPYING")
if os.path.exists(copyfile):
- copyrightFile = open(copyfile, "r")
+ copyrightFile = open(copyfile)
copytext = copyrightFile.read()
copyrightFile.close()
else:
@@ -302,7 +303,7 @@ def _pageLicense(self):
"""Licence about"""
licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT")
if os.path.exists(licfile):
- licenceFile = open(licfile, "r")
+ licenceFile = open(licfile)
license = "".join(licenceFile.readlines())
licenceFile.close()
else:
@@ -450,7 +451,7 @@ def _pageContributors(self, extra=False):
text = StaticText(parent=contribwin, id=wx.ID_ANY, label=item)
text.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
contribBox.Add(text)
- for vals in sorted(contribs, key=lambda x: x[0]):
+ for vals in sorted(contribs, key=itemgetter(0)):
for item in vals:
contribBox.Add(
StaticText(parent=contribwin, id=wx.ID_ANY, label=item)
@@ -565,15 +566,15 @@ def _langString(self, k, v):
allStr = "%s :" % k.upper()
try:
allStr += _(" %d translated") % v["good"]
- except:
+ except KeyError:
pass
try:
allStr += _(" %d fuzzy") % v["fuzzy"]
- except:
+ except KeyError:
pass
try:
allStr += _(" %d untranslated") % v["bad"]
- except:
+ except KeyError:
pass
return allStr
@@ -588,7 +589,7 @@ def _langBox(self, par, k, v):
)
tgood.SetForegroundColour(wx.Colour(35, 142, 35))
langBox.Add(tgood)
- except:
+ except KeyError:
tgood = StaticText(parent=par, id=wx.ID_ANY, label="")
langBox.Add(tgood)
try:
@@ -597,7 +598,7 @@ def _langBox(self, par, k, v):
)
tfuzzy.SetForegroundColour(wx.Colour(255, 142, 0))
langBox.Add(tfuzzy)
- except:
+ except KeyError:
tfuzzy = StaticText(parent=par, id=wx.ID_ANY, label="")
langBox.Add(tfuzzy)
try:
@@ -606,7 +607,7 @@ def _langBox(self, par, k, v):
)
tbad.SetForegroundColour(wx.Colour(255, 0, 0))
langBox.Add(tbad)
- except:
+ except KeyError:
tbad = StaticText(parent=par, id=wx.ID_ANY, label="")
langBox.Add(tbad)
return langBox
@@ -843,7 +844,7 @@ def fillContentsFromFile(self, htmlFile, skipDescription=True):
contents.append(line)
self.SetPage("".join(contents))
self.loaded = True
- except: # The Manual file was not found
+ except Exception: # The Manual file was not found
self.loaded = False
diff --git a/gui/wxpython/gui_core/gselect.py b/gui/wxpython/gui_core/gselect.py
index cec6963a527..ec873f60eb4 100644
--- a/gui/wxpython/gui_core/gselect.py
+++ b/gui/wxpython/gui_core/gselect.py
@@ -47,6 +47,8 @@
import glob
import ctypes
+from pathlib import Path
+
import wx
from core import globalvar
@@ -414,7 +416,7 @@ def SetData(self, **kargs):
self.multiple = kargs["multiple"]
if "onPopup" in kargs:
self.onPopup = kargs["onPopup"]
- if kargs.get("layerTree", None):
+ if kargs.get("layerTree"):
self.filterItems = [] # reset
ltype = kargs["type"]
for layer in kargs["layerTree"].GetVisibleLayers(skipDigitized=True):
@@ -482,7 +484,7 @@ def GetElementList(self, elements=None, exclude=False):
try:
self.seltree.EnsureVisible(item)
self.seltree.SelectItem(item)
- except:
+ except Exception:
pass
def _getElementList(self, element, mapsets=None, elements=None, exclude=False):
@@ -1289,7 +1291,7 @@ def __init__(
style = 0
# disabled, read-only widget has no TextCtrl children (TODO: rewrite)
# if not new and not multiple:
- ### style = wx.CB_READONLY
+ # style = wx.CB_READONLY
wx.ComboBox.__init__(self, parent, id, size=size, style=style, **kwargs)
self.searchPath = searchPath
@@ -1558,7 +1560,7 @@ def __init__(
labelText=_("File:"),
dialogTitle=_("Choose file to import"),
buttonText=_("Browse"),
- startDirectory=os.getcwd(),
+ startDirectory=str(Path.cwd()),
changeCallback=self.OnUpdate,
fileMask=fileMask,
)
@@ -1575,7 +1577,7 @@ def __init__(
labelText=_("Directory:"),
dialogTitle=_("Choose input directory"),
buttonText=_("Browse"),
- startDirectory=os.getcwd(),
+ startDirectory=str(Path.cwd()),
changeCallback=self.OnUpdate,
)
browse.GetChildren()[1].SetName("GdalSelectDataSource")
@@ -1628,7 +1630,7 @@ def __init__(
labelText=_("Name:"),
dialogTitle=_("Choose file"),
buttonText=_("Browse"),
- startDirectory=os.getcwd(),
+ startDirectory=str(Path.cwd()),
changeCallback=self.OnUpdate,
)
browse.GetChildren()[1].SetName("GdalSelectDataSource")
@@ -1663,7 +1665,7 @@ def __init__(
labelText=_("Directory:"),
dialogTitle=_("Choose input directory"),
buttonText=_("Browse"),
- startDirectory=os.getcwd(),
+ startDirectory=str(Path.cwd()),
changeCallback=self.OnUpdate,
)
self.dbWidgets["dirbrowse"] = browse
diff --git a/gui/wxpython/gui_core/preferences.py b/gui/wxpython/gui_core/preferences.py
index c2020b2ffba..724e4a5aec0 100644
--- a/gui/wxpython/gui_core/preferences.py
+++ b/gui/wxpython/gui_core/preferences.py
@@ -28,6 +28,8 @@
import os
import sys
+from pathlib import Path
+
try:
import pwd
@@ -2419,28 +2421,27 @@ def LoadData(self):
"""Load data into list"""
self.InsertColumn(0, _("Mapset"))
self.InsertColumn(1, _("Owner"))
- ### self.InsertColumn(2, _('Group'))
+ # self.InsertColumn(2, _('Group'))
gisenv = gs.gisenv()
locationPath = os.path.join(gisenv["GISDBASE"], gisenv["LOCATION_NAME"])
for mapset in self.parent.all_mapsets_ordered:
index = self.InsertItem(self.GetItemCount(), mapset)
- mapsetPath = os.path.join(locationPath, mapset)
- stat_info = os.stat(mapsetPath)
+ stat_info = Path(locationPath, mapset).stat()
if havePwd:
try:
self.SetItem(index, 1, "%s" % pwd.getpwuid(stat_info.st_uid)[0])
except KeyError:
self.SetItem(index, 1, "nobody")
# FIXME: get group name
- ### self.SetStringItem(index, 2, "%-8s" % stat_info.st_gid)
+ # self.SetStringItem(index, 2, "%-8s" % stat_info.st_gid)
else:
# FIXME: no pwd under MS Windows (owner: 0, group: 0)
self.SetItem(index, 1, "%-8s" % stat_info.st_uid)
- ### self.SetStringItem(index, 2, "%-8s" % stat_info.st_gid)
+ # self.SetStringItem(index, 2, "%-8s" % stat_info.st_gid)
self.SetColumnWidth(col=0, width=wx.LIST_AUTOSIZE)
- ### self.SetColumnWidth(col = 1, width = wx.LIST_AUTOSIZE)
+ # self.SetColumnWidth(col = 1, width = wx.LIST_AUTOSIZE)
def OnCheckItem(self, index, flag):
"""Mapset checked/unchecked"""
diff --git a/gui/wxpython/gui_core/pyedit.py b/gui/wxpython/gui_core/pyedit.py
index 2de2d45b066..5958ffb6d68 100644
--- a/gui/wxpython/gui_core/pyedit.py
+++ b/gui/wxpython/gui_core/pyedit.py
@@ -395,7 +395,7 @@ def SaveAs(self):
dlg = wx.FileDialog(
parent=self.guiparent,
message=_("Choose file to save"),
- defaultDir=os.getcwd(),
+ defaultDir=str(Path.cwd()),
wildcard=_("Python script (*.py)|*.py"),
style=wx.FD_SAVE,
)
@@ -466,7 +466,7 @@ def Open(self):
dlg = wx.FileDialog(
parent=self.guiparent,
message=_("Open file"),
- defaultDir=os.getcwd(),
+ defaultDir=str(Path.cwd()),
wildcard=_("Python script (*.py)|*.py"),
style=wx.FD_OPEN,
)
diff --git a/gui/wxpython/gui_core/vselect.py b/gui/wxpython/gui_core/vselect.py
index 0ff30afaafd..16bb64b7a57 100644
--- a/gui/wxpython/gui_core/vselect.py
+++ b/gui/wxpython/gui_core/vselect.py
@@ -232,7 +232,7 @@ def AddVecInfo(self, vInfoDictTMP) -> bool:
if self._dialog:
self.slist.AddItem(vInfoDictTMP)
- return not len(self.selectedFeatures) == 0
+ return len(self.selectedFeatures) != 0
def _draw(self):
"""Call class 'VectorSelectHighlighter' to draw selected features"""
diff --git a/gui/wxpython/gui_core/widgets.py b/gui/wxpython/gui_core/widgets.py
index 0ec50553114..dc9041e3578 100644
--- a/gui/wxpython/gui_core/widgets.py
+++ b/gui/wxpython/gui_core/widgets.py
@@ -435,7 +435,6 @@ class NumTextCtrl(TextCtrl):
"""Class derived from wx.TextCtrl for numerical values only"""
def __init__(self, parent, **kwargs):
- ## self.precision = kwargs.pop('prec')
TextCtrl.__init__(
self, parent=parent, validator=NTCValidator(flag="DIGIT_ONLY"), **kwargs
)
@@ -753,7 +752,7 @@ def _validate(self, win):
if text:
try:
datetime.strptime(text, "%Y-%m-%d")
- except:
+ except ValueError:
self._notvalid()
return False
@@ -1567,7 +1566,7 @@ def _loadSettings(self):
return data
try:
- fd = open(self.settingsFile, "r")
+ fd = open(self.settingsFile)
except OSError:
return data
diff --git a/gui/wxpython/iclass/frame.py b/gui/wxpython/iclass/frame.py
index 69c18780249..606c364c76f 100644
--- a/gui/wxpython/iclass/frame.py
+++ b/gui/wxpython/iclass/frame.py
@@ -570,7 +570,7 @@ def OnZoomMenu(self, event):
def OnZoomToTraining(self, event):
"""Set preview display to match extents of training display"""
- if not self.MapWindow == self.GetSecondWindow():
+ if self.MapWindow != self.GetSecondWindow():
self.MapWindow = self.GetSecondWindow()
self.Map = self.GetSecondMap()
self.UpdateActive(self.GetSecondWindow())
@@ -583,7 +583,7 @@ def OnZoomToTraining(self, event):
def OnZoomToPreview(self, event):
"""Set preview display to match extents of training display"""
- if not self.MapWindow == self.GetFirstWindow():
+ if self.MapWindow != self.GetFirstWindow():
self.MapWindow = self.GetFirstWindow()
self.Map = self.GetFirstMap()
self.UpdateActive(self.GetFirstWindow())
diff --git a/gui/wxpython/iclass/g.gui.iclass.html b/gui/wxpython/iclass/g.gui.iclass.html
index f6169b1d8f6..dd789cd75b5 100644
--- a/gui/wxpython/iclass/g.gui.iclass.html
+++ b/gui/wxpython/iclass/g.gui.iclass.html
@@ -35,7 +35,6 @@
@@ -62,6 +61,13 @@ DESCRIPTION
By doing this, the user can see how much of the image
is likely to be put into the class associated with the signature.
+wxIClass can also import training areas defined in a vector layer. In that case the program expects the vector layer to have the following columns defined:
+
+ - cat: category value
+ - class: a string with the class name
+ - color: a color defined using format "RRR:GGG:BBB"
+
+
The spectral signatures are composed of region means and covariance matrices.
These region means and covariance matrices are used in
diff --git a/gui/wxpython/image2target/g.gui.image2target.py b/gui/wxpython/image2target/g.gui.image2target.py
index 97c67b20354..e2704dc371b 100755
--- a/gui/wxpython/image2target/g.gui.image2target.py
+++ b/gui/wxpython/image2target/g.gui.image2target.py
@@ -30,76 +30,9 @@
# % keyword: GCP
# %end
-##%option G_OPT_M_LOCATION
-##% key: source_project
-##% label: The name of the source project (has no projection)
-##% description: The source project (location) has no CRS
-###% section: source
-##% required: yes
-##%end
-
-##%option G_OPT_M_MAPSET
-##% key: source_mapset
-##% label: The name of the source mapset (has no projection)
-##% description: The name of the source mapset (has no projection)
-###% section: source
-##% required: yes
-##%end
-
-##%option G_OPT_I_GROUP
-##% key: source_group
-##% required: yes
-##% section: source
-##%end
-
-##%option G_OPT_R_INPUT
-##% key: source_image
-##% required: yes
-###% section: source
-##%end
-
-##%option G_OPT_R_INPUT
-##% key: target_image
-##% label: The name of the image that is already georeferenced used to find location of GCPs
-##% description: The name of the image that is already georeferenced used to find the location of GCPs
-###% section: target
-##% required: no
-##%end
-
-##%option
-##% key: camera
-##% type: string
-##% label: The name of the camera (generated in i.ortho.camera)
-##% description: The name of the camera (generated in i.ortho.camera)
-##% required: yes
-###% section: parameters
-##%end
-
-##%option
-##% key: order
-##% type: string
-##% label: The rectification order
-##% description: The rectification order
-##% required: yes
-##% answer: 1
-###% section: parameters
-##%end
-
-##%option
-##% key: extension
-##% type: string
-##% label: The name of the output files extension
-##% description: The name of the output files extension
-##% required: yes
-##% answer: _ii2t_out
-##% section: target
-##%end
-
"""
Module to run GCP management tool as stadalone application.
-
-@author Vaclav Petras (standalone module)
"""
import os
@@ -129,55 +62,10 @@ def main():
else:
os.environ["GRASS_RENDER_IMMEDIATE"] = "cairo"
- # if options["source_location"]:
- # src_loc = options["source_location"]
- # else:
- # gscript.fatal(_("No georeferenced source location provided"))
-
- # if options["source_mapset"]:
- # src_mpt = options["source_mapset"]
- # else:
- # gscript.fatal(_("No georeferenced source mapset provided"))
-
- # if options["source_group"]:
- # src_grp = options["source_group"]
- # else:
- # gscript.fatal(_("Please provide a source group name to process"))
-
- # if options['source_image']:
- # src_ras = options["source_image"]
- # else:
- # gscript.fatal(_("Please provide a source image map name to process"))
-
- # if options["target_image"]:
- # tgt_ras = options["target_image"]
- # else:
- # gscript.fatal(_("No georeferenced target map provided"))
-
- # if options["camera"]:
- # camera = options["camera"]
- # else:
- # gscript.fatal(_(
- # "Please provide a camera name (generated by i.ortho.camera)"
- # ))
-
- # if options["order"]:
- # order = options["order"]
- # else:
- # gscript.fatal(_("Please provive an order value"))
-
- # if options["extension"]:
- # extension = options["extension"]
- # else:
- # gscript.fatal(_("Please provide an output file extension"))
-
app = wx.App()
- # wizard = GCPWizard(parent=None, giface=StandaloneGrassInterface(),
- # srcloc=src_loc,srcmpt=src_mpt,srcgrp=src_grp,srcras=src_ras,
- # tgtras=tgt_ras,camera=camera, order=order, extension=extension)
+ GCPWizard(parent=None, giface=StandaloneGrassInterface())
- wizard = GCPWizard(parent=None, giface=StandaloneGrassInterface())
app.MainLoop()
diff --git a/gui/wxpython/image2target/ii2t_gis_set.py b/gui/wxpython/image2target/ii2t_gis_set.py
index 0f51d969e93..4e5a5aee200 100644
--- a/gui/wxpython/image2target/ii2t_gis_set.py
+++ b/gui/wxpython/image2target/ii2t_gis_set.py
@@ -27,6 +27,8 @@
import platform
import getpass
+from pathlib import Path
+
from core import globalvar
import wx
import wx.lib.mixins.listctrl as listmix
@@ -94,7 +96,7 @@ def __init__(self, parent=None, id=wx.ID_ANY, style=wx.DEFAULT_FRAME_STYLE):
self.hbitmap = wx.StaticBitmap(
self.panel, wx.ID_ANY, wx.Bitmap(name=name, type=wx.BITMAP_TYPE_PNG)
)
- except:
+ except Exception:
self.hbitmap = wx.StaticBitmap(
self.panel, wx.ID_ANY, BitmapFromImage(wx.EmptyImage(530, 150))
)
@@ -315,7 +317,7 @@ def _set_properties(self, version, revision):
if os.path.isdir(os.getenv("HOME")):
self.gisdbase = os.getenv("HOME")
else:
- self.gisdbase = os.getcwd()
+ self.gisdbase = str(Path.cwd())
try:
self.tgisdbase.SetValue(self.gisdbase)
except UnicodeDecodeError:
@@ -541,7 +543,7 @@ def _readGisRC(self):
if gisrc and os.path.isfile(gisrc):
try:
- rc = open(gisrc, "r")
+ rc = open(gisrc)
for line in rc:
try:
key, val = line.split(":", 1)
@@ -822,8 +824,8 @@ def DeleteMapset(self, event):
shutil.rmtree(os.path.join(self.gisdbase, location, mapset))
self.OnSelectLocation(None)
self.lbmapsets.SetSelection(0)
- except:
- wx.MessageBox(message=_("Unable to delete mapset"))
+ except OSError as e:
+ wx.MessageBox(message=_("Unable to delete mapset: %s") % str(e))
dlg.Destroy()
@@ -854,8 +856,8 @@ def DeleteLocation(self, event):
self.lblocations.SetSelection(0)
self.OnSelectLocation(None)
self.lbmapsets.SetSelection(0)
- except:
- wx.MessageBox(message=_("Unable to delete location"))
+ except OSError as e:
+ wx.MessageBox(message=_("Unable to delete location: %s") % str(e))
dlg.Destroy()
@@ -1161,7 +1163,7 @@ def _getDefaultMapsetName(self):
defaultName = getpass.getuser()
# raise error if not ascii (not valid mapset name)
defaultName.encode("ascii")
- except: # whatever might go wrong
+ except Exception: # whatever might go wrong
defaultName = "user"
return defaultName
diff --git a/gui/wxpython/image2target/ii2t_manager.py b/gui/wxpython/image2target/ii2t_manager.py
index 17811ceceb1..3adcf5ef9bd 100644
--- a/gui/wxpython/image2target/ii2t_manager.py
+++ b/gui/wxpython/image2target/ii2t_manager.py
@@ -159,7 +159,7 @@ def __init__(self, parent, giface):
self.target_gisrc = os.environ["GISRC"]
self.gisrc_dict = {}
try:
- f = open(self.target_gisrc, "r")
+ f = open(self.target_gisrc)
for line in f:
line = line.replace("\n", "").strip()
if len(line) < 1:
@@ -884,20 +884,6 @@ def OnSrcSelection(self, event):
else:
wx.FindWindowById(wx.ID_FORWARD).Enable(True)
- try:
- # set computational region to match selected map and zoom display
- # to region
- if maptype == "raster":
- p = RunCommand("g.region", "raster=src_map")
- elif maptype == "vector":
- p = RunCommand("g.region", "vector=src_map")
-
- if p.returncode == 0:
- print("returncode = ", str(p.returncode))
- self.parent.Map.region = self.parent.Map.GetRegion()
- except:
- pass
-
def OnTgtRastSelection(self, event):
"""Source map to display selected"""
global tgt_map
@@ -1627,7 +1613,7 @@ def ReadGCPs(self):
GError(parent=self, message=_("target mapwin not defined"))
try:
- f = open(self.file["control_points"], "r")
+ f = open(self.file["control_points"])
GCPcnt = 0
for line in f:
@@ -1781,22 +1767,20 @@ def OnGeorect(self, event):
else:
flags = "a"
- busy = wx.BusyInfo(_("Rectifying images, please wait..."), parent=self)
- wx.GetApp().Yield()
-
- ret, msg = RunCommand(
- "i.ortho.rectify",
- parent=self,
- getErrorMsg=True,
- quiet=True,
- group=self.xygroup,
- extension=self.extension,
- method=self.gr_method,
- angle=self.grwiz.cam_angle,
- flags=flags,
- )
+ with wx.BusyInfo(_("Rectifying images, please wait..."), parent=self):
+ wx.GetApp().Yield()
- del busy
+ ret, msg = RunCommand(
+ "i.ortho.rectify",
+ parent=self,
+ getErrorMsg=True,
+ quiet=True,
+ group=self.xygroup,
+ extension=self.extension,
+ method=self.gr_method,
+ angle=self.grwiz.cam_angle,
+ flags=flags,
+ )
# provide feedback on failure
if ret != 0:
@@ -1828,23 +1812,21 @@ def OnGeorect(self, event):
)
ret = msg = ""
- busy = wx.BusyInfo(
+ with wx.BusyInfo(
_("Rectifying vector map <%s>, please wait...") % vect, parent=self
- )
- wx.GetApp().Yield()
-
- ret, msg = RunCommand(
- "v.rectify",
- parent=self,
- getErrorMsg=True,
- quiet=True,
- input=vect,
- output=self.outname,
- group=self.xygroup,
- order=self.gr_order,
- )
-
- del busy
+ ):
+ wx.GetApp().Yield()
+
+ ret, msg = RunCommand(
+ "v.rectify",
+ parent=self,
+ getErrorMsg=True,
+ quiet=True,
+ input=vect,
+ output=self.outname,
+ group=self.xygroup,
+ order=self.gr_order,
+ )
# provide feedback on failure
if ret != 0:
@@ -1971,7 +1953,6 @@ def OnGROrder(self, event):
elif self.gr_order == 2:
minNumOfItems = 6
- diff = 6 - numOfItems
# self.SetStatusText(_(
# "Insufficient points, 6+ points needed for 2nd order"))
@@ -2245,7 +2226,7 @@ def AdjustMap(self, newreg):
def OnZoomToSource(self, event):
"""Set target map window to match extents of source map window"""
- if not self.MapWindow == self.TgtMapWindow:
+ if self.MapWindow != self.TgtMapWindow:
self.MapWindow = self.TgtMapWindow
self.Map = self.TgtMap
self.UpdateActive(self.TgtMapWindow)
@@ -2258,7 +2239,7 @@ def OnZoomToSource(self, event):
def OnZoomToTarget(self, event):
"""Set source map window to match extents of target map window"""
- if not self.MapWindow == self.SrcMapWindow:
+ if self.MapWindow != self.SrcMapWindow:
self.MapWindow = self.SrcMapWindow
self.Map = self.SrcMap
self.UpdateActive(self.SrcMapWindow)
@@ -2270,7 +2251,6 @@ def OnZoomToTarget(self, event):
def OnZoomMenuGCP(self, event):
"""Popup Zoom menu"""
- point = wx.GetMousePosition()
zoommenu = Menu()
# Add items to the menu
@@ -3282,7 +3262,7 @@ def OnSrcSelection(self, event):
tmp_map = self.srcselection.GetValue()
- if not tmp_map == "" and not tmp_map == src_map:
+ if tmp_map not in ("", src_map):
self.new_src_map = tmp_map
def OnTgtRastSelection(self, event):
@@ -3376,7 +3356,6 @@ def UpdateSettings(self):
srcrenderVector = False
tgtrender = False
tgtrenderVector = False
- reload_target = False
if self.new_src_map != src_map:
# remove old layer
layers = self.parent.grwiz.SrcMap.GetListOfLayers()
@@ -3414,7 +3393,6 @@ def UpdateSettings(self):
del layers[0]
layers = self.parent.grwiz.TgtMap.GetListOfLayers()
# self.parent.grwiz.TgtMap.DeleteAllLayers()
- reload_target = True
tgt_map["raster"] = self.new_tgt_map["raster"]
tgt_map["vector"] = self.new_tgt_map["vector"]
diff --git a/gui/wxpython/image2target/ii2t_mapdisplay.py b/gui/wxpython/image2target/ii2t_mapdisplay.py
index b85a70a152f..9ee3b77d118 100644
--- a/gui/wxpython/image2target/ii2t_mapdisplay.py
+++ b/gui/wxpython/image2target/ii2t_mapdisplay.py
@@ -474,7 +474,6 @@ def PrintMenu(self, event):
"""
Print options and output menu for map display
"""
- point = wx.GetMousePosition()
printmenu = Menu()
# Add items to the menu
setup = wx.MenuItem(printmenu, wx.ID_ANY, _("Page setup"))
@@ -518,7 +517,6 @@ def SaveDisplayRegion(self, event):
def OnZoomMenu(self, event):
"""Popup Zoom menu"""
- point = wx.GetMousePosition()
zoommenu = Menu()
# Add items to the menu
@@ -567,7 +565,7 @@ def GetMapToolbar(self):
return self.toolbars["gcpdisp"]
def _setActiveMapWindow(self, mapWindow):
- if not self.MapWindow == mapWindow:
+ if self.MapWindow != mapWindow:
self.MapWindow = mapWindow
self.Map = mapWindow.Map
self.UpdateActive(mapWindow)
diff --git a/gui/wxpython/image2target/ii2t_statusbar.py b/gui/wxpython/image2target/ii2t_statusbar.py
index 3c5ee378a1e..7ff80e621d3 100644
--- a/gui/wxpython/image2target/ii2t_statusbar.py
+++ b/gui/wxpython/image2target/ii2t_statusbar.py
@@ -11,9 +11,6 @@
This program is free software under the GNU General Public License
(>=v2). Read the file COPYING that comes with GRASS for details.
-
-@author Vaclav Petras (statusbar refactoring)
-@author Anna Kratochvilova (statusbar refactoring)
"""
import wx
diff --git a/gui/wxpython/iscatt/controllers.py b/gui/wxpython/iscatt/controllers.py
index e8884f4f78b..aed917aacad 100644
--- a/gui/wxpython/iscatt/controllers.py
+++ b/gui/wxpython/iscatt/controllers.py
@@ -597,7 +597,7 @@ def _renderscattplts(self, scatt_ids, cats, cats_attrs):
try:
self.cat_ids.remove(c)
scatt_dt[c]["render"] = True
- except:
+ except ValueError:
scatt_dt[c]["render"] = False
if self.scatt_mgr.pol_sel_mode[0]:
@@ -674,7 +674,7 @@ def ChangePosition(self, cat_id, new_pos):
try:
pos = self.cats_ids.index(cat_id)
- except:
+ except ValueError:
return False
if pos > new_pos:
diff --git a/gui/wxpython/iscatt/core_c.py b/gui/wxpython/iscatt/core_c.py
index a2a3d5da9da..8be04964b90 100644
--- a/gui/wxpython/iscatt/core_c.py
+++ b/gui/wxpython/iscatt/core_c.py
@@ -11,17 +11,36 @@
@author Stepan Turek (mentor: Martin Landa)
"""
+import ctypes
import sys
-import numpy as np
+from ctypes import POINTER, c_char_p, c_double, c_int, c_uint8, pointer
from multiprocessing import Process, Queue
-from ctypes import *
+import numpy as np
try:
- from grass.lib.imagery import *
from grass.lib.gis import G_get_window
+ from grass.lib.imagery import (
+ SC_SCATT_CONDITIONS,
+ SC_SCATT_DATA,
+ I_apply_colormap,
+ I_compute_scatts,
+ I_create_cat_rast,
+ I_insert_patch_to_cat_rast,
+ I_merge_arrays,
+ I_rasterize,
+ I_sc_add_cat,
+ I_sc_free_cats,
+ I_sc_init_cats,
+ I_sc_insert_scatt_data,
+ I_scd_init_scatt_data,
+ scdScattData,
+ struct_Cell_head,
+ struct_Range,
+ struct_scCats,
+ )
except ImportError as e:
- sys.stderr.write(_("Loading ctypes libs failed"))
+ sys.stderr.write(_("Loading ctypes libs failed: %s") % e)
from core.gcmd import GException
from grass.script import encode
@@ -238,7 +257,7 @@ def _getComputationStruct(cats, cats_rasts, cats_type, n_bands):
scatt_vals = scdScattData()
- c_void_p = ctypes.POINTER(ctypes.c_void_p)
+ c_void_p = POINTER(ctypes.c_void_p)
if cats_type == SC_SCATT_DATA:
vals[:] = 0
@@ -269,7 +288,6 @@ def _updateCatRastProcess(patch_rast, region, cat_rast, output_queue):
def _rasterize(polygon, rast, region, value, output_queue):
- pol_size = len(polygon) * 2
pol = np.array(polygon, dtype=float)
c_uint8_p = POINTER(c_uint8)
diff --git a/gui/wxpython/iscatt/dialogs.py b/gui/wxpython/iscatt/dialogs.py
index 3e2d7fa7a59..a2d054b64a4 100644
--- a/gui/wxpython/iscatt/dialogs.py
+++ b/gui/wxpython/iscatt/dialogs.py
@@ -90,8 +90,6 @@ def _layout(self):
border = wx.BoxSizer(wx.VERTICAL)
dialogSizer = wx.BoxSizer(wx.VERTICAL)
- regionSizer = wx.BoxSizer(wx.HORIZONTAL)
-
dialogSizer.Add(
self._addSelectSizer(title=self.band_1_label, sel=self.band_1_ch)
)
@@ -356,7 +354,6 @@ def __init__(
self.scatt_mgr = scatt_mgr
- maxValue = 1e8
self.parent = parent
self.settings = {}
diff --git a/gui/wxpython/iscatt/frame.py b/gui/wxpython/iscatt/frame.py
index 45f50622d2f..122aa053b9b 100644
--- a/gui/wxpython/iscatt/frame.py
+++ b/gui/wxpython/iscatt/frame.py
@@ -221,9 +221,6 @@ def __init__(self, parent, scatt_mgr, id=wx.ID_ANY):
self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPlotPaneClosed)
- dlgSize = (-1, 400)
- # self.SetBestSize(dlgSize)
- # self.SetInitialSize(dlgSize)
self.SetAutoLayout(1)
# fix goutput's pane size (required for Mac OSX)
# if self.gwindow:
@@ -249,7 +246,7 @@ def CursorPlotMove(self, x, y, scatt_id):
x = int(round(x))
y = int(round(y))
coords = True
- except:
+ except TypeError:
coords = False
pane = self._getPane(scatt_id)
diff --git a/gui/wxpython/iscatt/plots.py b/gui/wxpython/iscatt/plots.py
index 08afe5906ed..a29098e8bcd 100644
--- a/gui/wxpython/iscatt/plots.py
+++ b/gui/wxpython/iscatt/plots.py
@@ -168,7 +168,7 @@ def SetEmpty(self):
return self.polygon_drawer.SetEmpty()
def OnRelease(self, event):
- if not self.mode == "zoom":
+ if self.mode != "zoom":
return
self.zoom_rect.set_visible(False)
self.ZoomRectangle(event)
@@ -348,7 +348,7 @@ def ZoomWheel(self, event):
def ZoomRectangle(self, event):
# get the current x and y limits
- if not self.mode == "zoom":
+ if self.mode != "zoom":
return
if event.inaxes is None:
return
@@ -394,7 +394,7 @@ def OnCanvasLeave(self, event):
def PanMotion(self, event):
"on mouse movement"
- if not self.mode == "pan":
+ if self.mode != "pan":
return
if event.inaxes is None:
return
@@ -426,7 +426,7 @@ def PanMotion(self, event):
self.canvas.draw()
def ZoomRectMotion(self, event):
- if not self.mode == "zoom":
+ if self.mode != "zoom":
return
if event.inaxes is None:
return
@@ -811,11 +811,11 @@ def _deleteVertex(self, event):
coords = []
for i, tup in enumerate(self.pol.xy):
- if i == ind:
- continue
- elif i == 0 and ind == len(self.pol.xy) - 1:
- continue
- elif i == len(self.pol.xy) - 1 and ind == 0:
+ if (
+ i == ind
+ or (i == 0 and ind == len(self.pol.xy) - 1)
+ or (i == len(self.pol.xy) - 1 and ind == 0)
+ ):
continue
coords.append(tup)
@@ -866,7 +866,7 @@ def _addVertex(self, event):
def motion_notify_callback(self, event):
"on mouse movement"
- if not self.mode == "move_vertex":
+ if self.mode != "move_vertex":
return
if not self.showverts:
return
diff --git a/gui/wxpython/lmgr/frame.py b/gui/wxpython/lmgr/frame.py
index a17b168f747..4eae371f68b 100644
--- a/gui/wxpython/lmgr/frame.py
+++ b/gui/wxpython/lmgr/frame.py
@@ -24,6 +24,8 @@
import platform
import re
+from pathlib import Path
+
from core import globalvar
import wx
import wx.aui
@@ -848,7 +850,7 @@ def OnRunModel(self, event):
dlg = wx.FileDialog(
parent=self,
message=_("Choose model to run"),
- defaultDir=os.getcwd(),
+ defaultDir=str(Path.cwd()),
wildcard=_("GRASS Model File (*.gxm)|*.gxm"),
)
if dlg.ShowModal() == wx.ID_OK:
@@ -1223,7 +1225,7 @@ def OnRunScript(self, event):
dlg = wx.FileDialog(
parent=self,
message=_("Choose script file to run"),
- defaultDir=os.getcwd(),
+ defaultDir=str(Path.cwd()),
wildcard=_("Python script (*.py)|*.py|Bash script (*.sh)|*.sh"),
)
@@ -1377,9 +1379,7 @@ def write_beginning(parameter=None, command=None):
self._giface.WriteCmdLog(" ".join(command))
def write_changed():
- self._giface.WriteLog(
- _('Working directory changed to:\n"%s"') % os.getcwd()
- )
+ self._giface.WriteLog(_('Working directory changed to:\n"%s"') % Path.cwd())
def write_end():
self._giface.WriteCmdLog(" ")
@@ -1433,7 +1433,7 @@ def write_help():
dlg = wx.DirDialog(
parent=self,
message=_("Choose a working directory"),
- defaultPath=os.getcwd(),
+ defaultPath=str(Path.cwd()),
)
if dlg.ShowModal() == wx.ID_OK:
diff --git a/gui/wxpython/lmgr/workspace.py b/gui/wxpython/lmgr/workspace.py
index bb40469fa56..a593c19b19c 100644
--- a/gui/wxpython/lmgr/workspace.py
+++ b/gui/wxpython/lmgr/workspace.py
@@ -18,6 +18,8 @@
import xml.etree.ElementTree as ET
+from pathlib import Path
+
import wx
import wx.aui
@@ -103,7 +105,7 @@ def Open(self):
dlg = wx.FileDialog(
parent=self.lmgr,
message=_("Choose workspace file"),
- defaultDir=os.getcwd(),
+ defaultDir=str(Path.cwd()),
wildcard=_("GRASS Workspace File (*.gxw)|*.gxw"),
)
@@ -362,7 +364,7 @@ def SaveAs(self):
dlg = wx.FileDialog(
parent=self.lmgr,
message=_("Choose file to save current workspace"),
- defaultDir=os.getcwd(),
+ defaultDir=str(Path.cwd()),
wildcard=_("GRASS Workspace File (*.gxw)|*.gxw"),
style=wx.FD_SAVE,
)
diff --git a/gui/wxpython/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py
index 7ba03ebee04..28ce94fbc48 100644
--- a/gui/wxpython/location_wizard/wizard.py
+++ b/gui/wxpython/location_wizard/wizard.py
@@ -38,6 +38,8 @@
import locale
import functools
+from pathlib import Path
+
import wx
import wx.lib.mixins.listctrl as listmix
from core import globalvar
@@ -318,7 +320,10 @@ def OnChangeName(self, event):
def OnBrowse(self, event):
"""Choose GRASS data directory"""
dlg = wx.DirDialog(
- self, _("Choose GRASS data directory:"), os.getcwd(), wx.DD_DEFAULT_STYLE
+ self,
+ _("Choose GRASS data directory:"),
+ str(Path.cwd()),
+ wx.DD_DEFAULT_STYLE,
)
if dlg.ShowModal() == wx.ID_OK:
self.grassdatabase = dlg.GetPath()
@@ -1493,7 +1498,7 @@ def OnText(self, event):
def OnBrowse(self, event):
"""Choose file"""
dlg = wx.FileDialog(
- self, _("Select georeferenced file"), os.getcwd(), "", "*.*", wx.FD_OPEN
+ self, _("Select georeferenced file"), str(Path.cwd()), "", "*.*", wx.FD_OPEN
)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
@@ -1977,7 +1982,7 @@ def OnBrowse(self, event):
"""Define path for IAU code file"""
path = os.path.dirname(self.tfile.GetValue())
if not path:
- path = os.getcwd()
+ path = str(Path.cwd())
dlg = wx.FileDialog(
parent=self,
@@ -2538,7 +2543,7 @@ def __readData(self):
"""Get georeferencing information from tables in $GISBASE/etc/proj"""
# read projection and parameters
- f = open(os.path.join(globalvar.ETCDIR, "proj", "parms.table"), "r")
+ f = open(os.path.join(globalvar.ETCDIR, "proj", "parms.table"))
self.projections = {}
self.projdesc = {}
for line in f:
@@ -2561,7 +2566,7 @@ def __readData(self):
f.close()
# read datum definitions
- f = open(os.path.join(globalvar.ETCDIR, "proj", "datum.table"), "r")
+ f = open(os.path.join(globalvar.ETCDIR, "proj", "datum.table"))
self.datums = {}
paramslist = []
for line in f:
@@ -2579,7 +2584,7 @@ def __readData(self):
f.close()
# read Earth-based ellipsiod definitions
- f = open(os.path.join(globalvar.ETCDIR, "proj", "ellipse.table"), "r")
+ f = open(os.path.join(globalvar.ETCDIR, "proj", "ellipse.table"))
self.ellipsoids = {}
for line in f:
line = line.expandtabs(1)
@@ -2595,9 +2600,7 @@ def __readData(self):
f.close()
# read Planetary ellipsiod definitions
- f = open(
- os.path.join(globalvar.ETCDIR, "proj", "ellipse.table.solar.system"), "r"
- )
+ f = open(os.path.join(globalvar.ETCDIR, "proj", "ellipse.table.solar.system"))
self.planetary_ellipsoids = {}
for line in f:
line = line.expandtabs(1)
@@ -2613,7 +2616,7 @@ def __readData(self):
f.close()
# read projection parameter description and parsing table
- f = open(os.path.join(globalvar.ETCDIR, "proj", "desc.table"), "r")
+ f = open(os.path.join(globalvar.ETCDIR, "proj", "desc.table"))
self.paramdesc = {}
for line in f:
line = line.strip()
diff --git a/gui/wxpython/main_window/frame.py b/gui/wxpython/main_window/frame.py
index 3b9b7f50261..8fc8dc8dde7 100644
--- a/gui/wxpython/main_window/frame.py
+++ b/gui/wxpython/main_window/frame.py
@@ -25,6 +25,8 @@
import platform
import re
+from pathlib import Path
+
from core import globalvar
try:
@@ -971,7 +973,7 @@ def OnRunModel(self, event):
dlg = wx.FileDialog(
parent=self,
message=_("Choose model to run"),
- defaultDir=os.getcwd(),
+ defaultDir=str(Path.cwd()),
wildcard=_("GRASS Model File (*.gxm)|*.gxm"),
)
if dlg.ShowModal() == wx.ID_OK:
@@ -1374,7 +1376,7 @@ def OnRunScript(self, event):
dlg = wx.FileDialog(
parent=self,
message=_("Choose script file to run"),
- defaultDir=os.getcwd(),
+ defaultDir=str(Path.cwd()),
wildcard=_("Python script (*.py)|*.py|Bash script (*.sh)|*.sh"),
)
@@ -1528,9 +1530,7 @@ def write_beginning(parameter=None, command=None):
self._giface.WriteCmdLog(" ".join(command))
def write_changed():
- self._giface.WriteLog(
- _('Working directory changed to:\n"%s"') % os.getcwd()
- )
+ self._giface.WriteLog(_('Working directory changed to:\n"%s"') % Path.cwd())
def write_end():
self._giface.WriteCmdLog(" ")
@@ -1584,7 +1584,7 @@ def write_help():
dlg = wx.DirDialog(
parent=self,
message=_("Choose a working directory"),
- defaultPath=os.getcwd(),
+ defaultPath=str(Path.cwd()),
)
if dlg.ShowModal() == wx.ID_OK:
diff --git a/gui/wxpython/mapdisp/frame.py b/gui/wxpython/mapdisp/frame.py
index 0b948f63794..f13ffbe0a81 100644
--- a/gui/wxpython/mapdisp/frame.py
+++ b/gui/wxpython/mapdisp/frame.py
@@ -831,7 +831,7 @@ def _DToRastDone():
overwrite=overwrite,
getErrorMsg=True,
)
- if not returncode == 0:
+ if returncode != 0:
self._giface.WriteError(_("Failed to run d.to.rast:\n") + messages)
return
# set region for composite
@@ -839,7 +839,7 @@ def _DToRastDone():
returncode, messages = RunCommand(
"g.region", raster=tmpName + ".red", quiet=True, getErrorMsg=True
)
- if not returncode == 0:
+ if returncode != 0:
gs.del_temp_region()
self._giface.WriteError(_("Failed to run d.to.rast:\n") + messages)
return
@@ -862,7 +862,7 @@ def _DToRastDone():
quiet=True,
name=[tmpName + ".red", tmpName + ".green", tmpName + ".blue"],
)
- if not returncode == 0:
+ if returncode != 0:
self._giface.WriteError(_("Failed to run d.to.rast:\n") + messages)
gs.try_remove(pngFile)
return
diff --git a/gui/wxpython/mapdisp/main.py b/gui/wxpython/mapdisp/main.py
index 1e94d3d8ba2..016261d248d 100644
--- a/gui/wxpython/mapdisp/main.py
+++ b/gui/wxpython/mapdisp/main.py
@@ -108,7 +108,7 @@ def __init__(self, giface, cmdfile=None, mapfile=None):
self.renderMgr = RenderMapMgr(self)
# update legend file variable with the one d.mon uses
- with open(monFile["env"], "r") as f:
+ with open(monFile["env"]) as f:
lines = f.readlines()
for line in lines:
if "GRASS_LEGEND_FILE" in line:
@@ -123,7 +123,7 @@ def GetLayersFromCmdFile(self):
nlayers = 0
try:
- fd = open(self.cmdfile, "r")
+ fd = open(self.cmdfile)
lines = fd.readlines()
fd.close()
# detect d.out.file, delete the line from the cmd file and export
diff --git a/gui/wxpython/mapwin/buffered.py b/gui/wxpython/mapwin/buffered.py
index e0442ed94ae..d8c6413017b 100644
--- a/gui/wxpython/mapwin/buffered.py
+++ b/gui/wxpython/mapwin/buffered.py
@@ -28,6 +28,8 @@
import wx
+from operator import itemgetter
+
from grass.pydispatch.signal import Signal
from core.globalvar import wxPythonPhoenix
@@ -462,10 +464,10 @@ def Draw(
brush = wx.TRANSPARENT_BRUSH
pdc.SetBrush(brush)
pdc.DrawPolygon(points=coords)
- x = min(coords, key=lambda x: x[0])[0]
- y = min(coords, key=lambda x: x[1])[1]
- w = max(coords, key=lambda x: x[0])[0] - x
- h = max(coords, key=lambda x: x[1])[1] - y
+ x = min(coords, key=itemgetter(0))[0]
+ y = min(coords, key=itemgetter(1))[1]
+ w = max(coords, key=itemgetter(0))[0] - x
+ h = max(coords, key=itemgetter(1))[1] - y
pdc.SetIdBounds(drawid, Rect(x, y, w, h))
elif pdctype == "circle": # draw circle
diff --git a/gui/wxpython/mapwin/decorations.py b/gui/wxpython/mapwin/decorations.py
index 7ab010a1d3f..24db63d8492 100644
--- a/gui/wxpython/mapwin/decorations.py
+++ b/gui/wxpython/mapwin/decorations.py
@@ -209,9 +209,7 @@ def CmdIsValid(self) -> bool:
inputs = 0
for param in self._cmd[1:]:
param = param.split("=")
- if len(param) == 1:
- inputs += 1
- elif param[0] == "text" and len(param) == 2:
+ if len(param) == 1 or (param[0] == "text" and len(param) == 2):
inputs += 1
return inputs >= 1
@@ -313,11 +311,11 @@ def CmdIsValid(self) -> bool:
inputs = 0
for param in self._cmd[1:]:
param = param.split("=")
- if len(param) == 1:
- inputs += 1
- elif param[0] == "raster" and len(param) == 2:
- inputs += 1
- elif param[0] == "raster_3d" and len(param) == 2:
+ if (
+ len(param) == 1
+ or (param[0] == "raster" and len(param) == 2)
+ or (param[0] == "raster_3d" and len(param) == 2)
+ ):
inputs += 1
return inputs == 1
diff --git a/gui/wxpython/modules/colorrules.py b/gui/wxpython/modules/colorrules.py
index b5b1047382e..cad74f38c4b 100644
--- a/gui/wxpython/modules/colorrules.py
+++ b/gui/wxpython/modules/colorrules.py
@@ -27,6 +27,8 @@
import copy
import tempfile
+from pathlib import Path
+
import wx
import wx.lib.colourselect as csel
import wx.lib.scrolledpanel as scrolled
@@ -448,7 +450,7 @@ def _createFileSelection(self, parent):
dialogTitle=_("Choose file to load color table"),
buttonText=_("Load"),
toolTip=_("Type filename or click to choose file and load color table"),
- startDirectory=os.getcwd(),
+ startDirectory=str(Path.cwd()),
fileMode=wx.FD_OPEN,
changeCallback=self.OnLoadRulesFile,
)
@@ -460,7 +462,7 @@ def _createFileSelection(self, parent):
dialogTitle=_("Choose file to save color table"),
toolTip=_("Type filename or click to choose file and save color table"),
buttonText=_("Save"),
- startDirectory=os.getcwd(),
+ startDirectory=str(Path.cwd()),
fileMode=wx.FD_SAVE,
changeCallback=self.OnSaveRulesFile,
)
@@ -698,7 +700,7 @@ def OnLoadRulesFile(self, event):
self.rulesPanel.Clear()
- fd = open(path, "r")
+ fd = open(path)
self.ReadColorTable(ctable=fd.read())
fd.close()
diff --git a/gui/wxpython/modules/import_export.py b/gui/wxpython/modules/import_export.py
index 15d4f76b586..03d179ffc2f 100644
--- a/gui/wxpython/modules/import_export.py
+++ b/gui/wxpython/modules/import_export.py
@@ -22,6 +22,8 @@
import os
+from pathlib import Path
+
import wx
from core import globalvar
import wx.lib.filebrowsebutton as filebrowse
@@ -836,7 +838,7 @@ def __init__(self, parent, giface):
labelText="",
dialogTitle=_("Choose DXF file to import"),
buttonText=_("Browse"),
- startDirectory=os.getcwd(),
+ startDirectory=str(Path.cwd()),
fileMode=0,
changeCallback=self.OnSetDsn,
fileMask="DXF File (*.dxf)|*.dxf",
diff --git a/gui/wxpython/modules/mcalc_builder.py b/gui/wxpython/modules/mcalc_builder.py
index e157375b4a4..b0f2f280409 100644
--- a/gui/wxpython/modules/mcalc_builder.py
+++ b/gui/wxpython/modules/mcalc_builder.py
@@ -759,7 +759,7 @@ def OnLoadExpression(self, event):
return
try:
- fobj = open(path, "r")
+ fobj = open(path)
mctxt = fobj.read()
finally:
fobj.close()
diff --git a/gui/wxpython/nviz/mapwindow.py b/gui/wxpython/nviz/mapwindow.py
index eaf0d266d4e..550939da7ce 100644
--- a/gui/wxpython/nviz/mapwindow.py
+++ b/gui/wxpython/nviz/mapwindow.py
@@ -534,7 +534,7 @@ def OnKeyDown(self, event):
Used for fly-through mode.
"""
- if not self.mouse["use"] == "fly":
+ if self.mouse["use"] != "fly":
return
key = event.GetKeyCode()
@@ -596,7 +596,7 @@ def OnKeyUp(self, event):
Used for fly-through mode.
"""
- if not self.mouse["use"] == "fly":
+ if self.mouse["use"] != "fly":
return
key = event.GetKeyCode()
diff --git a/gui/wxpython/nviz/tools.py b/gui/wxpython/nviz/tools.py
index a1b0a9fcb1b..0659c5cc2ba 100644
--- a/gui/wxpython/nviz/tools.py
+++ b/gui/wxpython/nviz/tools.py
@@ -23,6 +23,8 @@
import sys
import copy
+from pathlib import Path
+
import wx
import wx.lib.colourselect as csel
import wx.lib.scrolledpanel as SP
@@ -668,7 +670,7 @@ def _createAnimationPage(self):
vSizer = wx.BoxSizer(wx.VERTICAL)
gridSizer = wx.GridBagSizer(vgap=5, hgap=10)
- pwd = os.getcwd()
+ pwd = str(Path.cwd())
dir = filebrowse.DirBrowseButton(
parent=panel,
id=wx.ID_ANY,
diff --git a/gui/wxpython/nviz/wxnviz.py b/gui/wxpython/nviz/wxnviz.py
index 8002d6f32d2..e80a9fc9382 100644
--- a/gui/wxpython/nviz/wxnviz.py
+++ b/gui/wxpython/nviz/wxnviz.py
@@ -83,7 +83,7 @@ def print_progress(value):
"""Redirect progress info"""
global progress
if progress:
- if not progress.GetRange() == 100:
+ if progress.GetRange() != 100:
progress.SetRange(100)
progress.SetValue(value)
else:
diff --git a/gui/wxpython/photo2image/g.gui.photo2image.py b/gui/wxpython/photo2image/g.gui.photo2image.py
index 21635175374..65b4141873e 100755
--- a/gui/wxpython/photo2image/g.gui.photo2image.py
+++ b/gui/wxpython/photo2image/g.gui.photo2image.py
@@ -68,7 +68,6 @@
"""
Module to run GCP management tool as stadalone application.
-@author Vaclav Petras (standalone module)
"""
import os
import grass.script as gs
diff --git a/gui/wxpython/photo2image/ip2i_manager.py b/gui/wxpython/photo2image/ip2i_manager.py
index f251a1fa36e..5a2a1f14f21 100644
--- a/gui/wxpython/photo2image/ip2i_manager.py
+++ b/gui/wxpython/photo2image/ip2i_manager.py
@@ -118,7 +118,7 @@ def __init__(
self.source_gisrc = os.environ["GISRC"]
self.gisrc_dict = {}
try:
- f = open(self.target_gisrc, "r")
+ f = open(self.target_gisrc)
for line in f:
line = line.replace("\n", "").strip()
if len(line) < 1:
@@ -429,7 +429,7 @@ def __init__(
import re
try:
- fc = open(self.file["camera"], mode="r")
+ fc = open(self.file["camera"])
fc_count = 0
for line in fc:
fc_count += 1
@@ -438,7 +438,7 @@ def __init__(
numberOfFiducial = int(line.split()[-1])
dataFiducialX = []
dataFiducialY = []
- fc = open(self.file["camera"], mode="r")
+ fc = open(self.file["camera"])
fc_count = 0
for line in fc:
fc_count += 1
@@ -955,7 +955,7 @@ def ReadGCPs(self):
GError(parent=self, message=_("target mapwin not defined"))
try:
- f = open(self.file["points"], "r")
+ f = open(self.file["points"])
GCPcnt = 0
for line in f:
@@ -1531,7 +1531,7 @@ def AdjustMap(self, newreg):
def OnZoomToSource(self, event):
"""Set target map window to match extents of source map window"""
- if not self.MapWindow == self.TgtMapWindow:
+ if self.MapWindow != self.TgtMapWindow:
self.MapWindow = self.TgtMapWindow
self.Map = self.TgtMap
self.UpdateActive(self.TgtMapWindow)
@@ -1544,7 +1544,7 @@ def OnZoomToSource(self, event):
def OnZoomToTarget(self, event):
"""Set source map window to match extents of target map window"""
- if not self.MapWindow == self.SrcMapWindow:
+ if self.MapWindow != self.SrcMapWindow:
self.MapWindow = self.SrcMapWindow
self.Map = self.SrcMap
self.UpdateActive(self.SrcMapWindow)
@@ -2381,7 +2381,7 @@ def OnSrcSelection(self, event):
tmp_map = self.srcselection.GetValue()
- if not tmp_map == "" and not tmp_map == src_map:
+ if tmp_map not in ("", src_map):
self.new_src_map = tmp_map
def OnTgtRastSelection(self, event):
diff --git a/gui/wxpython/photo2image/ip2i_mapdisplay.py b/gui/wxpython/photo2image/ip2i_mapdisplay.py
index f85be35ec4a..aa085f2b43a 100644
--- a/gui/wxpython/photo2image/ip2i_mapdisplay.py
+++ b/gui/wxpython/photo2image/ip2i_mapdisplay.py
@@ -559,7 +559,7 @@ def GetMapToolbar(self):
return self.toolbars["gcpdisp"]
def _setActiveMapWindow(self, mapWindow):
- if not self.MapWindow == mapWindow:
+ if self.MapWindow != mapWindow:
self.MapWindow = mapWindow
self.Map = mapWindow.Map
self.UpdateActive(mapWindow)
diff --git a/gui/wxpython/photo2image/ip2i_statusbar.py b/gui/wxpython/photo2image/ip2i_statusbar.py
index 336dcd6903f..75a39b8141e 100644
--- a/gui/wxpython/photo2image/ip2i_statusbar.py
+++ b/gui/wxpython/photo2image/ip2i_statusbar.py
@@ -11,9 +11,6 @@
This program is free software under the GNU General Public License
(>=v2). Read the file COPYING that comes with GRASS for details.
-
-@author Vaclav Petras (statusbar refactoring)
-@author Anna Kratochvilova (statusbar refactoring)
"""
import wx
diff --git a/gui/wxpython/psmap/dialogs.py b/gui/wxpython/psmap/dialogs.py
index 216782037be..41493795893 100644
--- a/gui/wxpython/psmap/dialogs.py
+++ b/gui/wxpython/psmap/dialogs.py
@@ -37,6 +37,8 @@
import os
import string
from copy import deepcopy
+from operator import itemgetter
+from pathlib import Path
import wx
import wx.lib.agw.floatspin as fs
@@ -3622,9 +3624,7 @@ def _vectorLegend(self, notebook):
self.vectorListCtrl.InsertColumn(0, _("Vector map"))
self.vectorListCtrl.InsertColumn(1, _("Label"))
if self.vectorId:
- vectors = sorted(
- self.instruction[self.vectorId]["list"], key=lambda x: x[3]
- )
+ vectors = sorted(self.instruction[self.vectorId]["list"], key=itemgetter(3))
for vector in vectors:
index = self.vectorListCtrl.InsertItem(
@@ -4455,7 +4455,7 @@ def updateDialog(self):
if self.instruction.FindInstructionByType("vector"):
vectors = sorted(
self.instruction.FindInstructionByType("vector")["list"],
- key=lambda x: x[3],
+ key=itemgetter(3),
)
self.vectorListCtrl.DeleteAllItems()
for vector in vectors:
@@ -5965,7 +5965,7 @@ def OnPositionType(self, event):
def _getImageDirectory(self):
"""Default image directory"""
- return os.getcwd()
+ return str(Path.cwd())
def _addConvergence(self, panel, gridBagSizer):
pass
diff --git a/gui/wxpython/psmap/frame.py b/gui/wxpython/psmap/frame.py
index 3effc33cd4d..fa4a150aa3b 100644
--- a/gui/wxpython/psmap/frame.py
+++ b/gui/wxpython/psmap/frame.py
@@ -308,7 +308,7 @@ def OnPsMapDialog(self, event):
def OnPDFFile(self, event):
"""Generate PDF from PS with ps2pdf if available"""
- if not sys.platform == "win32":
+ if sys.platform != "win32":
try:
p = gs.Popen(["ps2pdf"], stderr=gs.PIPE)
p.stderr.close()
diff --git a/gui/wxpython/psmap/instructions.py b/gui/wxpython/psmap/instructions.py
index 8a535b96704..3c1b2e6d7bb 100644
--- a/gui/wxpython/psmap/instructions.py
+++ b/gui/wxpython/psmap/instructions.py
@@ -86,10 +86,7 @@ def __getitem__(self, id):
def __contains__(self, id):
"""Test if instruction is included"""
- for each in self.instruction:
- if each.id == id:
- return True
- return False
+ return any(each.id == id for each in self.instruction)
def __delitem__(self, id):
"""Delete instruction"""
@@ -1211,7 +1208,7 @@ def GetImageOrigSize(self, imagePath):
# if eps, read info from header
if os.path.splitext(fileName)[1].lower() == ".eps":
bbInfo = "%%BoundingBox"
- file = open(imagePath, "r")
+ file = open(imagePath)
w = h = 0
while file:
line = file.readline()
diff --git a/gui/wxpython/rdigit/dialogs.py b/gui/wxpython/rdigit/dialogs.py
index 650b75feb63..c632a84c0fe 100644
--- a/gui/wxpython/rdigit/dialogs.py
+++ b/gui/wxpython/rdigit/dialogs.py
@@ -114,7 +114,7 @@ def OnOK(self, event):
caption=_("Overwrite?"),
style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION,
)
- if not dlgOverwrite.ShowModal() == wx.ID_YES:
+ if dlgOverwrite.ShowModal() != wx.ID_YES:
dlgOverwrite.Destroy()
return
else:
diff --git a/gui/wxpython/timeline/frame.py b/gui/wxpython/timeline/frame.py
index 3982034619c..9a783f4f5d5 100644
--- a/gui/wxpython/timeline/frame.py
+++ b/gui/wxpython/timeline/frame.py
@@ -22,6 +22,7 @@
import wx
from functools import reduce
+from operator import add
try:
import matplotlib as mpl
@@ -495,9 +496,7 @@ def _checkDatasets(self, datasets):
]
# flatten this list
if allDatasets:
- allDatasets = reduce(
- lambda x, y: x + y, reduce(lambda x, y: x + y, allDatasets)
- )
+ allDatasets = reduce(add, reduce(add, allDatasets))
mapsets = tgis.get_tgis_c_library_interface().available_mapsets()
allDatasets = [
i
diff --git a/gui/wxpython/tplot/frame.py b/gui/wxpython/tplot/frame.py
index db21da4cd14..8994485dacc 100755
--- a/gui/wxpython/tplot/frame.py
+++ b/gui/wxpython/tplot/frame.py
@@ -20,6 +20,7 @@
"""
import os
from itertools import cycle
+from pathlib import Path
import numpy as np
import wx
@@ -68,6 +69,7 @@
from gui_core.widgets import GNotebook
from gui_core.wrap import CheckBox, TextCtrl, Button, StaticText
+from operator import add
ALPHA = 0.5
COLORS = ["b", "g", "r", "c", "m", "y", "k"]
@@ -371,7 +373,7 @@ def _layout(self):
labelText="",
dialogTitle=_("CVS path"),
buttonText=_("Browse"),
- startDirectory=os.getcwd(),
+ startDirectory=str(Path.cwd()),
fileMode=wx.FD_SAVE,
)
self.headerLabel = StaticText(
@@ -1152,9 +1154,7 @@ def _checkDatasets(self, datasets, typ):
]
# flatten this list
if allDatasets:
- allDatasets = reduce(
- lambda x, y: x + y, reduce(lambda x, y: x + y, allDatasets)
- )
+ allDatasets = reduce(add, reduce(add, allDatasets))
mapsets = tgis.get_tgis_c_library_interface().available_mapsets()
allDatasets = [
i
diff --git a/gui/wxpython/vdigit/wxdigit.py b/gui/wxpython/vdigit/wxdigit.py
index ddcdc5a2755..154d5ab7c81 100644
--- a/gui/wxpython/vdigit/wxdigit.py
+++ b/gui/wxpython/vdigit/wxdigit.py
@@ -1084,7 +1084,7 @@ def EditLine(self, line, coords):
# apply snapping (node or vertex)
snap = self._getSnapMode()
if snap != NO_SNAP:
- modeSnap = not (snap == SNAP)
+ modeSnap = snap != SNAP
Vedit_snap_line(
self.poMapInfo,
self.popoBgMapInfo,
@@ -1889,7 +1889,7 @@ def _addFeature(self, ftype, coords, layer, cat, snap, threshold):
if snap != NO_SNAP:
# apply snapping (node or vertex)
- modeSnap = not (snap == SNAP)
+ modeSnap = snap != SNAP
Vedit_snap_line(
self.poMapInfo,
self.popoBgMapInfo,
diff --git a/gui/wxpython/vdigit/wxdisplay.py b/gui/wxpython/vdigit/wxdisplay.py
index b747f3fa44f..9085aae8caf 100644
--- a/gui/wxpython/vdigit/wxdisplay.py
+++ b/gui/wxpython/vdigit/wxdisplay.py
@@ -868,10 +868,7 @@ def GetSelectedVertex(self, pos):
pos[0], pos[1], 0.0, points.x[idx], points.y[idx], points.z[idx], 0
)
- if idx == 0:
- minDist = dist
- Gid = idx
- elif minDist > dist:
+ if idx == 0 or minDist > dist:
minDist = dist
Gid = idx
diff --git a/gui/wxpython/vnet/dialogs.py b/gui/wxpython/vnet/dialogs.py
index 3d829ffd7be..a445b01e8eb 100644
--- a/gui/wxpython/vnet/dialogs.py
+++ b/gui/wxpython/vnet/dialogs.py
@@ -1952,9 +1952,7 @@ def SetVirtualData(self, row, column, text):
text = DegreesToRadians(text)
# Tested allowed range of values
- if text > math.pi:
- text = 0.0
- elif text < -math.pi:
+ if text > math.pi or text < -math.pi:
text = 0.0
self.data.SetValue(text, row, column)
diff --git a/gui/wxpython/vnet/vnet_core.py b/gui/wxpython/vnet/vnet_core.py
index 17d85ef6ca4..ac7625a4c22 100644
--- a/gui/wxpython/vnet/vnet_core.py
+++ b/gui/wxpython/vnet/vnet_core.py
@@ -828,9 +828,7 @@ def _prepareCmd(self, cmd):
if c.find("=") == -1:
continue
v = c.split("=")
- if len(v) != 2:
- cmd.remove(c)
- elif not v[1].strip():
+ if len(v) != 2 or not v[1].strip():
cmd.remove(c)
def _setCmdForSpecificAn(self, cmdParams):
diff --git a/gui/wxpython/vnet/vnet_data.py b/gui/wxpython/vnet/vnet_data.py
index 6404fcac5c0..9c5b592e043 100644
--- a/gui/wxpython/vnet/vnet_data.py
+++ b/gui/wxpython/vnet/vnet_data.py
@@ -1070,7 +1070,7 @@ def GetLastModified(self):
"head",
)
try:
- head = open(headPath, "r")
+ head = open(headPath)
for line in head:
i = line.find(
"MAP DATE:",
diff --git a/gui/wxpython/vnet/widgets.py b/gui/wxpython/vnet/widgets.py
index 12477cc93ea..ed1f2aa514c 100644
--- a/gui/wxpython/vnet/widgets.py
+++ b/gui/wxpython/vnet/widgets.py
@@ -542,7 +542,7 @@ def IsShown(self, colName) -> bool:
:return: False - if is not shown
"""
- return not self._getColumnNum(colName) == -1
+ return self._getColumnNum(colName) != -1
class EditItem(wx.Dialog):
diff --git a/gui/wxpython/wxplot/profile.py b/gui/wxpython/wxplot/profile.py
index 1d3de6681af..61efdcd67d0 100644
--- a/gui/wxpython/wxplot/profile.py
+++ b/gui/wxpython/wxplot/profile.py
@@ -20,6 +20,8 @@
import math
import numpy as np
+from pathlib import Path
+
import wx
from wx.lib import plot
@@ -410,7 +412,7 @@ def SaveProfileToFile(self, event):
dlg = wx.FileDialog(
parent=self,
message=_("Choose prefix for file(s) where to save profile values..."),
- defaultDir=os.getcwd(),
+ defaultDir=str(Path.cwd()),
wildcard=_("Comma separated value (*.csv)|*.csv"),
style=wx.FD_SAVE,
)
diff --git a/imagery/i.atcorr/create_iwave.py b/imagery/i.atcorr/create_iwave.py
index 8afc6bebd36..a575d8089cd 100644
--- a/imagery/i.atcorr/create_iwave.py
+++ b/imagery/i.atcorr/create_iwave.py
@@ -59,7 +59,7 @@ def read_input(csvfile):
first column is wavelength
values are those of the discrete band filter functions
"""
- infile = open(csvfile, "r")
+ infile = open(csvfile)
# get number of bands and band names
bands = infile.readline().split(",")
diff --git a/imagery/i.gensig/testsuite/test_i_gensig.py b/imagery/i.gensig/testsuite/test_i_gensig.py
index 0498cabaf79..d75e3da2af4 100644
--- a/imagery/i.gensig/testsuite/test_i_gensig.py
+++ b/imagery/i.gensig/testsuite/test_i_gensig.py
@@ -13,6 +13,7 @@
import stat
import ctypes
import shutil
+from pathlib import Path
from grass.pygrass import utils
from grass.pygrass.gis import Mapset
@@ -107,7 +108,7 @@ def test_creation(self):
)
# File must be present
- sig_stat = os.stat(f"{self.sig_dir1}/sig")
+ sig_stat = Path(self.sig_dir1, "sig").stat()
self.assertTrue(stat.S_ISREG(sig_stat.st_mode))
# Compare values within sig file
diff --git a/imagery/i.maxlik/testsuite/test_i_maxlik.py b/imagery/i.maxlik/testsuite/test_i_maxlik.py
index 1f6829e92b4..e7fbf6c1a79 100644
--- a/imagery/i.maxlik/testsuite/test_i_maxlik.py
+++ b/imagery/i.maxlik/testsuite/test_i_maxlik.py
@@ -19,6 +19,7 @@
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
+from grass.gunittest.utils import xfail_windows
from grass.lib.gis import G_mapset_path
from grass.lib.raster import Rast_write_semantic_label
@@ -172,6 +173,7 @@ def tearDownClass(cls):
)
cls.runModule("g.remove", flags="f", type="group", name=cls.group, quiet=True)
+ @xfail_windows
def test_v1(self):
"""Test v1 signature"""
self.assertModule(
@@ -193,6 +195,7 @@ def test_v1(self):
self.assertEqual(res.get_cat(0)[1], 1)
res.close()
+ @xfail_windows
def test_v2(self):
"""Test v2 signature"""
self.assertModule(
diff --git a/include/grass/defs/raster.h b/include/grass/defs/raster.h
index 7244da2a354..c2d26ccdccc 100644
--- a/include/grass/defs/raster.h
+++ b/include/grass/defs/raster.h
@@ -393,6 +393,7 @@ int Rast_option_to_interp_type(const struct Option *);
/* mask_info.c */
char *Rast_mask_info(void);
int Rast__mask_info(char *, char *);
+bool Rast_mask_is_present(void);
/* maskfd.c */
int Rast_maskfd(void);
diff --git a/lib/external/shapelib/README.md b/lib/external/shapelib/README.md
index e004d2d0419..d446eccd014 100644
--- a/lib/external/shapelib/README.md
+++ b/lib/external/shapelib/README.md
@@ -1,12 +1,13 @@
# Update history of SHAPELIB copy
-* files `shpopen.c`, `shapefil.h`, `dbfopen.c`
+* files `shpopen.c`, `shapefil.h`, `dbfopen.c`, `shapefil_private.h`
from GDAL [ogr/ogrsf_frmts/shape/](https://github.com/OSGeo/gdal/tree/master/ogr/ogrsf_frmts/shape)
* file `safileio.c`
from [SHAPELIB](http://download.osgeo.org/shapelib/)
## Last update
+* taken from GDAL 3.9.2 and SHAPELIB 1.6.0 (Sep 2024)
* taken from GDAL 3.5.3 and SHAPELIB 1.5.0 (Dec 2022)
* taken from GDAL 2.1.2 and SHAPELIB 1.3.0 (Thu Nov 24 10:45:41 CET 2016)
* taken from GDAL 1.5.1-SVN (Sun Mar 30 11:20:43 CEST 2008)
@@ -15,44 +16,5 @@
## Summary of fixes
-* dbfopen.c
- around line 1229: GDAL bug [ticket-#809](http://trac.osgeo.org/gdal/ticket/809)
-
* safileio.c
- SHP_CVSID: ISO C does not allow extra ‘;’ outside of a function
-
-## Full fix
-
-```diff
-diff --git a/lib/external/shapelib/dbfopen.c b/lib/external/shapelib/dbfopen.c
-index 5380e3e20b..5151148d33 100644
---- a/lib/external/shapelib/dbfopen.c
-+++ b/lib/external/shapelib/dbfopen.c
-@@ -1226,9 +1226,10 @@ DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName,
- else if( psDBF->pachFieldType[iField] == 'N'
- || psDBF->pachFieldType[iField] == 'F' )
- {
-- if( psDBF->panFieldDecimals[iField] > 0
-- || psDBF->panFieldSize[iField] >= 10 )
-+ if( psDBF->panFieldDecimals[iField] > 0 ) {
-+ /* || psDBF->panFieldSize[iField] >= 10 ) */ /* GDAL bug #809 */
- return( FTDouble );
-+ }
- else
- return( FTInteger );
- }
-diff --git a/lib/external/shapelib/safileio.c b/lib/external/shapelib/safileio.c
-index 289d347eaf..7a614a5806 100644
---- a/lib/external/shapelib/safileio.c
-+++ b/lib/external/shapelib/safileio.c
-@@ -74,7 +74,7 @@
- #include
- #include
-
--SHP_CVSID("$Id: safileio.c,v 1.6 2018-06-15 19:56:32 erouault Exp $");
-+SHP_CVSID("$Id: safileio.c,v 1.6 2018-06-15 19:56:32 erouault Exp $")
-
- #ifdef SHPAPI_UTF8_HOOKS
- # ifdef SHPAPI_WINDOWS
-
-```
+ [shapelib commit 316ff87](https://github.com/OSGeo/shapelib/commit/316ff872566ea0d91d6b62fe01bfe39931db39aa#diff-f068bc465ca1a32e1b9c214d4eb9504ef9e0f3c4cabc1aa4bab8aa41e2248cc6R153)
diff --git a/lib/external/shapelib/dbfopen.c b/lib/external/shapelib/dbfopen.c
index 2bbba339e21..9078a29ba95 100644
--- a/lib/external/shapelib/dbfopen.c
+++ b/lib/external/shapelib/dbfopen.c
@@ -1,5 +1,4 @@
/******************************************************************************
- * $Id$
*
* Project: Shapelib
* Purpose: Implementation of .dbf access API documented in dbf_api.html.
@@ -9,32 +8,10 @@
* Copyright (c) 1999, Frank Warmerdam
* Copyright (c) 2012-2019, Even Rouault
*
- * This software is available under the following "MIT Style" license,
- * or at the option of the licensee under the LGPL (see COPYING). This
- * option is discussed in more detail in shapelib.html.
- *
- * --
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+ * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
******************************************************************************/
-#include "shapefil.h"
+#include "shapefil_private.h"
#include
#include
@@ -47,7 +24,9 @@
#include "cpl_string.h"
#else
-#if defined(WIN32) || defined(_WIN32)
+#if defined(_MSC_VER)
+#define STRCASECMP(a, b) (_stricmp(a, b))
+#elif defined(_WIN32)
#define STRCASECMP(a, b) (stricmp(a, b))
#else
#include
@@ -58,7 +37,7 @@
#if _MSC_VER < 1900
#define snprintf _snprintf
#endif
-#elif defined(WIN32) || defined(_WIN32)
+#elif defined(_WIN32)
#ifndef snprintf
#define snprintf _snprintf
#endif
@@ -68,8 +47,6 @@
#define CPLsnprintf snprintf
#endif
-SHP_CVSID("$Id$")
-
#ifndef FALSE
#define FALSE 0
#define TRUE 1
@@ -91,33 +68,6 @@ CPL_INLINE static void CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused)
#define CPL_IGNORE_RET_VAL_INT(x) x
#endif
-#ifdef __cplusplus
-#define STATIC_CAST(type, x) static_cast(x)
-#define REINTERPRET_CAST(type, x) reinterpret_cast(x)
-#define CONST_CAST(type, x) const_cast(x)
-#define SHPLIB_NULLPTR nullptr
-#else
-#define STATIC_CAST(type, x) ((type)(x))
-#define REINTERPRET_CAST(type, x) ((type)(x))
-#define CONST_CAST(type, x) ((type)(x))
-#define SHPLIB_NULLPTR NULL
-#endif
-
-/************************************************************************/
-/* SfRealloc() */
-/* */
-/* A realloc cover function that will access a NULL pointer as */
-/* a valid input. */
-/************************************************************************/
-
-static void *SfRealloc(void *pMem, int nNewSize)
-{
- if (pMem == SHPLIB_NULLPTR)
- return malloc(nNewSize);
- else
- return realloc(pMem, nNewSize);
-}
-
/************************************************************************/
/* DBFWriteHeader() */
/* */
@@ -137,9 +87,9 @@ static void DBFWriteHeader(DBFHandle psDBF)
psDBF->bNoHeader = FALSE;
/* -------------------------------------------------------------------- */
- /* Initialize the file header information. */
+ /* Initialize the file header information. */
/* -------------------------------------------------------------------- */
- abyHeader[0] = 0x03; /* memo field? - just copying */
+ abyHeader[0] = 0x03; /* memo field? - just copying */
/* write out update date */
abyHeader[1] = STATIC_CAST(unsigned char, psDBF->nUpdateYearSince1900);
@@ -158,7 +108,7 @@ static void DBFWriteHeader(DBFHandle psDBF)
/* -------------------------------------------------------------------- */
/* Write the initial 32 byte file header, and all the field */
- /* descriptions. */
+ /* descriptions. */
/* -------------------------------------------------------------------- */
psDBF->sHooks.FSeek(psDBF->fp, 0, 0);
psDBF->sHooks.FWrite(abyHeader, XBASE_FILEHDR_SZ, 1, psDBF->fp);
@@ -344,7 +294,6 @@ void SHPAPI_CALL DBFSetLastModifiedDate(DBFHandle psDBF, int nYYSince1900,
/************************************************************************/
DBFHandle SHPAPI_CALL DBFOpen(const char *pszFilename, const char *pszAccess)
-
{
SAHooks sHooks;
@@ -376,7 +325,7 @@ static int DBFGetLenWithoutExtension(const char *pszBasename)
/************************************************************************/
DBFHandle SHPAPI_CALL DBFOpenLL(const char *pszFilename, const char *pszAccess,
- SAHooks *psHooks)
+ const SAHooks *psHooks)
{
/* -------------------------------------------------------------------- */
/* We only allow the access strings "rb" and "r+". */
@@ -393,8 +342,8 @@ DBFHandle SHPAPI_CALL DBFOpenLL(const char *pszFilename, const char *pszAccess,
pszAccess = "rb+";
/* -------------------------------------------------------------------- */
- /* Compute the base (layer) name. If there is any extension */
- /* on the passed in filename we will strip it off. */
+ /* Compute the base (layer) name. If there is any extension */
+ /* on the passed in filename we will strip it off. */
/* -------------------------------------------------------------------- */
const int nLenWithoutExtension = DBFGetLenWithoutExtension(pszFilename);
char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5));
@@ -402,19 +351,20 @@ DBFHandle SHPAPI_CALL DBFOpenLL(const char *pszFilename, const char *pszAccess,
memcpy(pszFullname + nLenWithoutExtension, ".dbf", 5);
DBFHandle psDBF = STATIC_CAST(DBFHandle, calloc(1, sizeof(DBFInfo)));
- psDBF->fp = psHooks->FOpen(pszFullname, pszAccess);
+ psDBF->fp = psHooks->FOpen(pszFullname, pszAccess, psHooks->pvUserData);
memcpy(&(psDBF->sHooks), psHooks, sizeof(SAHooks));
if (psDBF->fp == SHPLIB_NULLPTR) {
memcpy(pszFullname + nLenWithoutExtension, ".DBF", 5);
- psDBF->fp = psDBF->sHooks.FOpen(pszFullname, pszAccess);
+ psDBF->fp =
+ psDBF->sHooks.FOpen(pszFullname, pszAccess, psHooks->pvUserData);
}
memcpy(pszFullname + nLenWithoutExtension, ".cpg", 5);
- SAFile pfCPG = psHooks->FOpen(pszFullname, "r");
+ SAFile pfCPG = psHooks->FOpen(pszFullname, "r", psHooks->pvUserData);
if (pfCPG == SHPLIB_NULLPTR) {
memcpy(pszFullname + nLenWithoutExtension, ".CPG", 5);
- pfCPG = psHooks->FOpen(pszFullname, "r");
+ pfCPG = psHooks->FOpen(pszFullname, "r", psHooks->pvUserData);
}
free(pszFullname);
@@ -495,7 +445,7 @@ DBFHandle SHPAPI_CALL DBFOpenLL(const char *pszFilename, const char *pszAccess,
/* -------------------------------------------------------------------- */
/* Read in Field Definitions */
/* -------------------------------------------------------------------- */
- pabyBuf = STATIC_CAST(unsigned char *, SfRealloc(pabyBuf, nHeadLen));
+ pabyBuf = STATIC_CAST(unsigned char *, realloc(pabyBuf, nHeadLen));
psDBF->pszHeader = REINTERPRET_CAST(char *, pabyBuf);
psDBF->sHooks.FSeek(psDBF->fp, XBASE_FILEHDR_SZ, 0);
@@ -583,8 +533,8 @@ void SHPAPI_CALL DBFClose(DBFHandle psDBF)
CPL_IGNORE_RET_VAL_INT(DBFFlushRecord(psDBF));
/* -------------------------------------------------------------------- */
- /* Update last access date, and number of records if we have */
- /* write access. */
+ /* Update last access date, and number of records if we have */
+ /* write access. */
/* -------------------------------------------------------------------- */
if (psDBF->bUpdated)
DBFUpdateHeader(psDBF);
@@ -645,11 +595,12 @@ DBFHandle SHPAPI_CALL DBFCreateEx(const char *pszFilename,
/************************************************************************/
DBFHandle SHPAPI_CALL DBFCreateLL(const char *pszFilename,
- const char *pszCodePage, SAHooks *psHooks)
+ const char *pszCodePage,
+ const SAHooks *psHooks)
{
/* -------------------------------------------------------------------- */
- /* Compute the base (layer) name. If there is any extension */
- /* on the passed in filename we will strip it off. */
+ /* Compute the base (layer) name. If there is any extension */
+ /* on the passed in filename we will strip it off. */
/* -------------------------------------------------------------------- */
const int nLenWithoutExtension = DBFGetLenWithoutExtension(pszFilename);
char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5));
@@ -659,17 +610,7 @@ DBFHandle SHPAPI_CALL DBFCreateLL(const char *pszFilename,
/* -------------------------------------------------------------------- */
/* Create the file. */
/* -------------------------------------------------------------------- */
- SAFile fp = psHooks->FOpen(pszFullname, "wb");
- if (fp == SHPLIB_NULLPTR) {
- free(pszFullname);
- return SHPLIB_NULLPTR;
- }
-
- char chZero = '\0';
- psHooks->FWrite(&chZero, 1, 1, fp);
- psHooks->FClose(fp);
-
- fp = psHooks->FOpen(pszFullname, "rb+");
+ SAFile fp = psHooks->FOpen(pszFullname, "wb+", psHooks->pvUserData);
if (fp == SHPLIB_NULLPTR) {
free(pszFullname);
return SHPLIB_NULLPTR;
@@ -685,7 +626,8 @@ DBFHandle SHPAPI_CALL DBFCreateLL(const char *pszFilename,
// a valid one
}
if (ldid < 0) {
- SAFile fpCPG = psHooks->FOpen(pszFullname, "w");
+ SAFile fpCPG =
+ psHooks->FOpen(pszFullname, "w", psHooks->pvUserData);
psHooks->FWrite(
CONST_CAST(void *, STATIC_CAST(const void *, pszCodePage)),
strlen(pszCodePage), 1, fpCPG);
@@ -693,13 +635,13 @@ DBFHandle SHPAPI_CALL DBFCreateLL(const char *pszFilename,
}
}
if (pszCodePage == SHPLIB_NULLPTR || ldid >= 0) {
- psHooks->Remove(pszFullname);
+ psHooks->Remove(pszFullname, psHooks->pvUserData);
}
free(pszFullname);
/* -------------------------------------------------------------------- */
- /* Create the info structure. */
+ /* Create the info structure. */
/* -------------------------------------------------------------------- */
DBFHandle psDBF = STATIC_CAST(DBFHandle, calloc(1, sizeof(DBFInfo)));
@@ -829,23 +771,22 @@ int SHPAPI_CALL DBFAddNativeFieldType(DBFHandle psDBF, const char *pszFieldName,
const int nOldHeaderLength = psDBF->nHeaderLength;
/* -------------------------------------------------------------------- */
- /* SfRealloc all the arrays larger to hold the additional field */
+ /* realloc all the arrays larger to hold the additional field */
/* information. */
/* -------------------------------------------------------------------- */
psDBF->nFields++;
psDBF->panFieldOffset = STATIC_CAST(
- int *, SfRealloc(psDBF->panFieldOffset, sizeof(int) * psDBF->nFields));
+ int *, realloc(psDBF->panFieldOffset, sizeof(int) * psDBF->nFields));
psDBF->panFieldSize = STATIC_CAST(
- int *, SfRealloc(psDBF->panFieldSize, sizeof(int) * psDBF->nFields));
+ int *, realloc(psDBF->panFieldSize, sizeof(int) * psDBF->nFields));
- psDBF->panFieldDecimals =
- STATIC_CAST(int *, SfRealloc(psDBF->panFieldDecimals,
- sizeof(int) * psDBF->nFields));
+ psDBF->panFieldDecimals = STATIC_CAST(
+ int *, realloc(psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields));
psDBF->pachFieldType = STATIC_CAST(
- char *, SfRealloc(psDBF->pachFieldType, sizeof(char) * psDBF->nFields));
+ char *, realloc(psDBF->pachFieldType, sizeof(char) * psDBF->nFields));
/* -------------------------------------------------------------------- */
/* Assign the new field information fields. */
@@ -863,7 +804,7 @@ int SHPAPI_CALL DBFAddNativeFieldType(DBFHandle psDBF, const char *pszFieldName,
psDBF->bUpdated = FALSE;
psDBF->pszHeader = STATIC_CAST(
- char *, SfRealloc(psDBF->pszHeader, psDBF->nFields * XBASE_FLDHDR_SZ));
+ char *, realloc(psDBF->pszHeader, psDBF->nFields * XBASE_FLDHDR_SZ));
char *pszFInfo = psDBF->pszHeader + XBASE_FLDHDR_SZ * (psDBF->nFields - 1);
@@ -887,7 +828,7 @@ int SHPAPI_CALL DBFAddNativeFieldType(DBFHandle psDBF, const char *pszFieldName,
/* Make the current record buffer appropriately larger. */
/* -------------------------------------------------------------------- */
psDBF->pszCurrentRecord = STATIC_CAST(
- char *, SfRealloc(psDBF->pszCurrentRecord, psDBF->nRecordLength));
+ char *, realloc(psDBF->pszCurrentRecord, psDBF->nRecordLength));
/* we're done if dealing with new .dbf */
if (psDBF->bNoHeader)
@@ -971,13 +912,13 @@ static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField,
return SHPLIB_NULLPTR;
/* -------------------------------------------------------------------- */
- /* Have we read the record? */
+ /* Have we read the record? */
/* -------------------------------------------------------------------- */
if (!DBFLoadRecord(psDBF, hEntity))
return SHPLIB_NULLPTR;
- unsigned char *pabyRec =
- REINTERPRET_CAST(unsigned char *, psDBF->pszCurrentRecord);
+ const unsigned char *pabyRec =
+ REINTERPRET_CAST(const unsigned char *, psDBF->pszCurrentRecord);
/* -------------------------------------------------------------------- */
/* Ensure we have room to extract the target field. */
@@ -993,7 +934,7 @@ static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField,
}
/* -------------------------------------------------------------------- */
- /* Extract the requested field. */
+ /* Extract the requested field. */
/* -------------------------------------------------------------------- */
memcpy(psDBF->pszWorkField,
REINTERPRET_CAST(const char *, pabyRec) +
@@ -1085,7 +1026,6 @@ double SHPAPI_CALL DBFReadDoubleAttribute(DBFHandle psDBF, int iRecord,
const char SHPAPI_CALL1(*)
DBFReadStringAttribute(DBFHandle psDBF, int iRecord, int iField)
-
{
return STATIC_CAST(const char *,
DBFReadAttribute(psDBF, iRecord, iField, 'C'));
@@ -1099,12 +1039,40 @@ const char SHPAPI_CALL1(*)
const char SHPAPI_CALL1(*)
DBFReadLogicalAttribute(DBFHandle psDBF, int iRecord, int iField)
-
{
return STATIC_CAST(const char *,
DBFReadAttribute(psDBF, iRecord, iField, 'L'));
}
+/************************************************************************/
+/* DBFReadDateAttribute() */
+/* */
+/* Read a date attribute. */
+/************************************************************************/
+
+SHPDate SHPAPI_CALL DBFReadDateAttribute(DBFHandle psDBF, int iRecord,
+ int iField)
+{
+ const char *pdateValue = STATIC_CAST(
+ const char *, DBFReadAttribute(psDBF, iRecord, iField, 'D'));
+
+ SHPDate date;
+
+ if (pdateValue == SHPLIB_NULLPTR) {
+ date.year = 0;
+ date.month = 0;
+ date.day = 0;
+ }
+ else if (3 != sscanf(pdateValue, "%4d%2d%2d", &date.year, &date.month,
+ &date.day)) {
+ date.year = 0;
+ date.month = 0;
+ date.day = 0;
+ }
+
+ return date;
+}
+
/************************************************************************/
/* DBFIsValueNULL() */
/* */
@@ -1135,7 +1103,16 @@ static bool DBFIsValueNULL(char chType, const char *pszValue)
case 'D':
/* NULL date fields have value "00000000" */
- return strncmp(pszValue, "00000000", 8) == 0;
+ /* Some DBF files have fields filled with spaces */
+ /* (trimmed by DBFReadStringAttribute) to indicate null */
+ /* values for dates (#4265). */
+ /* And others have ' 0':
+ * https://lists.osgeo.org/pipermail/gdal-dev/2023-November/058010.html
+ */
+ /* And others just empty string:
+ * https://github.com/OSGeo/gdal/issues/10405 */
+ return pszValue[0] == 0 || strncmp(pszValue, "00000000", 8) == 0 ||
+ strcmp(pszValue, " ") == 0 || strcmp(pszValue, "0") == 0;
case 'L':
/* NULL boolean fields have value "?" */
@@ -1155,7 +1132,8 @@ static bool DBFIsValueNULL(char chType, const char *pszValue)
/* Contributed by Jim Matthews. */
/************************************************************************/
-int SHPAPI_CALL DBFIsAttributeNULL(DBFHandle psDBF, int iRecord, int iField)
+int SHPAPI_CALL DBFIsAttributeNULL(const DBFHandle psDBF, int iRecord,
+ int iField)
{
const char *pszValue = DBFReadStringAttribute(psDBF, iRecord, iField);
@@ -1171,8 +1149,7 @@ int SHPAPI_CALL DBFIsAttributeNULL(DBFHandle psDBF, int iRecord, int iField)
/* Return the number of fields in this table. */
/************************************************************************/
-int SHPAPI_CALL DBFGetFieldCount(DBFHandle psDBF)
-
+int SHPAPI_CALL DBFGetFieldCount(const DBFHandle psDBF)
{
return (psDBF->nFields);
}
@@ -1183,8 +1160,7 @@ int SHPAPI_CALL DBFGetFieldCount(DBFHandle psDBF)
/* Return the number of records in this table. */
/************************************************************************/
-int SHPAPI_CALL DBFGetRecordCount(DBFHandle psDBF)
-
+int SHPAPI_CALL DBFGetRecordCount(const DBFHandle psDBF)
{
return (psDBF->nRecords);
}
@@ -1197,10 +1173,9 @@ int SHPAPI_CALL DBFGetRecordCount(DBFHandle psDBF)
/* bytes long. */
/************************************************************************/
-DBFFieldType SHPAPI_CALL DBFGetFieldInfo(DBFHandle psDBF, int iField,
+DBFFieldType SHPAPI_CALL DBFGetFieldInfo(const DBFHandle psDBF, int iField,
char *pszFieldName, int *pnWidth,
int *pnDecimals)
-
{
if (iField < 0 || iField >= psDBF->nFields)
return (FTInvalid);
@@ -1230,10 +1205,9 @@ DBFFieldType SHPAPI_CALL DBFGetFieldInfo(DBFHandle psDBF, int iField,
else if (psDBF->pachFieldType[iField] == 'N' ||
psDBF->pachFieldType[iField] == 'F') {
- if (psDBF->panFieldDecimals[iField] > 0) {
- /* || psDBF->panFieldSize[iField] >= 10 ) */ /* GDAL bug #809 */
+ if (psDBF->panFieldDecimals[iField] > 0 ||
+ psDBF->panFieldSize[iField] >= 10)
return (FTDouble);
- }
else
return (FTInteger);
}
@@ -1244,15 +1218,15 @@ DBFFieldType SHPAPI_CALL DBFGetFieldInfo(DBFHandle psDBF, int iField,
/************************************************************************/
/* DBFWriteAttribute() */
-/* */
-/* Write an attribute record to the file. */
+/* */
+/* Write an attribute record to the file. */
/************************************************************************/
static bool DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField,
void *pValue)
{
/* -------------------------------------------------------------------- */
- /* Is this a valid record? */
+ /* Is this a valid record? */
/* -------------------------------------------------------------------- */
if (hEntity < 0 || hEntity > psDBF->nRecords)
return false;
@@ -1333,9 +1307,13 @@ static bool DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField,
case 'L':
if (psDBF->panFieldSize[iField] >= 1 &&
(*STATIC_CAST(char *, pValue) == 'F' ||
- *STATIC_CAST(char *, pValue) == 'T'))
+ *STATIC_CAST(char *, pValue) == 'T')) {
*(pabyRec + psDBF->panFieldOffset[iField]) =
*STATIC_CAST(char *, pValue);
+ }
+ else {
+ nRetResult = false;
+ }
break;
default: {
@@ -1370,10 +1348,10 @@ static bool DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField,
/************************************************************************/
int SHPAPI_CALL DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity,
- int iField, void *pValue)
+ int iField, const void *pValue)
{
/* -------------------------------------------------------------------- */
- /* Is this a valid record? */
+ /* Is this a valid record? */
/* -------------------------------------------------------------------- */
if (hEntity < 0 || hEntity > psDBF->nRecords)
return (FALSE);
@@ -1402,24 +1380,29 @@ int SHPAPI_CALL DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity,
if (!DBFLoadRecord(psDBF, hEntity))
return FALSE;
- unsigned char *pabyRec =
- REINTERPRET_CAST(unsigned char *, psDBF->pszCurrentRecord);
+ if (iField >= 0) {
+ unsigned char *pabyRec =
+ REINTERPRET_CAST(unsigned char *, psDBF->pszCurrentRecord);
- /* -------------------------------------------------------------------- */
- /* Assign all the record fields. */
- /* -------------------------------------------------------------------- */
- int j;
- if (STATIC_CAST(int, strlen(STATIC_CAST(char *, pValue))) >
- psDBF->panFieldSize[iField])
- j = psDBF->panFieldSize[iField];
- else {
- memset(pabyRec + psDBF->panFieldOffset[iField], ' ',
- psDBF->panFieldSize[iField]);
- j = STATIC_CAST(int, strlen(STATIC_CAST(char *, pValue)));
- }
+ /* --------------------------------------------------------------------
+ */
+ /* Assign all the record fields. */
+ /* --------------------------------------------------------------------
+ */
+ int j;
+ if (STATIC_CAST(int, strlen(STATIC_CAST(const char *, pValue))) >
+ psDBF->panFieldSize[iField])
+ j = psDBF->panFieldSize[iField];
+ else {
+ memset(pabyRec + psDBF->panFieldOffset[iField], ' ',
+ psDBF->panFieldSize[iField]);
+ j = STATIC_CAST(int, strlen(STATIC_CAST(const char *, pValue)));
+ }
- strncpy(REINTERPRET_CAST(char *, pabyRec + psDBF->panFieldOffset[iField]),
+ memcpy(
+ REINTERPRET_CAST(char *, pabyRec + psDBF->panFieldOffset[iField]),
STATIC_CAST(const char *, pValue), j);
+ }
psDBF->bCurrentRecordModified = TRUE;
psDBF->bUpdated = TRUE;
@@ -1443,7 +1426,7 @@ int SHPAPI_CALL DBFWriteDoubleAttribute(DBFHandle psDBF, int iRecord,
/************************************************************************/
/* DBFWriteIntegerAttribute() */
/* */
-/* Write a integer attribute. */
+/* Write an integer attribute. */
/************************************************************************/
int SHPAPI_CALL DBFWriteIntegerAttribute(DBFHandle psDBF, int iRecord,
@@ -1463,7 +1446,6 @@ int SHPAPI_CALL DBFWriteIntegerAttribute(DBFHandle psDBF, int iRecord,
int SHPAPI_CALL DBFWriteStringAttribute(DBFHandle psDBF, int iRecord,
int iField, const char *pszValue)
-
{
return (
DBFWriteAttribute(psDBF, iRecord, iField,
@@ -1473,11 +1455,10 @@ int SHPAPI_CALL DBFWriteStringAttribute(DBFHandle psDBF, int iRecord,
/************************************************************************/
/* DBFWriteNULLAttribute() */
/* */
-/* Write a string attribute. */
+/* Write a NULL attribute. */
/************************************************************************/
int SHPAPI_CALL DBFWriteNULLAttribute(DBFHandle psDBF, int iRecord, int iField)
-
{
return (DBFWriteAttribute(psDBF, iRecord, iField, SHPLIB_NULLPTR));
}
@@ -1490,23 +1471,47 @@ int SHPAPI_CALL DBFWriteNULLAttribute(DBFHandle psDBF, int iRecord, int iField)
int SHPAPI_CALL DBFWriteLogicalAttribute(DBFHandle psDBF, int iRecord,
int iField, const char lValue)
-
{
return (
DBFWriteAttribute(psDBF, iRecord, iField,
STATIC_CAST(void *, CONST_CAST(char *, &lValue))));
}
+/************************************************************************/
+/* DBFWriteDateAttribute() */
+/* */
+/* Write a date attribute. */
+/************************************************************************/
+
+int SHPAPI_CALL DBFWriteDateAttribute(DBFHandle psDBF, int iRecord, int iField,
+ const SHPDate *lValue)
+{
+ if (SHPLIB_NULLPTR == lValue)
+ return false;
+ /* check for supported digit range, but do not check for valid date */
+ if (lValue->year < 0 || lValue->year > 9999)
+ return false;
+ if (lValue->month < 0 || lValue->month > 99)
+ return false;
+ if (lValue->day < 0 || lValue->day > 99)
+ return false;
+ char dateValue[9]; /* "yyyyMMdd\0" */
+ snprintf(dateValue, sizeof(dateValue), "%04d%02d%02d", lValue->year,
+ lValue->month, lValue->day);
+ return (DBFWriteAttributeDirectly(psDBF, iRecord, iField, dateValue));
+}
+
/************************************************************************/
/* DBFWriteTuple() */
-/* */
-/* Write an attribute record to the file. */
+/* */
+/* Write an attribute record to the file. */
/************************************************************************/
-int SHPAPI_CALL DBFWriteTuple(DBFHandle psDBF, int hEntity, void *pRawTuple)
+int SHPAPI_CALL DBFWriteTuple(DBFHandle psDBF, int hEntity,
+ const void *pRawTuple)
{
/* -------------------------------------------------------------------- */
- /* Is this a valid record? */
+ /* Is this a valid record? */
/* -------------------------------------------------------------------- */
if (hEntity < 0 || hEntity > psDBF->nRecords)
return (FALSE);
@@ -1554,7 +1559,6 @@ int SHPAPI_CALL DBFWriteTuple(DBFHandle psDBF, int hEntity, void *pRawTuple)
/************************************************************************/
const char SHPAPI_CALL1(*) DBFReadTuple(DBFHandle psDBF, int hEntity)
-
{
if (hEntity < 0 || hEntity >= psDBF->nRecords)
return SHPLIB_NULLPTR;
@@ -1566,14 +1570,17 @@ const char SHPAPI_CALL1(*) DBFReadTuple(DBFHandle psDBF, int hEntity)
}
/************************************************************************/
-/* DBFCloneEmpty() */
+/* DBFCloneEmpty() */
/* */
-/* Read one of the attribute fields of a record. */
+/* Create a new .dbf file with same code page and field */
+/* definitions as the given handle. */
/************************************************************************/
-DBFHandle SHPAPI_CALL DBFCloneEmpty(DBFHandle psDBF, const char *pszFilename)
+DBFHandle SHPAPI_CALL DBFCloneEmpty(const DBFHandle psDBF,
+ const char *pszFilename)
{
- DBFHandle newDBF = DBFCreateEx(pszFilename, psDBF->pszCodePage);
+ DBFHandle newDBF =
+ DBFCreateLL(pszFilename, psDBF->pszCodePage, &psDBF->sHooks);
if (newDBF == SHPLIB_NULLPTR)
return SHPLIB_NULLPTR;
@@ -1629,8 +1636,7 @@ DBFHandle SHPAPI_CALL DBFCloneEmpty(DBFHandle psDBF, const char *pszFilename)
/* 'M' (Memo: 10 digits .DBT block ptr) */
/************************************************************************/
-char SHPAPI_CALL DBFGetNativeFieldType(DBFHandle psDBF, int iField)
-
+char SHPAPI_CALL DBFGetNativeFieldType(const DBFHandle psDBF, int iField)
{
if (iField >= 0 && iField < psDBF->nFields)
return psDBF->pachFieldType[iField];
@@ -1646,7 +1652,8 @@ char SHPAPI_CALL DBFGetNativeFieldType(DBFHandle psDBF, int iField)
/* Contributed by Jim Matthews. */
/************************************************************************/
-int SHPAPI_CALL DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName)
+int SHPAPI_CALL DBFGetFieldIndex(const DBFHandle psDBF,
+ const char *pszFieldName)
{
char name[XBASE_FLDNAME_LEN_READ + 1];
@@ -1665,7 +1672,7 @@ int SHPAPI_CALL DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName)
/* it returns FALSE. */
/************************************************************************/
-int SHPAPI_CALL DBFIsRecordDeleted(DBFHandle psDBF, int iShape)
+int SHPAPI_CALL DBFIsRecordDeleted(const DBFHandle psDBF, int iShape)
{
/* -------------------------------------------------------------------- */
/* Verify selection. */
@@ -1674,7 +1681,7 @@ int SHPAPI_CALL DBFIsRecordDeleted(DBFHandle psDBF, int iShape)
return TRUE;
/* -------------------------------------------------------------------- */
- /* Have we read the record? */
+ /* Have we read the record? */
/* -------------------------------------------------------------------- */
if (!DBFLoadRecord(psDBF, iShape))
return FALSE;
@@ -1727,7 +1734,7 @@ int SHPAPI_CALL DBFMarkRecordDeleted(DBFHandle psDBF, int iShape,
/* DBFGetCodePage */
/************************************************************************/
-const char SHPAPI_CALL1(*) DBFGetCodePage(DBFHandle psDBF)
+const char SHPAPI_CALL1(*) DBFGetCodePage(const DBFHandle psDBF)
{
if (psDBF == SHPLIB_NULLPTR)
return SHPLIB_NULLPTR;
@@ -1768,17 +1775,16 @@ int SHPAPI_CALL DBFDeleteField(DBFHandle psDBF, int iField)
psDBF->nFields--;
psDBF->panFieldOffset = STATIC_CAST(
- int *, SfRealloc(psDBF->panFieldOffset, sizeof(int) * psDBF->nFields));
+ int *, realloc(psDBF->panFieldOffset, sizeof(int) * psDBF->nFields));
psDBF->panFieldSize = STATIC_CAST(
- int *, SfRealloc(psDBF->panFieldSize, sizeof(int) * psDBF->nFields));
+ int *, realloc(psDBF->panFieldSize, sizeof(int) * psDBF->nFields));
- psDBF->panFieldDecimals =
- STATIC_CAST(int *, SfRealloc(psDBF->panFieldDecimals,
- sizeof(int) * psDBF->nFields));
+ psDBF->panFieldDecimals = STATIC_CAST(
+ int *, realloc(psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields));
psDBF->pachFieldType = STATIC_CAST(
- char *, SfRealloc(psDBF->pachFieldType, sizeof(char) * psDBF->nFields));
+ char *, realloc(psDBF->pachFieldType, sizeof(char) * psDBF->nFields));
/* update header information */
psDBF->nHeaderLength -= XBASE_FLDHDR_SZ;
@@ -1790,11 +1796,11 @@ int SHPAPI_CALL DBFDeleteField(DBFHandle psDBF, int iField)
sizeof(char) * (psDBF->nFields - iField) * XBASE_FLDHDR_SZ);
psDBF->pszHeader = STATIC_CAST(
- char *, SfRealloc(psDBF->pszHeader, psDBF->nFields * XBASE_FLDHDR_SZ));
+ char *, realloc(psDBF->pszHeader, psDBF->nFields * XBASE_FLDHDR_SZ));
/* update size of current record appropriately */
psDBF->pszCurrentRecord = STATIC_CAST(
- char *, SfRealloc(psDBF->pszCurrentRecord, psDBF->nRecordLength));
+ char *, realloc(psDBF->pszCurrentRecord, psDBF->nRecordLength));
/* we're done if we're dealing with not yet created .dbf */
if (psDBF->bNoHeader && psDBF->nRecords == 0)
@@ -1866,7 +1872,7 @@ int SHPAPI_CALL DBFDeleteField(DBFHandle psDBF, int iField)
/* code of DBFReorderFields. */
/************************************************************************/
-int SHPAPI_CALL DBFReorderFields(DBFHandle psDBF, int *panMap)
+int SHPAPI_CALL DBFReorderFields(DBFHandle psDBF, const int *panMap)
{
if (psDBF->nFields == 0)
return TRUE;
@@ -1878,13 +1884,13 @@ int SHPAPI_CALL DBFReorderFields(DBFHandle psDBF, int *panMap)
/* a simple malloc() would be enough, but calloc() helps clang static
* analyzer */
int *panFieldOffsetNew =
- STATIC_CAST(int *, calloc(sizeof(int), psDBF->nFields));
+ STATIC_CAST(int *, calloc(psDBF->nFields, sizeof(int)));
int *panFieldSizeNew =
- STATIC_CAST(int *, calloc(sizeof(int), psDBF->nFields));
+ STATIC_CAST(int *, calloc(psDBF->nFields, sizeof(int)));
int *panFieldDecimalsNew =
- STATIC_CAST(int *, calloc(sizeof(int), psDBF->nFields));
+ STATIC_CAST(int *, calloc(psDBF->nFields, sizeof(int)));
char *pachFieldTypeNew =
- STATIC_CAST(char *, calloc(sizeof(char), psDBF->nFields));
+ STATIC_CAST(char *, calloc(psDBF->nFields, sizeof(char)));
char *pszHeaderNew = STATIC_CAST(
char *, malloc(sizeof(char) * XBASE_FLDHDR_SZ * psDBF->nFields));
@@ -2050,7 +2056,7 @@ int SHPAPI_CALL DBFAlterFieldDefn(DBFHandle psDBF, int iField,
psDBF->nRecordLength += nWidth - nOldWidth;
psDBF->pszCurrentRecord = STATIC_CAST(
- char *, SfRealloc(psDBF->pszCurrentRecord, psDBF->nRecordLength));
+ char *, realloc(psDBF->pszCurrentRecord, psDBF->nRecordLength));
}
/* we're done if we're dealing with not yet created .dbf */
@@ -2069,7 +2075,6 @@ int SHPAPI_CALL DBFAlterFieldDefn(DBFHandle psDBF, int iField,
char *pszOldField =
STATIC_CAST(char *, malloc(sizeof(char) * (nOldWidth + 1)));
- /* cppcheck-suppress uninitdata */
pszOldField[nOldWidth] = 0;
/* move records to their new positions */
@@ -2139,7 +2144,6 @@ int SHPAPI_CALL DBFAlterFieldDefn(DBFHandle psDBF, int iField,
char *pszOldField =
STATIC_CAST(char *, malloc(sizeof(char) * (nOldWidth + 1)));
- /* cppcheck-suppress uninitdata */
pszOldField[nOldWidth] = 0;
/* move records to their new positions */
diff --git a/lib/external/shapelib/safileio.c b/lib/external/shapelib/safileio.c
index 3377642e4d4..e13f1f29acf 100644
--- a/lib/external/shapelib/safileio.c
+++ b/lib/external/shapelib/safileio.c
@@ -1,5 +1,4 @@
/******************************************************************************
- * $Id: safileio.c,v 1.6 2018-06-15 19:56:32 erouault Exp $
*
* Project: Shapelib
* Purpose: Default implementation of file io based on stdio.
@@ -8,74 +7,19 @@
******************************************************************************
* Copyright (c) 2007, Frank Warmerdam
*
- * This software is available under the following "MIT Style" license,
- * or at the option of the licensee under the LGPL (see COPYING). This
- * option is discussed in more detail in shapelib.html.
- *
- * --
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+ * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
******************************************************************************
*
- * $Log: safileio.c,v $
- * Revision 1.6 2018-06-15 19:56:32 erouault
- * * safileio.c: remove duplicate test. Patch by Jaroslav Fojtik.
- * Fixes http://bugzilla.maptools.org/show_bug.cgi?id=2744
- *
- * Revision 1.5 2016-12-05 12:44:05 erouault
- * * Major overhaul of Makefile build system to use autoconf/automake.
- *
- * * Warning fixes in contrib/
- *
- * Revision 1.4 2008-01-16 20:05:14 bram
- * Add file hooks that accept UTF-8 encoded filenames on some platforms. Use
- *SASetupUtf8Hooks tosetup the hooks and check SHPAPI_UTF8_HOOKS for its
- *availability. Currently, this is only available on the Windows platform that
- *decodes the UTF-8 filenames to wide character strings and feeds them to
- *_wfopen and _wremove.
- *
- * Revision 1.3 2007/12/18 18:28:11 bram
- * - create hook for client specific atof (bugzilla ticket 1615)
- * - check for NULL handle before closing cpCPG file, and close after reading.
- *
- * Revision 1.2 2007/12/15 20:25:30 bram
- * dbfopen.c now reads the Code Page information from the DBF file, and exports
- * this information as a string through the DBFGetCodePage function. This is
- * either the number from the LDID header field ("LDID/") or as the
- * content of an accompanying .CPG file. When creating a DBF file, the code can
- * be set using DBFCreateEx.
- *
- * Revision 1.1 2007/12/06 06:56:41 fwarmerdam
- * new
- *
*/
#include "shapefil.h"
+#include
#include
#include
-#include
+#include
#include
#include
-#include
-
-SHP_CVSID("$Id: safileio.c,v 1.6 2018-06-15 19:56:32 erouault Exp $")
#ifdef SHPAPI_UTF8_HOOKS
#ifdef SHPAPI_WINDOWS
@@ -86,102 +30,64 @@ SHP_CVSID("$Id: safileio.c,v 1.6 2018-06-15 19:56:32 erouault Exp $")
#endif
#endif
-/************************************************************************/
-/* SADFOpen() */
-/************************************************************************/
-
-SAFile SADFOpen(const char *pszFilename, const char *pszAccess)
-
+static SAFile SADFOpen(const char *pszFilename, const char *pszAccess,
+ void *pvUserData)
{
+ (void)pvUserData;
return (SAFile)fopen(pszFilename, pszAccess);
}
-/************************************************************************/
-/* SADFRead() */
-/************************************************************************/
-
-SAOffset SADFRead(void *p, SAOffset size, SAOffset nmemb, SAFile file)
-
+static SAOffset SADFRead(void *p, SAOffset size, SAOffset nmemb, SAFile file)
{
return (SAOffset)fread(p, (size_t)size, (size_t)nmemb, (FILE *)file);
}
-/************************************************************************/
-/* SADFWrite() */
-/************************************************************************/
-
-SAOffset SADFWrite(void *p, SAOffset size, SAOffset nmemb, SAFile file)
-
+static SAOffset SADFWrite(const void *p, SAOffset size, SAOffset nmemb,
+ SAFile file)
{
return (SAOffset)fwrite(p, (size_t)size, (size_t)nmemb, (FILE *)file);
}
-/************************************************************************/
-/* SADFSeek() */
-/************************************************************************/
-
-SAOffset SADFSeek(SAFile file, SAOffset offset, int whence)
-
+static SAOffset SADFSeek(SAFile file, SAOffset offset, int whence)
{
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+ return (SAOffset)_fseeki64((FILE *)file, (__int64)offset, whence);
+#else
return (SAOffset)fseek((FILE *)file, (long)offset, whence);
+#endif
}
-/************************************************************************/
-/* SADFTell() */
-/************************************************************************/
-
-SAOffset SADFTell(SAFile file)
-
+static SAOffset SADFTell(SAFile file)
{
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+ return (SAOffset)_ftelli64((FILE *)file);
+#else
return (SAOffset)ftell((FILE *)file);
+#endif
}
-/************************************************************************/
-/* SADFFlush() */
-/************************************************************************/
-
-int SADFFlush(SAFile file)
-
+static int SADFFlush(SAFile file)
{
return fflush((FILE *)file);
}
-/************************************************************************/
-/* SADFClose() */
-/************************************************************************/
-
-int SADFClose(SAFile file)
-
+static int SADFClose(SAFile file)
{
return fclose((FILE *)file);
}
-/************************************************************************/
-/* SADFClose() */
-/************************************************************************/
-
-int SADRemove(const char *filename)
-
+static int SADRemove(const char *filename, void *pvUserData)
{
+ (void)pvUserData;
return remove(filename);
}
-/************************************************************************/
-/* SADError() */
-/************************************************************************/
-
-void SADError(const char *message)
-
+static void SADError(const char *message)
{
fprintf(stderr, "%s\n", message);
}
-/************************************************************************/
-/* SASetupDefaultHooks() */
-/************************************************************************/
-
void SASetupDefaultHooks(SAHooks *psHooks)
-
{
psHooks->FOpen = SADFOpen;
psHooks->FRead = SADFRead;
@@ -194,25 +100,20 @@ void SASetupDefaultHooks(SAHooks *psHooks)
psHooks->Error = SADError;
psHooks->Atof = atof;
+ psHooks->pvUserData = NULL;
}
#ifdef SHPAPI_WINDOWS
-/************************************************************************/
-/* Utf8ToWideChar */
-/************************************************************************/
-
-const wchar_t *Utf8ToWideChar(const char *pszFilename)
+static wchar_t *Utf8ToWideChar(const char *pszFilename)
{
- int nMulti, nWide;
- wchar_t *pwszFileName;
-
- nMulti = strlen(pszFilename) + 1;
- nWide = MultiByteToWideChar(CP_UTF8, 0, pszFilename, nMulti, 0, 0);
+ const int nMulti = (int)strlen(pszFilename) + 1;
+ const int nWide =
+ MultiByteToWideChar(CP_UTF8, 0, pszFilename, nMulti, 0, 0);
if (nWide == 0) {
return NULL;
}
- pwszFileName = (wchar_t *)malloc(nWide * sizeof(wchar_t));
+ wchar_t *pwszFileName = (wchar_t *)malloc(nWide * sizeof(wchar_t));
if (pwszFileName == NULL) {
return NULL;
}
@@ -228,51 +129,44 @@ const wchar_t *Utf8ToWideChar(const char *pszFilename)
/* SAUtf8WFOpen */
/************************************************************************/
-SAFile SAUtf8WFOpen(const char *pszFilename, const char *pszAccess)
+static SAFile SAUtf8WFOpen(const char *pszFilename, const char *pszAccess,
+ void *pvUserData)
{
+ (void)pvUserData;
SAFile file = NULL;
- const wchar_t *pwszFileName, *pwszAccess;
- pwszFileName = Utf8ToWideChar(pszFilename);
- pwszAccess = Utf8ToWideChar(pszAccess);
+ wchar_t *pwszFileName = Utf8ToWideChar(pszFilename);
+ wchar_t *pwszAccess = Utf8ToWideChar(pszAccess);
if (pwszFileName != NULL && pwszAccess != NULL) {
file = (SAFile)_wfopen(pwszFileName, pwszAccess);
}
- free((wchar_t *)pwszFileName);
- free((wchar_t *)pwszAccess);
+ free(pwszFileName);
+ free(pwszAccess);
return file;
}
-/************************************************************************/
-/* SAUtf8WRemove() */
-/************************************************************************/
-
-int SAUtf8WRemove(const char *pszFilename)
+static int SAUtf8WRemove(const char *pszFilename, void *pvUserData)
{
- const wchar_t *pwszFileName = Utf8ToWideChar(pszFilename);
+ (void)pvUserData;
+ wchar_t *pwszFileName = Utf8ToWideChar(pszFilename);
int rc = -1;
if (pwszFileName != NULL) {
rc = _wremove(pwszFileName);
}
- free((wchar_t *)pwszFileName);
+ free(pwszFileName);
return rc;
}
#endif
#ifdef SHPAPI_UTF8_HOOKS
-
-/************************************************************************/
-/* SASetupUtf8Hooks() */
-/************************************************************************/
+#ifndef SHPAPI_WINDOWS
+#error "no implementations of UTF-8 hooks available for this platform"
+#endif
void SASetupUtf8Hooks(SAHooks *psHooks)
{
-#ifdef SHPAPI_WINDOWS
psHooks->FOpen = SAUtf8WFOpen;
psHooks->Remove = SAUtf8WRemove;
-#else
-#error "no implementations of UTF-8 hooks available for this platform"
-#endif
psHooks->FRead = SADFRead;
psHooks->FWrite = SADFWrite;
psHooks->FSeek = SADFSeek;
@@ -283,5 +177,4 @@ void SASetupUtf8Hooks(SAHooks *psHooks)
psHooks->Error = SADError;
psHooks->Atof = atof;
}
-
#endif
diff --git a/lib/external/shapelib/shapefil.h b/lib/external/shapelib/shapefil.h
index cd4ef54aa56..c11632fa6ba 100644
--- a/lib/external/shapelib/shapefil.h
+++ b/lib/external/shapelib/shapefil.h
@@ -2,7 +2,6 @@
#define SHAPEFILE_H_INCLUDED
/******************************************************************************
- * $Id$
*
* Project: Shapelib
* Purpose: Primary include file for Shapelib.
@@ -12,29 +11,7 @@
* Copyright (c) 1999, Frank Warmerdam
* Copyright (c) 2012-2016, Even Rouault
*
- * This software is available under the following "MIT Style" license,
- * or at the option of the licensee under the LGPL (see COPYING). This
- * option is discussed in more detail in shapelib.html.
- *
- * --
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+ * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
******************************************************************************
*
*/
@@ -49,6 +26,26 @@
extern "C" {
#endif
+/************************************************************************/
+/* Version related macros (added in 1.6.0) */
+/************************************************************************/
+
+#define SHAPELIB_VERSION_MAJOR 1
+#define SHAPELIB_VERSION_MINOR 6
+#define SHAPELIB_VERSION_MICRO 0
+
+#define SHAPELIB_MAKE_VERSION_NUMBER(major, minor, micro) \
+ ((major) * 10000 + (minor) * 100 + (micro))
+
+#define SHAPELIB_VERSION_NUMBER \
+ SHAPELIB_MAKE_VERSION_NUMBER(SHAPELIB_VERSION_MAJOR, \
+ SHAPELIB_VERSION_MINOR, \
+ SHAPELIB_VERSION_MICRO)
+
+#define SHAPELIB_AT_LEAST(major, minor, micro) \
+ (SHAPELIB_VERSION_NUMBER >= \
+ SHAPELIB_MAKE_VERSION_NUMBER(major, minor, micro))
+
/************************************************************************/
/* Configuration options. */
/************************************************************************/
@@ -73,7 +70,7 @@ extern "C" {
/* various calling conventions on the Shapelib API. */
/* */
/* To force __stdcall conventions (needed to call Shapelib */
-/* from Visual Basic and/or Dephi I believe) the makefile could */
+/* from Visual Basic and/or Delphi I believe) the makefile could */
/* be modified to define: */
/* */
/* /DSHPAPI_CALL=__stdcall */
@@ -113,31 +110,11 @@ extern "C" {
#define SHPAPI_CALL1(x) x SHPAPI_CALL
#endif
-/* -------------------------------------------------------------------- */
-/* Macros for controlling CVSID and ensuring they don't appear */
-/* as unreferenced variables resulting in lots of warnings. */
-/* -------------------------------------------------------------------- */
-#ifndef DISABLE_CVSID
-#if defined(__GNUC__) && __GNUC__ >= 4
-#define SHP_CVSID(string) \
- static const char cpl_cvsid[] __attribute__((used)) = string;
-#else
-#define SHP_CVSID(string) \
- static const char cpl_cvsid[] = string; \
- static const char *cvsid_aw() \
- { \
- return (cvsid_aw() ? NULL : cpl_cvsid); \
- }
-#endif
-#else
-#define SHP_CVSID(string)
-#endif
-
/* -------------------------------------------------------------------- */
/* On some platforms, additional file IO hooks are defined that */
/* UTF-8 encoded filenames Unicode filenames */
/* -------------------------------------------------------------------- */
-#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
+#if defined(_WIN32)
#define SHPAPI_WINDOWS
#define SHPAPI_UTF8_HOOKS
#endif
@@ -148,21 +125,27 @@ extern "C" {
typedef int *SAFile;
#ifndef SAOffset
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+typedef unsigned __int64 SAOffset;
+#else
typedef unsigned long SAOffset;
#endif
+#endif
typedef struct {
- SAFile (*FOpen)(const char *filename, const char *access);
+ SAFile (*FOpen)(const char *filename, const char *access, void *pvUserData);
SAOffset (*FRead)(void *p, SAOffset size, SAOffset nmemb, SAFile file);
- SAOffset (*FWrite)(void *p, SAOffset size, SAOffset nmemb, SAFile file);
+ SAOffset (*FWrite)(const void *p, SAOffset size, SAOffset nmemb,
+ SAFile file);
SAOffset (*FSeek)(SAFile file, SAOffset offset, int whence);
SAOffset (*FTell)(SAFile file);
int (*FFlush)(SAFile file);
int (*FClose)(SAFile file);
- int (*Remove)(const char *filename);
+ int (*Remove)(const char *filename, void *pvUserData);
void (*Error)(const char *message);
double (*Atof)(const char *str);
+ void *pvUserData;
} SAHooks;
void SHPAPI_CALL SASetupDefaultHooks(SAHooks *psHooks);
@@ -206,6 +189,12 @@ typedef struct {
typedef SHPInfo *SHPHandle;
+typedef struct {
+ int year;
+ int month;
+ int day;
+} SHPDate;
+
/* -------------------------------------------------------------------- */
/* Shape types (nSHPType) */
/* -------------------------------------------------------------------- */
@@ -277,13 +266,13 @@ struct tagSHPObject {
/* will be NULL as it is not necessary to keep the SHX file open */
SHPHandle SHPAPI_CALL SHPOpen(const char *pszShapeFile, const char *pszAccess);
SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszShapeFile, const char *pszAccess,
- SAHooks *psHooks);
+ const SAHooks *psHooks);
SHPHandle SHPAPI_CALL SHPOpenLLEx(const char *pszShapeFile,
- const char *pszAccess, SAHooks *psHooks,
+ const char *pszAccess, const SAHooks *psHooks,
int bRestoreSHX);
int SHPAPI_CALL SHPRestoreSHX(const char *pszShapeFile, const char *pszAccess,
- SAHooks *psHooks);
+ const SAHooks *psHooks);
/* If setting bFastMode = TRUE, the content of SHPReadObject() is owned by the
* SHPHandle. */
@@ -296,12 +285,14 @@ void SHPAPI_CALL SHPSetFastModeReadObject(SHPHandle hSHP, int bFastMode);
SHPHandle SHPAPI_CALL SHPCreate(const char *pszShapeFile, int nShapeType);
SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszShapeFile, int nShapeType,
- SAHooks *psHooks);
-void SHPAPI_CALL SHPGetInfo(SHPHandle hSHP, int *pnEntities, int *pnShapeType,
- double *padfMinBound, double *padfMaxBound);
+ const SAHooks *psHooks);
+void SHPAPI_CALL SHPGetInfo(const SHPHandle hSHP, int *pnEntities,
+ int *pnShapeType, double *padfMinBound,
+ double *padfMaxBound);
-SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle hSHP, int iShape);
-int SHPAPI_CALL SHPWriteObject(SHPHandle hSHP, int iShape, SHPObject *psObject);
+SHPObject SHPAPI_CALL1(*) SHPReadObject(const SHPHandle hSHP, int iShape);
+int SHPAPI_CALL SHPWriteObject(SHPHandle hSHP, int iShape,
+ const SHPObject *psObject);
void SHPAPI_CALL SHPDestroyObject(SHPObject *psObject);
void SHPAPI_CALL SHPComputeExtents(SHPObject *psObject);
@@ -314,7 +305,7 @@ SHPObject SHPAPI_CALL1(*)
SHPCreateSimpleObject(int nSHPType, int nVertices, const double *padfX,
const double *padfY, const double *padfZ);
-int SHPAPI_CALL SHPRewindObject(SHPHandle hSHP, SHPObject *psObject);
+int SHPAPI_CALL SHPRewindObject(const SHPHandle hSHP, SHPObject *psObject);
void SHPAPI_CALL SHPClose(SHPHandle hSHP);
void SHPAPI_CALL SHPWriteHeader(SHPHandle hSHP);
@@ -360,21 +351,20 @@ typedef struct {
SHPTree SHPAPI_CALL1(*)
SHPCreateTree(SHPHandle hSHP, int nDimension, int nMaxDepth,
- double *padfBoundsMin, double *padfBoundsMax);
+ const double *padfBoundsMin, const double *padfBoundsMax);
void SHPAPI_CALL SHPDestroyTree(SHPTree *hTree);
int SHPAPI_CALL SHPWriteTree(SHPTree *hTree, const char *pszFilename);
int SHPAPI_CALL SHPTreeAddShapeId(SHPTree *hTree, SHPObject *psObject);
-int SHPAPI_CALL SHPTreeRemoveShapeId(SHPTree *hTree, int nShapeId);
void SHPAPI_CALL SHPTreeTrimExtraNodes(SHPTree *hTree);
int SHPAPI_CALL1(*)
- SHPTreeFindLikelyShapes(SHPTree *hTree, double *padfBoundsMin,
+ SHPTreeFindLikelyShapes(const SHPTree *hTree, double *padfBoundsMin,
double *padfBoundsMax, int *);
-int SHPAPI_CALL SHPCheckBoundsOverlap(double *, double *, double *, double *,
- int);
+int SHPAPI_CALL SHPCheckBoundsOverlap(const double *, const double *,
+ const double *, const double *, int);
int SHPAPI_CALL1(*) SHPSearchDiskTree(FILE *fp, double *padfBoundsMin,
double *padfBoundsMax, int *pnShapeCount);
@@ -382,16 +372,17 @@ int SHPAPI_CALL1(*) SHPSearchDiskTree(FILE *fp, double *padfBoundsMin,
typedef struct SHPDiskTreeInfo *SHPTreeDiskHandle;
SHPTreeDiskHandle SHPAPI_CALL SHPOpenDiskTree(const char *pszQIXFilename,
- SAHooks *psHooks);
+ const SAHooks *psHooks);
void SHPAPI_CALL SHPCloseDiskTree(SHPTreeDiskHandle hDiskTree);
int SHPAPI_CALL1(*)
- SHPSearchDiskTreeEx(SHPTreeDiskHandle hDiskTree, double *padfBoundsMin,
- double *padfBoundsMax, int *pnShapeCount);
+ SHPSearchDiskTreeEx(const SHPTreeDiskHandle hDiskTree,
+ double *padfBoundsMin, double *padfBoundsMax,
+ int *pnShapeCount);
int SHPAPI_CALL SHPWriteTreeLL(SHPTree *hTree, const char *pszFilename,
- SAHooks *psHooks);
+ const SAHooks *psHooks);
/* -------------------------------------------------------------------- */
/* SBN Search API */
@@ -400,16 +391,16 @@ int SHPAPI_CALL SHPWriteTreeLL(SHPTree *hTree, const char *pszFilename,
typedef struct SBNSearchInfo *SBNSearchHandle;
SBNSearchHandle SHPAPI_CALL SBNOpenDiskTree(const char *pszSBNFilename,
- SAHooks *psHooks);
+ const SAHooks *psHooks);
void SHPAPI_CALL SBNCloseDiskTree(SBNSearchHandle hSBN);
int SHPAPI_CALL1(*)
- SBNSearchDiskTree(SBNSearchHandle hSBN, double *padfBoundsMin,
- double *padfBoundsMax, int *pnShapeCount);
+ SBNSearchDiskTree(const SBNSearchHandle hSBN, const double *padfBoundsMin,
+ const double *padfBoundsMax, int *pnShapeCount);
int SHPAPI_CALL1(*)
- SBNSearchDiskTreeInteger(SBNSearchHandle hSBN, int bMinX, int bMinY,
+ SBNSearchDiskTreeInteger(const SBNSearchHandle hSBN, int bMinX, int bMinY,
int bMaxX, int bMaxY, int *pnShapeCount);
void SHPAPI_CALL SBNSearchFreeIds(int *panShapeId);
@@ -485,15 +476,16 @@ typedef enum {
DBFHandle SHPAPI_CALL DBFOpen(const char *pszDBFFile, const char *pszAccess);
DBFHandle SHPAPI_CALL DBFOpenLL(const char *pszDBFFile, const char *pszAccess,
- SAHooks *psHooks);
+ const SAHooks *psHooks);
DBFHandle SHPAPI_CALL DBFCreate(const char *pszDBFFile);
DBFHandle SHPAPI_CALL DBFCreateEx(const char *pszDBFFile,
const char *pszCodePage);
DBFHandle SHPAPI_CALL DBFCreateLL(const char *pszDBFFile,
- const char *pszCodePage, SAHooks *psHooks);
+ const char *pszCodePage,
+ const SAHooks *psHooks);
-int SHPAPI_CALL DBFGetFieldCount(DBFHandle psDBF);
-int SHPAPI_CALL DBFGetRecordCount(DBFHandle psDBF);
+int SHPAPI_CALL DBFGetFieldCount(const DBFHandle psDBF);
+int SHPAPI_CALL DBFGetRecordCount(const DBFHandle psDBF);
int SHPAPI_CALL DBFAddField(DBFHandle hDBF, const char *pszFieldName,
DBFFieldType eType, int nWidth, int nDecimals);
@@ -502,17 +494,18 @@ int SHPAPI_CALL DBFAddNativeFieldType(DBFHandle hDBF, const char *pszFieldName,
int SHPAPI_CALL DBFDeleteField(DBFHandle hDBF, int iField);
-int SHPAPI_CALL DBFReorderFields(DBFHandle psDBF, int *panMap);
+int SHPAPI_CALL DBFReorderFields(DBFHandle psDBF, const int *panMap);
int SHPAPI_CALL DBFAlterFieldDefn(DBFHandle psDBF, int iField,
const char *pszFieldName, char chType,
int nWidth, int nDecimals);
-DBFFieldType SHPAPI_CALL DBFGetFieldInfo(DBFHandle psDBF, int iField,
+DBFFieldType SHPAPI_CALL DBFGetFieldInfo(const DBFHandle psDBF, int iField,
char *pszFieldName, int *pnWidth,
int *pnDecimals);
-int SHPAPI_CALL DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName);
+int SHPAPI_CALL DBFGetFieldIndex(const DBFHandle psDBF,
+ const char *pszFieldName);
int SHPAPI_CALL DBFReadIntegerAttribute(DBFHandle hDBF, int iShape, int iField);
double SHPAPI_CALL DBFReadDoubleAttribute(DBFHandle hDBF, int iShape,
@@ -521,7 +514,10 @@ const char SHPAPI_CALL1(*)
DBFReadStringAttribute(DBFHandle hDBF, int iShape, int iField);
const char SHPAPI_CALL1(*)
DBFReadLogicalAttribute(DBFHandle hDBF, int iShape, int iField);
-int SHPAPI_CALL DBFIsAttributeNULL(DBFHandle hDBF, int iShape, int iField);
+SHPDate SHPAPI_CALL DBFReadDateAttribute(DBFHandle hDBF, int iShape,
+ int iField);
+int SHPAPI_CALL DBFIsAttributeNULL(const DBFHandle hDBF, int iShape,
+ int iField);
int SHPAPI_CALL DBFWriteIntegerAttribute(DBFHandle hDBF, int iShape, int iField,
int nFieldValue);
@@ -533,22 +529,26 @@ int SHPAPI_CALL DBFWriteNULLAttribute(DBFHandle hDBF, int iShape, int iField);
int SHPAPI_CALL DBFWriteLogicalAttribute(DBFHandle hDBF, int iShape, int iField,
const char lFieldValue);
+int SHPAPI_CALL DBFWriteDateAttribute(DBFHandle hDBF, int iShape, int iField,
+ const SHPDate *dateFieldValue);
int SHPAPI_CALL DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity,
- int iField, void *pValue);
+ int iField, const void *pValue);
const char SHPAPI_CALL1(*) DBFReadTuple(DBFHandle psDBF, int hEntity);
-int SHPAPI_CALL DBFWriteTuple(DBFHandle psDBF, int hEntity, void *pRawTuple);
+int SHPAPI_CALL DBFWriteTuple(DBFHandle psDBF, int hEntity,
+ const void *pRawTuple);
-int SHPAPI_CALL DBFIsRecordDeleted(DBFHandle psDBF, int iShape);
+int SHPAPI_CALL DBFIsRecordDeleted(const DBFHandle psDBF, int iShape);
int SHPAPI_CALL DBFMarkRecordDeleted(DBFHandle psDBF, int iShape,
int bIsDeleted);
-DBFHandle SHPAPI_CALL DBFCloneEmpty(DBFHandle psDBF, const char *pszFilename);
+DBFHandle SHPAPI_CALL DBFCloneEmpty(const DBFHandle psDBF,
+ const char *pszFilename);
void SHPAPI_CALL DBFClose(DBFHandle hDBF);
void SHPAPI_CALL DBFUpdateHeader(DBFHandle hDBF);
-char SHPAPI_CALL DBFGetNativeFieldType(DBFHandle hDBF, int iField);
+char SHPAPI_CALL DBFGetNativeFieldType(const DBFHandle hDBF, int iField);
-const char SHPAPI_CALL1(*) DBFGetCodePage(DBFHandle psDBF);
+const char SHPAPI_CALL1(*) DBFGetCodePage(const DBFHandle psDBF);
void SHPAPI_CALL DBFSetLastModifiedDate(DBFHandle psDBF, int nYYSince1900,
int nMM, int nDD);
diff --git a/lib/external/shapelib/shapefil_private.h b/lib/external/shapelib/shapefil_private.h
new file mode 100644
index 00000000000..1bed49e7cce
--- /dev/null
+++ b/lib/external/shapelib/shapefil_private.h
@@ -0,0 +1,115 @@
+#ifndef SHAPEFILE_PRIVATE_H_INCLUDED
+#define SHAPEFILE_PRIVATE_H_INCLUDED
+
+/******************************************************************************
+ *
+ * Project: Shapelib
+ * Purpose: Private include file for Shapelib.
+ * Author: Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ * Copyright (c) 2012-2016, Even Rouault
+ *
+ * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+ ******************************************************************************
+ *
+ */
+
+#ifdef __cplusplus
+#define STATIC_CAST(type, x) static_cast(x)
+#define REINTERPRET_CAST(type, x) reinterpret_cast(x)
+#define CONST_CAST(type, x) const_cast(x)
+#define SHPLIB_NULLPTR nullptr
+#else
+#define STATIC_CAST(type, x) ((type)(x))
+#define REINTERPRET_CAST(type, x) ((type)(x))
+#define CONST_CAST(type, x) ((type)(x))
+#define SHPLIB_NULLPTR NULL
+#endif
+
+#if !defined(SHP_BIG_ENDIAN)
+#if defined(CPL_MSB)
+#define SHP_BIG_ENDIAN 1
+#elif (defined(__GNUC__) && __GNUC__ >= 5) || \
+ (defined(__GNUC__) && defined(__GNUC_MINOR__) && __GNUC__ == 4 && \
+ __GNUC_MINOR__ >= 6)
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define SHP_BIG_ENDIAN 1
+#endif
+#elif defined(__GLIBC__)
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define SHP_BIG_ENDIAN 1
+#endif
+#elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
+#define SHP_BIG_ENDIAN 1
+#elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
+#elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || \
+ defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || \
+ defined(_MIPSEB) || defined(_POWER) || defined(__s390__)
+#define SHP_BIG_ENDIAN 1
+#endif
+#endif
+
+#include "shapefil.h"
+#include
+#include
+
+/************************************************************************/
+/* Little endian <==> big endian byte swap macros. */
+/************************************************************************/
+
+#if (defined(__GNUC__) && __GNUC__ >= 5) || \
+ (defined(__GNUC__) && defined(__GNUC_MINOR__) && __GNUC__ == 4 && \
+ __GNUC_MINOR__ >= 8)
+#define _SHP_SWAP32(x) \
+ STATIC_CAST(uint32_t, __builtin_bswap32(STATIC_CAST(uint32_t, x)))
+#define _SHP_SWAP64(x) \
+ STATIC_CAST(uint64_t, __builtin_bswap64(STATIC_CAST(uint64_t, x)))
+#elif defined(_MSC_VER)
+#define _SHP_SWAP32(x) \
+ STATIC_CAST(uint32_t, _byteswap_ulong(STATIC_CAST(uint32_t, x)))
+#define _SHP_SWAP64(x) \
+ STATIC_CAST(uint64_t, _byteswap_uint64(STATIC_CAST(uint64_t, x)))
+#else
+#define _SHP_SWAP32(x) \
+ STATIC_CAST(uint32_t, \
+ ((STATIC_CAST(uint32_t, x) & 0x000000ffU) << 24) | \
+ ((STATIC_CAST(uint32_t, x) & 0x0000ff00U) << 8) | \
+ ((STATIC_CAST(uint32_t, x) & 0x00ff0000U) >> 8) | \
+ ((STATIC_CAST(uint32_t, x) & 0xff000000U) >> 24))
+#define _SHP_SWAP64(x) \
+ ((STATIC_CAST(uint64_t, _SHP_SWAP32(STATIC_CAST(uint32_t, x))) << 32) | \
+ (STATIC_CAST(uint64_t, _SHP_SWAP32(STATIC_CAST( \
+ uint32_t, STATIC_CAST(uint64_t, x) >> 32)))))
+
+#endif
+
+/* in-place uint32_t* swap */
+#define SHP_SWAP32(p) \
+ *REINTERPRET_CAST(uint32_t *, p) = \
+ _SHP_SWAP32(*REINTERPRET_CAST(uint32_t *, p))
+/* in-place uint64_t* swap */
+#define SHP_SWAP64(p) \
+ *REINTERPRET_CAST(uint64_t *, p) = \
+ _SHP_SWAP64(*REINTERPRET_CAST(uint64_t *, p))
+/* in-place double* swap */
+#define SHP_SWAPDOUBLE(x) \
+ do { \
+ uint64_t _n64; \
+ void *_lx = x; \
+ memcpy(&_n64, _lx, 8); \
+ _n64 = _SHP_SWAP64(_n64); \
+ memcpy(_lx, &_n64, 8); \
+ } while (0)
+/* copy double* swap*/
+#define SHP_SWAPDOUBLE_CPY(dst, src) \
+ do { \
+ uint64_t _n64; \
+ const void *_ls = src; \
+ void *_ld = dst; \
+ memcpy(&_n64, _ls, 8); \
+ _n64 = _SHP_SWAP64(_n64); \
+ memcpy(_ld, &_n64, 8); \
+ } while (0)
+#endif /* ndef SHAPEFILE_PRIVATE_H_INCLUDED */
diff --git a/lib/external/shapelib/shpopen.c b/lib/external/shapelib/shpopen.c
index e63cf768d25..b2eeb3614fb 100644
--- a/lib/external/shapelib/shpopen.c
+++ b/lib/external/shapelib/shpopen.c
@@ -8,52 +8,21 @@
* Copyright (c) 1999, 2001, Frank Warmerdam
* Copyright (c) 2011-2019, Even Rouault
*
- * This software is available under the following "MIT Style" license,
- * or at the option of the licensee under the LGPL (see COPYING). This
- * option is discussed in more detail in shapelib.html.
- *
- * --
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+ * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
******************************************************************************/
-#include "shapefil.h"
+#include "shapefil_private.h"
#include
#include
#include
#include
#include
+#include
#include
#include
#include
-SHP_CVSID("$Id$")
-
-typedef unsigned char uchar;
-
-#if UINT_MAX == 65535
-typedef unsigned long int32;
-#else
-typedef unsigned int int32;
-#endif
-
#ifndef FALSE
#define FALSE 0
#define TRUE 1
@@ -70,73 +39,18 @@ typedef unsigned int int32;
#if _MSC_VER < 1900
#define snprintf _snprintf
#endif
-#elif defined(WIN32) || defined(_WIN32)
+#elif defined(_WIN32)
#ifndef snprintf
#define snprintf _snprintf
#endif
#endif
#endif
-#ifndef CPL_UNUSED
-#if defined(__GNUC__) && __GNUC__ >= 4
-#define CPL_UNUSED __attribute((__unused__))
-#else
-#define CPL_UNUSED
-#endif
-#endif
-
-#if defined(CPL_LSB)
-#define bBigEndian false
-#elif defined(CPL_MSB)
-#define bBigEndian true
-#else
-static bool bBigEndian;
-#endif
-
-#ifdef __cplusplus
-#define STATIC_CAST(type, x) static_cast(x)
-#define SHPLIB_NULLPTR nullptr
-#else
-#define STATIC_CAST(type, x) ((type)(x))
-#define SHPLIB_NULLPTR NULL
-#endif
-
-/************************************************************************/
-/* SwapWord() */
-/* */
-/* Swap a 2, 4 or 8 byte word. */
-/************************************************************************/
-
-static void SwapWord(int length, void *wordP)
-{
- for (int i = 0; i < length / 2; i++) {
- const uchar temp = STATIC_CAST(uchar *, wordP)[i];
- STATIC_CAST(uchar *, wordP)
- [i] = STATIC_CAST(uchar *, wordP)[length - i - 1];
- STATIC_CAST(uchar *, wordP)[length - i - 1] = temp;
- }
-}
-
-/************************************************************************/
-/* SfRealloc() */
-/* */
-/* A realloc cover function that will access a NULL pointer as */
-/* a valid input. */
-/************************************************************************/
-
-static void *SfRealloc(void *pMem, int nNewSize)
-{
- if (pMem == SHPLIB_NULLPTR)
- return malloc(nNewSize);
- else
- return realloc(pMem, nNewSize);
-}
-
/************************************************************************/
/* SHPWriteHeader() */
/* */
-/* Write out a header for the .shp and .shx files as well as the */
-/* contents of the index (.shx) file. */
+/* Write out a header for the .shp and .shx files as well as the */
+/* contents of the index (.shx) file. */
/************************************************************************/
void SHPAPI_CALL SHPWriteHeader(SHPHandle psSHP)
@@ -150,64 +64,73 @@ void SHPAPI_CALL SHPWriteHeader(SHPHandle psSHP)
/* Prepare header block for .shp file. */
/* -------------------------------------------------------------------- */
- uchar abyHeader[100] = {0};
+ unsigned char abyHeader[100] = {0};
abyHeader[2] = 0x27; /* magic cookie */
abyHeader[3] = 0x0a;
- int32 i32 = psSHP->nFileSize / 2; /* file size */
+ uint32_t i32 = psSHP->nFileSize / 2; /* file size */
ByteCopy(&i32, abyHeader + 24, 4);
- if (!bBigEndian)
- SwapWord(4, abyHeader + 24);
+#if !defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(abyHeader + 24);
+#endif
i32 = 1000; /* version */
ByteCopy(&i32, abyHeader + 28, 4);
- if (bBigEndian)
- SwapWord(4, abyHeader + 28);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(abyHeader + 28);
+#endif
i32 = psSHP->nShapeType; /* shape type */
ByteCopy(&i32, abyHeader + 32, 4);
- if (bBigEndian)
- SwapWord(4, abyHeader + 32);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(abyHeader + 32);
+#endif
double dValue = psSHP->adBoundsMin[0]; /* set bounds */
ByteCopy(&dValue, abyHeader + 36, 8);
- if (bBigEndian)
- SwapWord(8, abyHeader + 36);
-
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(abyHeader + 36);
+#endif
dValue = psSHP->adBoundsMin[1];
ByteCopy(&dValue, abyHeader + 44, 8);
- if (bBigEndian)
- SwapWord(8, abyHeader + 44);
-
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(abyHeader + 44);
+#endif
dValue = psSHP->adBoundsMax[0];
ByteCopy(&dValue, abyHeader + 52, 8);
- if (bBigEndian)
- SwapWord(8, abyHeader + 52);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(abyHeader + 52);
+#endif
dValue = psSHP->adBoundsMax[1];
ByteCopy(&dValue, abyHeader + 60, 8);
- if (bBigEndian)
- SwapWord(8, abyHeader + 60);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(abyHeader + 60);
+#endif
dValue = psSHP->adBoundsMin[2]; /* z */
ByteCopy(&dValue, abyHeader + 68, 8);
- if (bBigEndian)
- SwapWord(8, abyHeader + 68);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(abyHeader + 68);
+#endif
dValue = psSHP->adBoundsMax[2];
ByteCopy(&dValue, abyHeader + 76, 8);
- if (bBigEndian)
- SwapWord(8, abyHeader + 76);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(abyHeader + 76);
+#endif
dValue = psSHP->adBoundsMin[3]; /* m */
ByteCopy(&dValue, abyHeader + 84, 8);
- if (bBigEndian)
- SwapWord(8, abyHeader + 84);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(abyHeader + 84);
+#endif
dValue = psSHP->adBoundsMax[3];
ByteCopy(&dValue, abyHeader + 92, 8);
- if (bBigEndian)
- SwapWord(8, abyHeader + 92);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(abyHeader + 92);
+#endif
/* -------------------------------------------------------------------- */
/* Write .shp file header. */
@@ -226,10 +149,11 @@ void SHPAPI_CALL SHPWriteHeader(SHPHandle psSHP)
/* -------------------------------------------------------------------- */
/* Prepare, and write .shx file header. */
/* -------------------------------------------------------------------- */
- i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100) / 2; /* file size */
+ i32 = (psSHP->nRecords * 2 * sizeof(uint32_t) + 100) / 2; /* file size */
ByteCopy(&i32, abyHeader + 24, 4);
- if (!bBigEndian)
- SwapWord(4, abyHeader + 24);
+#if !defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(abyHeader + 24);
+#endif
if (psSHP->sHooks.FSeek(psSHP->fpSHX, 0, 0) != 0 ||
psSHP->sHooks.FWrite(abyHeader, 100, 1, psSHP->fpSHX) != 1) {
@@ -246,8 +170,8 @@ void SHPAPI_CALL SHPWriteHeader(SHPHandle psSHP)
/* -------------------------------------------------------------------- */
/* Write out the .shx contents. */
/* -------------------------------------------------------------------- */
- int32 *panSHX =
- STATIC_CAST(int32 *, malloc(sizeof(int32) * 2 * psSHP->nRecords));
+ uint32_t *panSHX =
+ STATIC_CAST(uint32_t *, malloc(sizeof(uint32_t) * 2 * psSHP->nRecords));
if (panSHX == SHPLIB_NULLPTR) {
psSHP->sHooks.Error("Failure allocatin panSHX");
return;
@@ -256,13 +180,13 @@ void SHPAPI_CALL SHPWriteHeader(SHPHandle psSHP)
for (int i = 0; i < psSHP->nRecords; i++) {
panSHX[i * 2] = psSHP->panRecOffset[i] / 2;
panSHX[i * 2 + 1] = psSHP->panRecSize[i] / 2;
- if (!bBigEndian)
- SwapWord(4, panSHX + i * 2);
- if (!bBigEndian)
- SwapWord(4, panSHX + i * 2 + 1);
+#if !defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(panSHX + i * 2);
+ SHP_SWAP32(panSHX + i * 2 + 1);
+#endif
}
- if (STATIC_CAST(int, psSHP->sHooks.FWrite(panSHX, sizeof(int32) * 2,
+ if (STATIC_CAST(int, psSHP->sHooks.FWrite(panSHX, sizeof(uint32_t) * 2,
psSHP->nRecords, psSHP->fpSHX)) !=
psSHP->nRecords) {
char szErrorMsg[200];
@@ -319,7 +243,7 @@ static int SHPGetLenWithoutExtension(const char *pszBasename)
/************************************************************************/
SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess,
- SAHooks *psHooks)
+ const SAHooks *psHooks)
{
/* -------------------------------------------------------------------- */
/* Ensure the access string is one of the legal ones. We */
@@ -336,23 +260,10 @@ SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess,
pszAccess = "rb";
}
-/* -------------------------------------------------------------------- */
-/* Establish the byte order on this machine. */
-/* -------------------------------------------------------------------- */
-#if !defined(bBigEndian)
- {
- int i = 1;
- if (*((uchar *)&i) == 1)
- bBigEndian = false;
- else
- bBigEndian = true;
- }
-#endif
-
/* -------------------------------------------------------------------- */
/* Initialize the info structure. */
/* -------------------------------------------------------------------- */
- SHPHandle psSHP = STATIC_CAST(SHPHandle, calloc(sizeof(SHPInfo), 1));
+ SHPHandle psSHP = STATIC_CAST(SHPHandle, calloc(1, sizeof(SHPInfo)));
psSHP->bUpdated = FALSE;
memcpy(&(psSHP->sHooks), psHooks, sizeof(SAHooks));
@@ -365,18 +276,21 @@ SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess,
char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5));
memcpy(pszFullname, pszLayer, nLenWithoutExtension);
memcpy(pszFullname + nLenWithoutExtension, ".shp", 5);
- psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess);
+ psSHP->fpSHP =
+ psSHP->sHooks.FOpen(pszFullname, pszAccess, psSHP->sHooks.pvUserData);
if (psSHP->fpSHP == SHPLIB_NULLPTR) {
memcpy(pszFullname + nLenWithoutExtension, ".SHP", 5);
- psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess);
+ psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess,
+ psSHP->sHooks.pvUserData);
}
if (psSHP->fpSHP == SHPLIB_NULLPTR) {
const size_t nMessageLen = strlen(pszFullname) * 2 + 256;
char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen));
pszFullname[nLenWithoutExtension] = 0;
- snprintf(pszMessage, nMessageLen, "Unable to open %s.shp or %s.SHP.",
- pszFullname, pszFullname);
+ snprintf(pszMessage, nMessageLen,
+ "Unable to open %s.shp or %s.SHP in %s mode.", pszFullname,
+ pszFullname, pszAccess);
psHooks->Error(pszMessage);
free(pszMessage);
@@ -387,10 +301,12 @@ SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess,
}
memcpy(pszFullname + nLenWithoutExtension, ".shx", 5);
- psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess);
+ psSHP->fpSHX =
+ psSHP->sHooks.FOpen(pszFullname, pszAccess, psSHP->sHooks.pvUserData);
if (psSHP->fpSHX == SHPLIB_NULLPTR) {
memcpy(pszFullname + nLenWithoutExtension, ".SHX", 5);
- psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess);
+ psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess,
+ psSHP->sHooks.pvUserData);
}
if (psSHP->fpSHX == SHPLIB_NULLPTR) {
@@ -416,7 +332,7 @@ SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess,
/* -------------------------------------------------------------------- */
/* Read the file size from the SHP file. */
/* -------------------------------------------------------------------- */
- uchar *pabyBuf = STATIC_CAST(uchar *, malloc(100));
+ unsigned char *pabyBuf = STATIC_CAST(unsigned char *, malloc(100));
if (psSHP->sHooks.FRead(pabyBuf, 100, 1, psSHP->fpSHP) != 1) {
psSHP->sHooks.Error(".shp file is unreadable, or corrupt.");
psSHP->sHooks.FClose(psSHP->fpSHP);
@@ -489,43 +405,51 @@ SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess,
/* -------------------------------------------------------------------- */
double dValue;
- if (bBigEndian)
- SwapWord(8, pabyBuf + 36);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyBuf + 36);
+#endif
memcpy(&dValue, pabyBuf + 36, 8);
psSHP->adBoundsMin[0] = dValue;
- if (bBigEndian)
- SwapWord(8, pabyBuf + 44);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyBuf + 44);
+#endif
memcpy(&dValue, pabyBuf + 44, 8);
psSHP->adBoundsMin[1] = dValue;
- if (bBigEndian)
- SwapWord(8, pabyBuf + 52);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyBuf + 52);
+#endif
memcpy(&dValue, pabyBuf + 52, 8);
psSHP->adBoundsMax[0] = dValue;
- if (bBigEndian)
- SwapWord(8, pabyBuf + 60);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyBuf + 60);
+#endif
memcpy(&dValue, pabyBuf + 60, 8);
psSHP->adBoundsMax[1] = dValue;
- if (bBigEndian)
- SwapWord(8, pabyBuf + 68); /* z */
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyBuf + 68); /* z */
+#endif
memcpy(&dValue, pabyBuf + 68, 8);
psSHP->adBoundsMin[2] = dValue;
- if (bBigEndian)
- SwapWord(8, pabyBuf + 76);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyBuf + 76);
+#endif
memcpy(&dValue, pabyBuf + 76, 8);
psSHP->adBoundsMax[2] = dValue;
- if (bBigEndian)
- SwapWord(8, pabyBuf + 84); /* z */
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyBuf + 84); /* z */
+#endif
memcpy(&dValue, pabyBuf + 84, 8);
psSHP->adBoundsMin[3] = dValue;
- if (bBigEndian)
- SwapWord(8, pabyBuf + 92);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyBuf + 92);
+#endif
memcpy(&dValue, pabyBuf + 92, 8);
psSHP->adBoundsMax[3] = dValue;
@@ -546,7 +470,8 @@ SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess,
if (bLazySHXLoading)
pabyBuf = SHPLIB_NULLPTR;
else
- pabyBuf = STATIC_CAST(uchar *, malloc(8 * MAX(1, psSHP->nRecords)));
+ pabyBuf =
+ STATIC_CAST(unsigned char *, malloc(8 * MAX(1, psSHP->nRecords)));
if (psSHP->panRecOffset == SHPLIB_NULLPTR ||
psSHP->panRecSize == SHPLIB_NULLPTR ||
@@ -612,13 +537,15 @@ SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess,
for (int i = 0; i < psSHP->nRecords; i++) {
unsigned int nOffset;
memcpy(&nOffset, pabyBuf + i * 8, 4);
- if (!bBigEndian)
- SwapWord(4, &nOffset);
+#if !defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(&nOffset);
+#endif
unsigned int nLength;
memcpy(&nLength, pabyBuf + i * 8 + 4, 4);
- if (!bBigEndian)
- SwapWord(4, &nLength);
+#if !defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(&nLength);
+#endif
if (nOffset > STATIC_CAST(unsigned int, INT_MAX)) {
char str[128];
@@ -657,7 +584,7 @@ SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess,
/************************************************************************/
SHPHandle SHPAPI_CALL SHPOpenLLEx(const char *pszLayer, const char *pszAccess,
- SAHooks *psHooks, int bRestoreSHX)
+ const SAHooks *psHooks, int bRestoreSHX)
{
if (!bRestoreSHX)
return SHPOpenLL(pszLayer, pszAccess, psHooks);
@@ -678,7 +605,7 @@ SHPHandle SHPAPI_CALL SHPOpenLLEx(const char *pszLayer, const char *pszAccess,
/************************************************************************/
int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess,
- SAHooks *psHooks)
+ const SAHooks *psHooks)
{
/* -------------------------------------------------------------------- */
/* Ensure the access string is one of the legal ones. We */
@@ -693,19 +620,6 @@ int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess,
pszAccess = "rb";
}
-/* -------------------------------------------------------------------- */
-/* Establish the byte order on this machine. */
-/* -------------------------------------------------------------------- */
-#if !defined(bBigEndian)
- {
- int i = 1;
- if (*((uchar *)&i) == 1)
- bBigEndian = false;
- else
- bBigEndian = true;
- }
-#endif
-
/* -------------------------------------------------------------------- */
/* Open the .shp file. Note that files pulled from */
/* a PC to Unix with upper case filenames won't work! */
@@ -714,10 +628,10 @@ int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess,
char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5));
memcpy(pszFullname, pszLayer, nLenWithoutExtension);
memcpy(pszFullname + nLenWithoutExtension, ".shp", 5);
- SAFile fpSHP = psHooks->FOpen(pszFullname, pszAccess);
+ SAFile fpSHP = psHooks->FOpen(pszFullname, pszAccess, psHooks->pvUserData);
if (fpSHP == SHPLIB_NULLPTR) {
memcpy(pszFullname + nLenWithoutExtension, ".SHP", 5);
- fpSHP = psHooks->FOpen(pszFullname, pszAccess);
+ fpSHP = psHooks->FOpen(pszFullname, pszAccess, psHooks->pvUserData);
}
if (fpSHP == SHPLIB_NULLPTR) {
@@ -738,7 +652,7 @@ int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess,
/* -------------------------------------------------------------------- */
/* Read the file size from the SHP file. */
/* -------------------------------------------------------------------- */
- uchar *pabyBuf = STATIC_CAST(uchar *, malloc(100));
+ unsigned char *pabyBuf = STATIC_CAST(unsigned char *, malloc(100));
if (psHooks->FRead(pabyBuf, 100, 1, fpSHP) != 1) {
psHooks->Error(".shp file is unreadable, or corrupt.");
psHooks->FClose(fpSHP);
@@ -759,7 +673,8 @@ int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess,
memcpy(pszFullname + nLenWithoutExtension, ".shx", 5);
const char pszSHXAccess[] = "w+b";
- SAFile fpSHX = psHooks->FOpen(pszFullname, pszSHXAccess);
+ SAFile fpSHX =
+ psHooks->FOpen(pszFullname, pszSHXAccess, psHooks->pvUserData);
if (fpSHX == SHPLIB_NULLPTR) {
size_t nMessageLen = strlen(pszFullname) * 2 + 256;
char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen));
@@ -789,25 +704,70 @@ int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess,
// unsigned int nCurrentRecordOffset = 0;
unsigned int nCurrentSHPOffset = 100;
unsigned int nRealSHXContentSize = 100;
- unsigned int niRecord = 0;
- unsigned int nRecordLength = 0;
+ int nRetCode = TRUE;
unsigned int nRecordOffset = 50;
- char abyReadRecord[8];
while (nCurrentSHPOffset < nSHPFilesize) {
+ unsigned int niRecord = 0;
+ unsigned int nRecordLength = 0;
+ int nSHPType;
+
if (psHooks->FRead(&niRecord, 4, 1, fpSHP) == 1 &&
- psHooks->FRead(&nRecordLength, 4, 1, fpSHP) == 1) {
- if (!bBigEndian)
- SwapWord(4, &nRecordOffset);
- memcpy(abyReadRecord, &nRecordOffset, 4);
+ psHooks->FRead(&nRecordLength, 4, 1, fpSHP) == 1 &&
+ psHooks->FRead(&nSHPType, 4, 1, fpSHP) == 1) {
+ char abyReadRecord[8];
+ unsigned int nRecordOffsetBE = nRecordOffset;
+
+#if !defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(&nRecordOffsetBE);
+#endif
+ memcpy(abyReadRecord, &nRecordOffsetBE, 4);
memcpy(abyReadRecord + 4, &nRecordLength, 4);
+#if !defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(&nRecordLength);
+#endif
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(&nSHPType);
+#endif
+
+ // Sanity check on record length
+ if (nRecordLength < 1 ||
+ nRecordLength > (nSHPFilesize - (nCurrentSHPOffset + 8)) / 2) {
+ char szErrorMsg[200];
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "Error parsing .shp to restore .shx. "
+ "Invalid record length = %u at record starting at "
+ "offset %u",
+ nRecordLength, nCurrentSHPOffset);
+ psHooks->Error(szErrorMsg);
+
+ nRetCode = FALSE;
+ break;
+ }
+
+ // Sanity check on record type
+ if (nSHPType != SHPT_NULL && nSHPType != SHPT_POINT &&
+ nSHPType != SHPT_ARC && nSHPType != SHPT_POLYGON &&
+ nSHPType != SHPT_MULTIPOINT && nSHPType != SHPT_POINTZ &&
+ nSHPType != SHPT_ARCZ && nSHPType != SHPT_POLYGONZ &&
+ nSHPType != SHPT_MULTIPOINTZ && nSHPType != SHPT_POINTM &&
+ nSHPType != SHPT_ARCM && nSHPType != SHPT_POLYGONM &&
+ nSHPType != SHPT_MULTIPOINTM && nSHPType != SHPT_MULTIPATCH) {
+ char szErrorMsg[200];
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "Error parsing .shp to restore .shx. "
+ "Invalid shape type = %d at record starting at "
+ "offset %u",
+ nSHPType, nCurrentSHPOffset);
+ psHooks->Error(szErrorMsg);
+
+ nRetCode = FALSE;
+ break;
+ }
+
psHooks->FWrite(abyReadRecord, 8, 1, fpSHX);
- if (!bBigEndian)
- SwapWord(4, &nRecordOffset);
- if (!bBigEndian)
- SwapWord(4, &nRecordLength);
nRecordOffset += nRecordLength + 4;
// nCurrentRecordOffset += 8;
nCurrentSHPOffset += 8 + nRecordLength * 2;
@@ -816,21 +776,30 @@ int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess,
nRealSHXContentSize += 8;
}
else {
- psHooks->Error("Error parsing .shp to restore .shx");
-
- psHooks->FClose(fpSHX);
- psHooks->FClose(fpSHP);
-
- free(pabySHXHeader);
- free(pszFullname);
+ char szErrorMsg[200];
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "Error parsing .shp to restore .shx. "
+ "Cannot read first bytes of record starting at "
+ "offset %u",
+ nCurrentSHPOffset);
+ psHooks->Error(szErrorMsg);
- return (0);
+ nRetCode = FALSE;
+ break;
}
}
+ if (nRetCode && nCurrentSHPOffset != nSHPFilesize) {
+ psHooks->Error("Error parsing .shp to restore .shx. "
+ "Not expected number of bytes");
+
+ nRetCode = FALSE;
+ }
nRealSHXContentSize /= 2; // Bytes counted -> WORDs
- if (!bBigEndian)
- SwapWord(4, &nRealSHXContentSize);
+#if !defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(&nRealSHXContentSize);
+#endif
+
psHooks->FSeek(fpSHX, 24, 0);
psHooks->FWrite(&nRealSHXContentSize, 4, 1, fpSHX);
@@ -840,13 +809,13 @@ int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess,
free(pszFullname);
free(pabySHXHeader);
- return (1);
+ return nRetCode;
}
/************************************************************************/
/* SHPClose() */
-/* */
-/* Close the .shp and .shx files. */
+/* */
+/* Close the .shp and .shx files. */
/************************************************************************/
void SHPAPI_CALL SHPClose(SHPHandle psSHP)
@@ -855,7 +824,7 @@ void SHPAPI_CALL SHPClose(SHPHandle psSHP)
return;
/* -------------------------------------------------------------------- */
- /* Update the header if we have modified anything. */
+ /* Update the header if we have modified anything. */
/* -------------------------------------------------------------------- */
if (psSHP->bUpdated)
SHPWriteHeader(psSHP);
@@ -914,8 +883,9 @@ void SHPAPI_CALL SHPSetFastModeReadObject(SHPHandle hSHP, int bFastMode)
/* Fetch general information about the shape file. */
/************************************************************************/
-void SHPAPI_CALL SHPGetInfo(SHPHandle psSHP, int *pnEntities, int *pnShapeType,
- double *padfMinBound, double *padfMaxBound)
+void SHPAPI_CALL SHPGetInfo(const SHPHandle psSHP, int *pnEntities,
+ int *pnShapeType, double *padfMinBound,
+ double *padfMaxBound)
{
if (psSHP == SHPLIB_NULLPTR)
return;
@@ -958,21 +928,8 @@ SHPHandle SHPAPI_CALL SHPCreate(const char *pszLayer, int nShapeType)
/************************************************************************/
SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType,
- SAHooks *psHooks)
+ const SAHooks *psHooks)
{
-/* -------------------------------------------------------------------- */
-/* Establish the byte order on this system. */
-/* -------------------------------------------------------------------- */
-#if !defined(bBigEndian)
- {
- int i = 1;
- if (*((uchar *)&i) == 1)
- bBigEndian = false;
- else
- bBigEndian = true;
- }
-#endif
-
/* -------------------------------------------------------------------- */
/* Open the two files so we can write their headers. */
/* -------------------------------------------------------------------- */
@@ -980,7 +937,7 @@ SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType,
char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5));
memcpy(pszFullname, pszLayer, nLenWithoutExtension);
memcpy(pszFullname + nLenWithoutExtension, ".shp", 5);
- SAFile fpSHP = psHooks->FOpen(pszFullname, "wb");
+ SAFile fpSHP = psHooks->FOpen(pszFullname, "w+b", psHooks->pvUserData);
if (fpSHP == SHPLIB_NULLPTR) {
char szErrorMsg[200];
snprintf(szErrorMsg, sizeof(szErrorMsg), "Failed to create file %s: %s",
@@ -988,11 +945,11 @@ SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType,
psHooks->Error(szErrorMsg);
free(pszFullname);
- return NULL;
+ return SHPLIB_NULLPTR;
}
memcpy(pszFullname + nLenWithoutExtension, ".shx", 5);
- SAFile fpSHX = psHooks->FOpen(pszFullname, "wb");
+ SAFile fpSHX = psHooks->FOpen(pszFullname, "w+b", psHooks->pvUserData);
if (fpSHX == SHPLIB_NULLPTR) {
char szErrorMsg[200];
snprintf(szErrorMsg, sizeof(szErrorMsg), "Failed to create file %s: %s",
@@ -1001,7 +958,7 @@ SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType,
free(pszFullname);
psHooks->FClose(fpSHP);
- return NULL;
+ return SHPLIB_NULLPTR;
}
free(pszFullname);
@@ -1010,26 +967,29 @@ SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType,
/* -------------------------------------------------------------------- */
/* Prepare header block for .shp file. */
/* -------------------------------------------------------------------- */
- uchar abyHeader[100];
+ unsigned char abyHeader[100];
memset(abyHeader, 0, sizeof(abyHeader));
abyHeader[2] = 0x27; /* magic cookie */
abyHeader[3] = 0x0a;
- int32 i32 = 50; /* file size */
+ uint32_t i32 = 50; /* file size */
ByteCopy(&i32, abyHeader + 24, 4);
- if (!bBigEndian)
- SwapWord(4, abyHeader + 24);
+#if !defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(abyHeader + 24);
+#endif
i32 = 1000; /* version */
ByteCopy(&i32, abyHeader + 28, 4);
- if (bBigEndian)
- SwapWord(4, abyHeader + 28);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(abyHeader + 28);
+#endif
i32 = nShapeType; /* shape type */
ByteCopy(&i32, abyHeader + 32, 4);
- if (bBigEndian)
- SwapWord(4, abyHeader + 32);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(abyHeader + 32);
+#endif
double dValue = 0.0; /* set bounds */
ByteCopy(&dValue, abyHeader + 36, 8);
@@ -1051,7 +1011,7 @@ SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType,
free(pszFullname);
psHooks->FClose(fpSHP);
psHooks->FClose(fpSHX);
- return NULL;
+ return SHPLIB_NULLPTR;
}
/* -------------------------------------------------------------------- */
@@ -1059,8 +1019,9 @@ SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType,
/* -------------------------------------------------------------------- */
i32 = 50; /* file size */
ByteCopy(&i32, abyHeader + 24, 4);
- if (!bBigEndian)
- SwapWord(4, abyHeader + 24);
+#if !defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(abyHeader + 24);
+#endif
if (psHooks->FWrite(abyHeader, 100, 1, fpSHX) != 1) {
char szErrorMsg[200];
@@ -1073,16 +1034,37 @@ SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType,
free(pszFullname);
psHooks->FClose(fpSHP);
psHooks->FClose(fpSHX);
- return NULL;
+ return SHPLIB_NULLPTR;
}
- /* -------------------------------------------------------------------- */
- /* Close the files, and then open them as regular existing files. */
- /* -------------------------------------------------------------------- */
- psHooks->FClose(fpSHP);
- psHooks->FClose(fpSHX);
+ SHPHandle psSHP = STATIC_CAST(SHPHandle, calloc(1, sizeof(SHPInfo)));
- return (SHPOpenLL(pszLayer, "r+b", psHooks));
+ psSHP->bUpdated = FALSE;
+ memcpy(&(psSHP->sHooks), psHooks, sizeof(SAHooks));
+
+ psSHP->fpSHP = fpSHP;
+ psSHP->fpSHX = fpSHX;
+ psSHP->nShapeType = nShapeType;
+ psSHP->nFileSize = 100;
+ psSHP->panRecOffset =
+ STATIC_CAST(unsigned int *, malloc(sizeof(unsigned int)));
+ psSHP->panRecSize =
+ STATIC_CAST(unsigned int *, malloc(sizeof(unsigned int)));
+
+ if (psSHP->panRecOffset == SHPLIB_NULLPTR ||
+ psSHP->panRecSize == SHPLIB_NULLPTR) {
+ psSHP->sHooks.Error("Not enough memory to allocate requested memory");
+ psSHP->sHooks.FClose(psSHP->fpSHP);
+ psSHP->sHooks.FClose(psSHP->fpSHX);
+ if (psSHP->panRecOffset)
+ free(psSHP->panRecOffset);
+ if (psSHP->panRecSize)
+ free(psSHP->panRecSize);
+ free(psSHP);
+ return SHPLIB_NULLPTR;
+ }
+
+ return psSHP;
}
/************************************************************************/
@@ -1092,19 +1074,19 @@ SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType,
/* indicated location in the record. */
/************************************************************************/
-static void _SHPSetBounds(uchar *pabyRec, SHPObject *psShape)
+static void _SHPSetBounds(unsigned char *pabyRec, const SHPObject *psShape)
{
ByteCopy(&(psShape->dfXMin), pabyRec + 0, 8);
ByteCopy(&(psShape->dfYMin), pabyRec + 8, 8);
ByteCopy(&(psShape->dfXMax), pabyRec + 16, 8);
ByteCopy(&(psShape->dfYMax), pabyRec + 24, 8);
- if (bBigEndian) {
- SwapWord(8, pabyRec + 0);
- SwapWord(8, pabyRec + 8);
- SwapWord(8, pabyRec + 16);
- SwapWord(8, pabyRec + 24);
- }
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyRec + 0);
+ SHP_SWAP64(pabyRec + 8);
+ SHP_SWAP64(pabyRec + 16);
+ SHP_SWAP64(pabyRec + 24);
+#endif
}
/************************************************************************/
@@ -1159,7 +1141,7 @@ SHPObject SHPAPI_CALL1(*)
psObject->bMeasureIsUsed = FALSE;
/* -------------------------------------------------------------------- */
- /* Establish whether this shape type has M, and Z values. */
+ /* Establish whether this shape type has M, and Z values. */
/* -------------------------------------------------------------------- */
bool bHasM;
bool bHasZ;
@@ -1191,7 +1173,7 @@ SHPObject SHPAPI_CALL1(*)
psObject->nParts = MAX(1, nParts);
psObject->panPartStart =
- STATIC_CAST(int *, calloc(sizeof(int), psObject->nParts));
+ STATIC_CAST(int *, calloc(psObject->nParts, sizeof(int)));
psObject->panPartType =
STATIC_CAST(int *, malloc(sizeof(int) * psObject->nParts));
@@ -1208,8 +1190,7 @@ SHPObject SHPAPI_CALL1(*)
psObject->panPartType[i] = SHPP_RING;
}
- if (psObject->panPartStart[0] != 0)
- psObject->panPartStart[0] = 0;
+ psObject->panPartStart[0] = 0;
}
/* -------------------------------------------------------------------- */
@@ -1219,16 +1200,16 @@ SHPObject SHPAPI_CALL1(*)
const size_t nSize = sizeof(double) * nVertices;
psObject->padfX =
STATIC_CAST(double *, padfX ? malloc(nSize)
- : calloc(sizeof(double), nVertices));
+ : calloc(nVertices, sizeof(double)));
psObject->padfY =
STATIC_CAST(double *, padfY ? malloc(nSize)
- : calloc(sizeof(double), nVertices));
+ : calloc(nVertices, sizeof(double)));
psObject->padfZ = STATIC_CAST(
double *,
- padfZ &&bHasZ ? malloc(nSize) : calloc(sizeof(double), nVertices));
+ padfZ &&bHasZ ? malloc(nSize) : calloc(nVertices, sizeof(double)));
psObject->padfM = STATIC_CAST(
double *,
- padfM &&bHasM ? malloc(nSize) : calloc(sizeof(double), nVertices));
+ padfM &&bHasM ? malloc(nSize) : calloc(nVertices, sizeof(double)));
if (padfX != SHPLIB_NULLPTR)
memcpy(psObject->padfX, padfX, nSize);
if (padfY != SHPLIB_NULLPTR)
@@ -1273,7 +1254,7 @@ SHPObject SHPAPI_CALL1(*)
/************************************************************************/
int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId,
- SHPObject *psObject)
+ const SHPObject *psObject)
{
psSHP->bUpdated = TRUE;
@@ -1298,22 +1279,32 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId,
/* Add the new entity to the in memory index. */
/* -------------------------------------------------------------------- */
if (nShapeId == -1 && psSHP->nRecords + 1 > psSHP->nMaxRecords) {
+ /* This cannot overflow given that we check that the file size does
+ * not grow over 4 GB, and the minimum size of a record is 12 bytes,
+ * hence the maximm value for nMaxRecords is 357,913,941
+ */
int nNewMaxRecords = psSHP->nMaxRecords + psSHP->nMaxRecords / 3 + 100;
unsigned int *panRecOffsetNew;
unsigned int *panRecSizeNew;
panRecOffsetNew = STATIC_CAST(
- unsigned int *, SfRealloc(psSHP->panRecOffset,
- sizeof(unsigned int) * nNewMaxRecords));
- if (panRecOffsetNew == SHPLIB_NULLPTR)
+ unsigned int *, realloc(psSHP->panRecOffset,
+ sizeof(unsigned int) * nNewMaxRecords));
+ if (panRecOffsetNew == SHPLIB_NULLPTR) {
+ psSHP->sHooks.Error("Failed to write shape object. "
+ "Memory allocation error.");
return -1;
+ }
psSHP->panRecOffset = panRecOffsetNew;
panRecSizeNew = STATIC_CAST(
- unsigned int *, SfRealloc(psSHP->panRecSize,
- sizeof(unsigned int) * nNewMaxRecords));
- if (panRecSizeNew == SHPLIB_NULLPTR)
+ unsigned int *,
+ realloc(psSHP->panRecSize, sizeof(unsigned int) * nNewMaxRecords));
+ if (panRecSizeNew == SHPLIB_NULLPTR) {
+ psSHP->sHooks.Error("Failed to write shape object. "
+ "Memory allocation error.");
return -1;
+ }
psSHP->panRecSize = panRecSizeNew;
psSHP->nMaxRecords = nNewMaxRecords;
@@ -1322,14 +1313,28 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId,
/* -------------------------------------------------------------------- */
/* Initialize record. */
/* -------------------------------------------------------------------- */
- uchar *pabyRec =
- STATIC_CAST(uchar *, malloc(psObject->nVertices * 4 * sizeof(double) +
- psObject->nParts * 8 + 128));
- if (pabyRec == SHPLIB_NULLPTR)
+
+ /* The following computation cannot overflow on 32-bit platforms given that
+ * the user had to allocate arrays of at least that size. */
+ size_t nRecMaxSize =
+ psObject->nVertices * 4 * sizeof(double) + psObject->nParts * 8;
+ /* But the following test could trigger on 64-bit platforms on huge
+ * geometries. */
+ const unsigned nExtraSpaceForGeomHeader = 128;
+ if (nRecMaxSize > UINT_MAX - nExtraSpaceForGeomHeader) {
+ psSHP->sHooks.Error("Failed to write shape object. Too big geometry.");
return -1;
+ }
+ nRecMaxSize += nExtraSpaceForGeomHeader;
+ unsigned char *pabyRec = STATIC_CAST(unsigned char *, malloc(nRecMaxSize));
+ if (pabyRec == SHPLIB_NULLPTR) {
+ psSHP->sHooks.Error("Failed to write shape object. "
+ "Memory allocation error.");
+ return -1;
+ }
/* -------------------------------------------------------------------- */
- /* Extract vertices for a Polygon or Arc. */
+ /* Extract vertices for a Polygon or Arc. */
/* -------------------------------------------------------------------- */
unsigned int nRecordSize = 0;
const bool bFirstFeature = psSHP->nRecords == 0;
@@ -1339,15 +1344,15 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId,
psObject->nSHPType == SHPT_POLYGONM || psObject->nSHPType == SHPT_ARC ||
psObject->nSHPType == SHPT_ARCZ || psObject->nSHPType == SHPT_ARCM ||
psObject->nSHPType == SHPT_MULTIPATCH) {
- int32 nPoints = psObject->nVertices;
- int32 nParts = psObject->nParts;
+ uint32_t nPoints = psObject->nVertices;
+ uint32_t nParts = psObject->nParts;
_SHPSetBounds(pabyRec + 12, psObject);
- if (bBigEndian)
- SwapWord(4, &nPoints);
- if (bBigEndian)
- SwapWord(4, &nParts);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(&nPoints);
+ SHP_SWAP32(&nParts);
+#endif
ByteCopy(&nPoints, pabyRec + 40 + 8, 4);
ByteCopy(&nParts, pabyRec + 36 + 8, 4);
@@ -1360,8 +1365,9 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId,
ByteCopy(psObject->panPartStart, pabyRec + 44 + 8,
4 * psObject->nParts);
for (int i = 0; i < psObject->nParts; i++) {
- if (bBigEndian)
- SwapWord(4, pabyRec + 44 + 8 + 4 * i);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(pabyRec + 44 + 8 + 4 * i);
+#endif
nRecordSize += 4;
}
@@ -1372,8 +1378,9 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId,
memcpy(pabyRec + nRecordSize, psObject->panPartType,
4 * psObject->nParts);
for (int i = 0; i < psObject->nParts; i++) {
- if (bBigEndian)
- SwapWord(4, pabyRec + nRecordSize);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(pabyRec + nRecordSize);
+#endif
nRecordSize += 4;
}
}
@@ -1385,11 +1392,10 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId,
ByteCopy(psObject->padfX + i, pabyRec + nRecordSize, 8);
ByteCopy(psObject->padfY + i, pabyRec + nRecordSize + 8, 8);
- if (bBigEndian)
- SwapWord(8, pabyRec + nRecordSize);
-
- if (bBigEndian)
- SwapWord(8, pabyRec + nRecordSize + 8);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyRec + nRecordSize);
+ SHP_SWAP64(pabyRec + nRecordSize + 8);
+#endif
nRecordSize += 2 * 8;
}
@@ -1401,19 +1407,22 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId,
psObject->nSHPType == SHPT_ARCZ ||
psObject->nSHPType == SHPT_MULTIPATCH) {
ByteCopy(&(psObject->dfZMin), pabyRec + nRecordSize, 8);
- if (bBigEndian)
- SwapWord(8, pabyRec + nRecordSize);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyRec + nRecordSize);
+#endif
nRecordSize += 8;
ByteCopy(&(psObject->dfZMax), pabyRec + nRecordSize, 8);
- if (bBigEndian)
- SwapWord(8, pabyRec + nRecordSize);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyRec + nRecordSize);
+#endif
nRecordSize += 8;
for (int i = 0; i < psObject->nVertices; i++) {
ByteCopy(psObject->padfZ + i, pabyRec + nRecordSize, 8);
- if (bBigEndian)
- SwapWord(8, pabyRec + nRecordSize);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyRec + nRecordSize);
+#endif
nRecordSize += 8;
}
}
@@ -1430,65 +1439,72 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId,
|| psObject->nSHPType == SHPT_POLYGONZ ||
psObject->nSHPType == SHPT_ARCZ)) {
ByteCopy(&(psObject->dfMMin), pabyRec + nRecordSize, 8);
- if (bBigEndian)
- SwapWord(8, pabyRec + nRecordSize);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyRec + nRecordSize);
+#endif
nRecordSize += 8;
ByteCopy(&(psObject->dfMMax), pabyRec + nRecordSize, 8);
- if (bBigEndian)
- SwapWord(8, pabyRec + nRecordSize);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyRec + nRecordSize);
+#endif
nRecordSize += 8;
for (int i = 0; i < psObject->nVertices; i++) {
ByteCopy(psObject->padfM + i, pabyRec + nRecordSize, 8);
- if (bBigEndian)
- SwapWord(8, pabyRec + nRecordSize);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyRec + nRecordSize);
+#endif
nRecordSize += 8;
}
}
}
/* -------------------------------------------------------------------- */
- /* Extract vertices for a MultiPoint. */
+ /* Extract vertices for a MultiPoint. */
/* -------------------------------------------------------------------- */
else if (psObject->nSHPType == SHPT_MULTIPOINT ||
psObject->nSHPType == SHPT_MULTIPOINTZ ||
psObject->nSHPType == SHPT_MULTIPOINTM) {
- int32 nPoints = psObject->nVertices;
+ uint32_t nPoints = psObject->nVertices;
_SHPSetBounds(pabyRec + 12, psObject);
- if (bBigEndian)
- SwapWord(4, &nPoints);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(&nPoints);
+#endif
ByteCopy(&nPoints, pabyRec + 44, 4);
for (int i = 0; i < psObject->nVertices; i++) {
ByteCopy(psObject->padfX + i, pabyRec + 48 + i * 16, 8);
ByteCopy(psObject->padfY + i, pabyRec + 48 + i * 16 + 8, 8);
- if (bBigEndian)
- SwapWord(8, pabyRec + 48 + i * 16);
- if (bBigEndian)
- SwapWord(8, pabyRec + 48 + i * 16 + 8);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyRec + 48 + i * 16);
+ SHP_SWAP64(pabyRec + 48 + i * 16 + 8);
+#endif
}
nRecordSize = 48 + 16 * psObject->nVertices;
if (psObject->nSHPType == SHPT_MULTIPOINTZ) {
ByteCopy(&(psObject->dfZMin), pabyRec + nRecordSize, 8);
- if (bBigEndian)
- SwapWord(8, pabyRec + nRecordSize);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyRec + nRecordSize);
+#endif
nRecordSize += 8;
ByteCopy(&(psObject->dfZMax), pabyRec + nRecordSize, 8);
- if (bBigEndian)
- SwapWord(8, pabyRec + nRecordSize);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyRec + nRecordSize);
+#endif
nRecordSize += 8;
for (int i = 0; i < psObject->nVertices; i++) {
ByteCopy(psObject->padfZ + i, pabyRec + nRecordSize, 8);
- if (bBigEndian)
- SwapWord(8, pabyRec + nRecordSize);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyRec + nRecordSize);
+#endif
nRecordSize += 8;
}
}
@@ -1497,26 +1513,29 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId,
(psObject->nSHPType == SHPT_MULTIPOINTZ ||
psObject->nSHPType == SHPT_MULTIPOINTM)) {
ByteCopy(&(psObject->dfMMin), pabyRec + nRecordSize, 8);
- if (bBigEndian)
- SwapWord(8, pabyRec + nRecordSize);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyRec + nRecordSize);
+#endif
nRecordSize += 8;
ByteCopy(&(psObject->dfMMax), pabyRec + nRecordSize, 8);
- if (bBigEndian)
- SwapWord(8, pabyRec + nRecordSize);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyRec + nRecordSize);
+#endif
nRecordSize += 8;
for (int i = 0; i < psObject->nVertices; i++) {
ByteCopy(psObject->padfM + i, pabyRec + nRecordSize, 8);
- if (bBigEndian)
- SwapWord(8, pabyRec + nRecordSize);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyRec + nRecordSize);
+#endif
nRecordSize += 8;
}
}
}
/* -------------------------------------------------------------------- */
- /* Write point. */
+ /* Write point. */
/* -------------------------------------------------------------------- */
else if (psObject->nSHPType == SHPT_POINT ||
psObject->nSHPType == SHPT_POINTZ ||
@@ -1524,25 +1543,27 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId,
ByteCopy(psObject->padfX, pabyRec + 12, 8);
ByteCopy(psObject->padfY, pabyRec + 20, 8);
- if (bBigEndian)
- SwapWord(8, pabyRec + 12);
- if (bBigEndian)
- SwapWord(8, pabyRec + 20);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyRec + 12);
+ SHP_SWAP64(pabyRec + 20);
+#endif
nRecordSize = 28;
if (psObject->nSHPType == SHPT_POINTZ) {
ByteCopy(psObject->padfZ, pabyRec + nRecordSize, 8);
- if (bBigEndian)
- SwapWord(8, pabyRec + nRecordSize);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyRec + nRecordSize);
+#endif
nRecordSize += 8;
}
if (psObject->bMeasureIsUsed && (psObject->nSHPType == SHPT_POINTZ ||
psObject->nSHPType == SHPT_POINTM)) {
ByteCopy(psObject->padfM, pabyRec + nRecordSize, 8);
- if (bBigEndian)
- SwapWord(8, pabyRec + nRecordSize);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP64(pabyRec + nRecordSize);
+#endif
nRecordSize += 8;
}
}
@@ -1598,20 +1619,23 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId,
/* -------------------------------------------------------------------- */
/* Set the shape type, record number, and record size. */
/* -------------------------------------------------------------------- */
- int32 i32 =
+ uint32_t i32 =
(nShapeId < 0) ? psSHP->nRecords + 1 : nShapeId + 1; /* record # */
- if (!bBigEndian)
- SwapWord(4, &i32);
+#if !defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(&i32);
+#endif
ByteCopy(&i32, pabyRec, 4);
i32 = (nRecordSize - 8) / 2; /* record size */
- if (!bBigEndian)
- SwapWord(4, &i32);
+#if !defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(&i32);
+#endif
ByteCopy(&i32, pabyRec + 4, 4);
i32 = psObject->nSHPType; /* shape type */
- if (bBigEndian)
- SwapWord(4, &i32);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(&i32);
+#endif
ByteCopy(&i32, pabyRec + 8, 4);
/* -------------------------------------------------------------------- */
@@ -1666,7 +1690,7 @@ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId,
psSHP->panRecSize[nShapeId] = nRecordSize - 8;
/* -------------------------------------------------------------------- */
- /* Expand file wide bounds based on this shape. */
+ /* Expand file wide bounds based on this shape. */
/* -------------------------------------------------------------------- */
if (bFirstFeature) {
if (psObject->nSHPType == SHPT_NULL || psObject->nVertices == 0) {
@@ -1754,11 +1778,11 @@ static unsigned char *SHPReallocObjectBufIfNecessary(SHPHandle psSHP,
/************************************************************************/
/* SHPReadObject() */
/* */
-/* Read the vertices, parts, and other non-attribute information */
-/* for one shape. */
+/* Read the vertices, parts, and other non-attribute information */
+/* for one shape. */
/************************************************************************/
-SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
+SHPObject SHPAPI_CALL1(*) SHPReadObject(const SHPHandle psSHP, int hEntity)
{
/* -------------------------------------------------------------------- */
/* Validate the record/entity number. */
@@ -1786,10 +1810,10 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
psSHP->sHooks.Error(str);
return SHPLIB_NULLPTR;
}
- if (!bBigEndian)
- SwapWord(4, &nOffset);
- if (!bBigEndian)
- SwapWord(4, &nLength);
+#if !defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(&nOffset);
+ SHP_SWAP32(&nLength);
+#endif
if (nOffset > STATIC_CAST(unsigned int, INT_MAX)) {
char str[128];
@@ -1859,8 +1883,8 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
}
}
- uchar *pabyRecNew =
- STATIC_CAST(uchar *, SfRealloc(psSHP->pabyRec, nNewBufSize));
+ unsigned char *pabyRecNew =
+ STATIC_CAST(unsigned char *, realloc(psSHP->pabyRec, nNewBufSize));
if (pabyRecNew == SHPLIB_NULLPTR) {
char szErrorMsg[160];
snprintf(szErrorMsg, sizeof(szErrorMsg),
@@ -1919,8 +1943,9 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
/* Do a sanity check */
int nSHPContentLength;
memcpy(&nSHPContentLength, psSHP->pabyRec + 4, 4);
- if (!bBigEndian)
- SwapWord(4, &(nSHPContentLength));
+#if !defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(&(nSHPContentLength));
+#endif
if (nSHPContentLength < 0 || nSHPContentLength > INT_MAX / 2 - 4 ||
2 * nSHPContentLength + 8 != nBytesRead) {
char str[128];
@@ -1962,11 +1987,12 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
int nSHPType;
memcpy(&nSHPType, psSHP->pabyRec + 8, 4);
- if (bBigEndian)
- SwapWord(4, &(nSHPType));
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(&(nSHPType));
+#endif
/* -------------------------------------------------------------------- */
- /* Allocate and minimally initialize the object. */
+ /* Allocate and minimally initialize the object. */
/* -------------------------------------------------------------------- */
SHPObject *psShape;
if (psSHP->bFastModeReadObject) {
@@ -1988,7 +2014,7 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
psShape->bFastModeReadObject = psSHP->bFastModeReadObject;
/* ==================================================================== */
- /* Extract vertices for a Polygon or Arc. */
+ /* Extract vertices for a Polygon or Arc. */
/* ==================================================================== */
if (psShape->nSHPType == SHPT_POLYGON || psShape->nSHPType == SHPT_ARC ||
psShape->nSHPType == SHPT_POLYGONZ ||
@@ -2007,22 +2033,20 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
}
/* --------------------------------------------------------------------
*/
- /* Get the X/Y bounds. */
+ /* Get the X/Y bounds. */
/* --------------------------------------------------------------------
*/
- memcpy(&(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8);
- memcpy(&(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8);
- memcpy(&(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8);
- memcpy(&(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8);
-
- if (bBigEndian)
- SwapWord(8, &(psShape->dfXMin));
- if (bBigEndian)
- SwapWord(8, &(psShape->dfYMin));
- if (bBigEndian)
- SwapWord(8, &(psShape->dfXMax));
- if (bBigEndian)
- SwapWord(8, &(psShape->dfYMax));
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAPDOUBLE_CPY(&psShape->dfXMin, psSHP->pabyRec + 8 + 4);
+ SHP_SWAPDOUBLE_CPY(&psShape->dfYMin, psSHP->pabyRec + 8 + 12);
+ SHP_SWAPDOUBLE_CPY(&psShape->dfXMax, psSHP->pabyRec + 8 + 20);
+ SHP_SWAPDOUBLE_CPY(&psShape->dfYMax, psSHP->pabyRec + 8 + 28);
+#else
+ memcpy(&psShape->dfXMin, psSHP->pabyRec + 8 + 4, 8);
+ memcpy(&psShape->dfYMin, psSHP->pabyRec + 8 + 12, 8);
+ memcpy(&psShape->dfXMax, psSHP->pabyRec + 8 + 20, 8);
+ memcpy(&psShape->dfYMax, psSHP->pabyRec + 8 + 28, 8);
+#endif
/* --------------------------------------------------------------------
*/
@@ -2030,15 +2054,15 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
/* to proper size. */
/* --------------------------------------------------------------------
*/
- int32 nPoints;
+ uint32_t nPoints;
memcpy(&nPoints, psSHP->pabyRec + 40 + 8, 4);
- int32 nParts;
+ uint32_t nParts;
memcpy(&nParts, psSHP->pabyRec + 36 + 8, 4);
- if (bBigEndian)
- SwapWord(4, &nPoints);
- if (bBigEndian)
- SwapWord(4, &nParts);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(&nPoints);
+ SHP_SWAP32(&nParts);
+#endif
/* nPoints and nParts are unsigned */
if (/* nPoints < 0 || nParts < 0 || */
@@ -2121,7 +2145,7 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
return SHPLIB_NULLPTR;
}
- for (int i = 0; STATIC_CAST(int32, i) < nParts; i++)
+ for (int i = 0; STATIC_CAST(uint32_t, i) < nParts; i++)
psShape->panPartType[i] = SHPP_RING;
/* --------------------------------------------------------------------
@@ -2130,9 +2154,10 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
/* --------------------------------------------------------------------
*/
memcpy(psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts);
- for (int i = 0; STATIC_CAST(int32, i) < nParts; i++) {
- if (bBigEndian)
- SwapWord(4, psShape->panPartStart + i);
+ for (int i = 0; STATIC_CAST(uint32_t, i) < nParts; i++) {
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(psShape->panPartStart + i);
+#endif
/* We check that the offset is inside the vertex array */
if (psShape->panPartStart[i] < 0 ||
@@ -2174,9 +2199,10 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
*/
if (psShape->nSHPType == SHPT_MULTIPATCH) {
memcpy(psShape->panPartType, psSHP->pabyRec + nOffset, 4 * nParts);
- for (int i = 0; STATIC_CAST(int32, i) < nParts; i++) {
- if (bBigEndian)
- SwapWord(4, psShape->panPartType + i);
+ for (int i = 0; STATIC_CAST(uint32_t, i) < nParts; i++) {
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(psShape->panPartType + i);
+#endif
}
nOffset += 4 * nParts;
@@ -2187,16 +2213,17 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
/* Copy out the vertices from the record. */
/* --------------------------------------------------------------------
*/
- for (int i = 0; STATIC_CAST(int32, i) < nPoints; i++) {
+ for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++) {
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAPDOUBLE_CPY(psShape->padfX + i,
+ psSHP->pabyRec + nOffset + i * 16);
+ SHP_SWAPDOUBLE_CPY(psShape->padfY + i,
+ psSHP->pabyRec + nOffset + i * 16 + 8);
+#else
memcpy(psShape->padfX + i, psSHP->pabyRec + nOffset + i * 16, 8);
-
memcpy(psShape->padfY + i, psSHP->pabyRec + nOffset + i * 16 + 8,
8);
-
- if (bBigEndian)
- SwapWord(8, psShape->padfX + i);
- if (bBigEndian)
- SwapWord(8, psShape->padfY + i);
+#endif
}
nOffset += 16 * nPoints;
@@ -2209,19 +2236,23 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
if (psShape->nSHPType == SHPT_POLYGONZ ||
psShape->nSHPType == SHPT_ARCZ ||
psShape->nSHPType == SHPT_MULTIPATCH) {
- memcpy(&(psShape->dfZMin), psSHP->pabyRec + nOffset, 8);
- memcpy(&(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAPDOUBLE_CPY(&psShape->dfZMin, psSHP->pabyRec + nOffset);
+ SHP_SWAPDOUBLE_CPY(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8);
+#else
+ memcpy(&psShape->dfZMin, psSHP->pabyRec + nOffset, 8);
+ memcpy(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8, 8);
- if (bBigEndian)
- SwapWord(8, &(psShape->dfZMin));
- if (bBigEndian)
- SwapWord(8, &(psShape->dfZMax));
+#endif
- for (int i = 0; STATIC_CAST(int32, i) < nPoints; i++) {
+ for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++) {
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAPDOUBLE_CPY(psShape->padfZ + i,
+ psSHP->pabyRec + nOffset + 16 + i * 8);
+#else
memcpy(psShape->padfZ + i,
psSHP->pabyRec + nOffset + 16 + i * 8, 8);
- if (bBigEndian)
- SwapWord(8, psShape->padfZ + i);
+#endif
}
nOffset += 16 + 8 * nPoints;
@@ -2239,19 +2270,22 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
/* --------------------------------------------------------------------
*/
if (nEntitySize >= STATIC_CAST(int, nOffset + 16 + 8 * nPoints)) {
- memcpy(&(psShape->dfMMin), psSHP->pabyRec + nOffset, 8);
- memcpy(&(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8);
-
- if (bBigEndian)
- SwapWord(8, &(psShape->dfMMin));
- if (bBigEndian)
- SwapWord(8, &(psShape->dfMMax));
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAPDOUBLE_CPY(&psShape->dfMMin, psSHP->pabyRec + nOffset);
+ SHP_SWAPDOUBLE_CPY(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8);
+#else
+ memcpy(&psShape->dfMMin, psSHP->pabyRec + nOffset, 8);
+ memcpy(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8, 8);
+#endif
- for (int i = 0; STATIC_CAST(int32, i) < nPoints; i++) {
+ for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++) {
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAPDOUBLE_CPY(psShape->padfM + i,
+ psSHP->pabyRec + nOffset + 16 + i * 8);
+#else
memcpy(psShape->padfM + i,
psSHP->pabyRec + nOffset + 16 + i * 8, 8);
- if (bBigEndian)
- SwapWord(8, psShape->padfM + i);
+#endif
}
psShape->bMeasureIsUsed = TRUE;
}
@@ -2261,7 +2295,7 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
}
/* ==================================================================== */
- /* Extract vertices for a MultiPoint. */
+ /* Extract vertices for a MultiPoint. */
/* ==================================================================== */
else if (psShape->nSHPType == SHPT_MULTIPOINT ||
psShape->nSHPType == SHPT_MULTIPOINTM ||
@@ -2276,11 +2310,12 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
SHPDestroyObject(psShape);
return SHPLIB_NULLPTR;
}
- int32 nPoints;
+ uint32_t nPoints;
memcpy(&nPoints, psSHP->pabyRec + 44, 4);
- if (bBigEndian)
- SwapWord(4, &nPoints);
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAP32(&nPoints);
+#endif
/* nPoints is unsigned */
if (/* nPoints < 0 || */ nPoints > 50 * 1000 * 1000) {
@@ -2346,36 +2381,36 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
return SHPLIB_NULLPTR;
}
- for (int i = 0; STATIC_CAST(int32, i) < nPoints; i++) {
+ for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++) {
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAPDOUBLE_CPY(psShape->padfX + i,
+ psSHP->pabyRec + 48 + 16 * i);
+ SHP_SWAPDOUBLE_CPY(psShape->padfY + i,
+ psSHP->pabyRec + 48 + 16 * i + 8);
+#else
memcpy(psShape->padfX + i, psSHP->pabyRec + 48 + 16 * i, 8);
memcpy(psShape->padfY + i, psSHP->pabyRec + 48 + 16 * i + 8, 8);
-
- if (bBigEndian)
- SwapWord(8, psShape->padfX + i);
- if (bBigEndian)
- SwapWord(8, psShape->padfY + i);
+#endif
}
int nOffset = 48 + 16 * nPoints;
/* --------------------------------------------------------------------
*/
- /* Get the X/Y bounds. */
+ /* Get the X/Y bounds. */
/* --------------------------------------------------------------------
*/
- memcpy(&(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8);
- memcpy(&(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8);
- memcpy(&(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8);
- memcpy(&(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8);
-
- if (bBigEndian)
- SwapWord(8, &(psShape->dfXMin));
- if (bBigEndian)
- SwapWord(8, &(psShape->dfYMin));
- if (bBigEndian)
- SwapWord(8, &(psShape->dfXMax));
- if (bBigEndian)
- SwapWord(8, &(psShape->dfYMax));
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAPDOUBLE_CPY(&psShape->dfXMin, psSHP->pabyRec + 8 + 4);
+ SHP_SWAPDOUBLE_CPY(&psShape->dfYMin, psSHP->pabyRec + 8 + 12);
+ SHP_SWAPDOUBLE_CPY(&psShape->dfXMax, psSHP->pabyRec + 8 + 20);
+ SHP_SWAPDOUBLE_CPY(&psShape->dfYMax, psSHP->pabyRec + 8 + 28);
+#else
+ memcpy(&psShape->dfXMin, psSHP->pabyRec + 8 + 4, 8);
+ memcpy(&psShape->dfYMin, psSHP->pabyRec + 8 + 12, 8);
+ memcpy(&psShape->dfXMax, psSHP->pabyRec + 8 + 20, 8);
+ memcpy(&psShape->dfYMax, psSHP->pabyRec + 8 + 28, 8);
+#endif
/* --------------------------------------------------------------------
*/
@@ -2383,19 +2418,22 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
/* --------------------------------------------------------------------
*/
if (psShape->nSHPType == SHPT_MULTIPOINTZ) {
- memcpy(&(psShape->dfZMin), psSHP->pabyRec + nOffset, 8);
- memcpy(&(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8);
-
- if (bBigEndian)
- SwapWord(8, &(psShape->dfZMin));
- if (bBigEndian)
- SwapWord(8, &(psShape->dfZMax));
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAPDOUBLE_CPY(&psShape->dfZMin, psSHP->pabyRec + nOffset);
+ SHP_SWAPDOUBLE_CPY(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8);
+#else
+ memcpy(&psShape->dfZMin, psSHP->pabyRec + nOffset, 8);
+ memcpy(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8, 8);
+#endif
- for (int i = 0; STATIC_CAST(int32, i) < nPoints; i++) {
+ for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++) {
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAPDOUBLE_CPY(psShape->padfZ + i,
+ psSHP->pabyRec + nOffset + 16 + i * 8);
+#else
memcpy(psShape->padfZ + i,
psSHP->pabyRec + nOffset + 16 + i * 8, 8);
- if (bBigEndian)
- SwapWord(8, psShape->padfZ + i);
+#endif
}
nOffset += 16 + 8 * nPoints;
@@ -2412,19 +2450,22 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
/* --------------------------------------------------------------------
*/
if (nEntitySize >= STATIC_CAST(int, nOffset + 16 + 8 * nPoints)) {
- memcpy(&(psShape->dfMMin), psSHP->pabyRec + nOffset, 8);
- memcpy(&(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8);
-
- if (bBigEndian)
- SwapWord(8, &(psShape->dfMMin));
- if (bBigEndian)
- SwapWord(8, &(psShape->dfMMax));
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAPDOUBLE_CPY(&psShape->dfMMin, psSHP->pabyRec + nOffset);
+ SHP_SWAPDOUBLE_CPY(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8);
+#else
+ memcpy(&psShape->dfMMin, psSHP->pabyRec + nOffset, 8);
+ memcpy(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8, 8);
+#endif
- for (int i = 0; STATIC_CAST(int32, i) < nPoints; i++) {
+ for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++) {
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAPDOUBLE_CPY(psShape->padfM + i,
+ psSHP->pabyRec + nOffset + 16 + i * 8);
+#else
memcpy(psShape->padfM + i,
psSHP->pabyRec + nOffset + 16 + i * 8, 8);
- if (bBigEndian)
- SwapWord(8, psShape->padfM + i);
+#endif
}
psShape->bMeasureIsUsed = TRUE;
}
@@ -2465,13 +2506,13 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
SHPDestroyObject(psShape);
return SHPLIB_NULLPTR;
}
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAPDOUBLE_CPY(psShape->padfX, psSHP->pabyRec + 12);
+ SHP_SWAPDOUBLE_CPY(psShape->padfY, psSHP->pabyRec + 20);
+#else
memcpy(psShape->padfX, psSHP->pabyRec + 12, 8);
memcpy(psShape->padfY, psSHP->pabyRec + 20, 8);
-
- if (bBigEndian)
- SwapWord(8, psShape->padfX);
- if (bBigEndian)
- SwapWord(8, psShape->padfY);
+#endif
int nOffset = 20 + 8;
@@ -2481,10 +2522,11 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
/* --------------------------------------------------------------------
*/
if (psShape->nSHPType == SHPT_POINTZ) {
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAPDOUBLE_CPY(psShape->padfZ, psSHP->pabyRec + nOffset);
+#else
memcpy(psShape->padfZ, psSHP->pabyRec + nOffset, 8);
-
- if (bBigEndian)
- SwapWord(8, psShape->padfZ);
+#endif
nOffset += 8;
}
@@ -2498,10 +2540,11 @@ SHPObject SHPAPI_CALL1(*) SHPReadObject(SHPHandle psSHP, int hEntity)
/* --------------------------------------------------------------------
*/
if (nEntitySize >= nOffset + 8) {
+#if defined(SHP_BIG_ENDIAN)
+ SHP_SWAPDOUBLE_CPY(psShape->padfM, psSHP->pabyRec + nOffset);
+#else
memcpy(psShape->padfM, psSHP->pabyRec + nOffset, 8);
-
- if (bBigEndian)
- SwapWord(8, psShape->padfM);
+#endif
psShape->bMeasureIsUsed = TRUE;
}
@@ -2739,8 +2782,9 @@ static int SHPRewindIsInnerRing(const SHPObject *psObject, int iOpRing,
/* specification. */
/************************************************************************/
-int SHPAPI_CALL SHPRewindObject(CPL_UNUSED SHPHandle hSHP, SHPObject *psObject)
+int SHPAPI_CALL SHPRewindObject(const SHPHandle hSHP, SHPObject *psObject)
{
+ (void)hSHP;
/* -------------------------------------------------------------------- */
/* Do nothing if this is not a polygon object. */
/* -------------------------------------------------------------------- */
diff --git a/lib/gis/testsuite/test_parser_json.py b/lib/gis/testsuite/test_parser_json.py
index 918fb453d04..74ff9673c7f 100644
--- a/lib/gis/testsuite/test_parser_json.py
+++ b/lib/gis/testsuite/test_parser_json.py
@@ -10,11 +10,14 @@
import subprocess
from grass.gunittest.case import TestCase
+from grass.gunittest.utils import xfail_windows
from grass.script import decode
import json
class TestParserJson(TestCase):
+
+ @xfail_windows
def test_r_slope_aspect_json(self):
args = [
"r.slope.aspect",
@@ -58,6 +61,7 @@ def test_r_slope_aspect_json(self):
self.assertEqual(json_code["inputs"], inputs)
self.assertEqual(json_code["outputs"], outputs)
+ @xfail_windows
def test_v_out_ascii(self):
args = [
"v.out.ascii",
@@ -91,6 +95,7 @@ def test_v_out_ascii(self):
self.assertEqual(json_code["inputs"], inputs)
self.assertEqual(json_code["outputs"], outputs)
+ @xfail_windows
def test_v_info(self):
args = ["v.info", "map=hospitals@PERMANENT", "-c", "--json"]
diff --git a/lib/imagery/testsuite/test_imagery_sigfile.py b/lib/imagery/testsuite/test_imagery_sigfile.py
index 104cd5a59b4..8d0e288561d 100644
--- a/lib/imagery/testsuite/test_imagery_sigfile.py
+++ b/lib/imagery/testsuite/test_imagery_sigfile.py
@@ -9,10 +9,10 @@
for details
"""
-import os
import stat
import ctypes
import shutil
+from pathlib import Path
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
@@ -80,7 +80,7 @@ def test_roundtrip_signature_v1_norgb_one_label(self):
# Write signatures to file
p_new_sigfile = I_fopen_signature_file_new(self.sig_name)
- sig_stat = os.stat(f"{self.sig_dir}/sig")
+ sig_stat = Path(self.sig_dir, "sig").stat()
self.assertTrue(stat.S_ISREG(sig_stat.st_mode))
I_write_signatures(p_new_sigfile, ctypes.byref(So))
self.libc.fclose(p_new_sigfile)
@@ -136,7 +136,7 @@ def test_broken_signature_v1_norgb(self):
# Write signatures to file
p_new_sigfile = I_fopen_signature_file_new(self.sig_name)
- sig_stat = os.stat(f"{self.sig_dir}/sig")
+ sig_stat = Path(self.sig_dir, "sig").stat()
self.assertTrue(stat.S_ISREG(sig_stat.st_mode))
I_write_signatures(p_new_sigfile, ctypes.byref(So))
self.libc.fclose(p_new_sigfile)
@@ -188,7 +188,7 @@ def test_roundtrip_signature_v1_norgb_two_labelss(self):
# Write signatures to file
p_new_sigfile = I_fopen_signature_file_new(self.sig_name)
- sig_stat = os.stat(f"{self.sig_dir}/sig")
+ sig_stat = Path(self.sig_dir, "sig").stat()
self.assertTrue(stat.S_ISREG(sig_stat.st_mode))
I_write_signatures(p_new_sigfile, ctypes.byref(So))
self.libc.fclose(p_new_sigfile)
@@ -277,7 +277,7 @@ def test_roundtrip_signature_v2_norgb_two_labels_oclass(self):
# Write signatures to file
p_new_sigfile = I_fopen_signature_file_new(self.sig_name)
- sig_stat = os.stat(f"{self.sig_dir}/sig")
+ sig_stat = Path(self.sig_dir, "sig").stat()
self.assertTrue(stat.S_ISREG(sig_stat.st_mode))
I_write_signatures(p_new_sigfile, ctypes.byref(So))
self.libc.fclose(p_new_sigfile)
diff --git a/lib/imagery/testsuite/test_imagery_signature_management.py b/lib/imagery/testsuite/test_imagery_signature_management.py
index 7b1dc8bdc57..42c55c45123 100644
--- a/lib/imagery/testsuite/test_imagery_signature_management.py
+++ b/lib/imagery/testsuite/test_imagery_signature_management.py
@@ -15,6 +15,7 @@
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
+from grass.gunittest.utils import xfail_windows
from grass.script.core import tempname
import grass.script as gs
@@ -44,16 +45,19 @@
class GetSignaturesDirTestCase(TestCase):
+ @xfail_windows
def test_get_sig(self):
cdir = ctypes.create_string_buffer(GNAME_MAX)
I_get_signatures_dir(cdir, I_SIGFILE_TYPE_SIG)
self.assertEqual(utils.decode(cdir.value), f"signatures{HOST_DIRSEP}sig")
+ @xfail_windows
def test_get_sigset(self):
cdir = ctypes.create_string_buffer(GNAME_MAX)
I_get_signatures_dir(cdir, I_SIGFILE_TYPE_SIGSET)
self.assertEqual(utils.decode(cdir.value), f"signatures{HOST_DIRSEP}sigset")
+ @xfail_windows
def test_get_libsvm(self):
elem = ctypes.create_string_buffer(GNAME_MAX)
I_get_signatures_dir(elem, I_SIGFILE_TYPE_LIBSVM)
diff --git a/lib/imagery/testsuite/test_imagery_sigsetfile.py b/lib/imagery/testsuite/test_imagery_sigsetfile.py
index b8bcb566520..d526e920885 100644
--- a/lib/imagery/testsuite/test_imagery_sigsetfile.py
+++ b/lib/imagery/testsuite/test_imagery_sigsetfile.py
@@ -9,10 +9,10 @@
for details
"""
-import os
import stat
import ctypes
import shutil
+from pathlib import Path
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
@@ -84,7 +84,7 @@ def test_roundtrip_sigset_v1_one_label(self):
# Write signatures to file
p_new_sigfile = I_fopen_sigset_file_new(self.sig_name)
- sig_stat = os.stat(f"{self.sig_dir}/sig")
+ sig_stat = Path(self.sig_dir, "sig").stat()
self.assertTrue(stat.S_ISREG(sig_stat.st_mode))
I_WriteSigSet(p_new_sigfile, ctypes.byref(So))
self.libc.fclose(p_new_sigfile)
@@ -139,7 +139,7 @@ def test_read_fail_sigset_v1_one_label(self):
# Write signatures to file
p_new_sigfile = I_fopen_sigset_file_new(self.sig_name)
- sig_stat = os.stat(f"{self.sig_dir}/sig")
+ sig_stat = Path(self.sig_dir, "sig").stat()
self.assertTrue(stat.S_ISREG(sig_stat.st_mode))
I_WriteSigSet(p_new_sigfile, ctypes.byref(So))
self.libc.fclose(p_new_sigfile)
@@ -183,7 +183,7 @@ def test_roundtrip_sigset_v1_two_labels(self):
# Write signatures to file
p_new_sigfile = I_fopen_sigset_file_new(self.sig_name)
- sig_stat = os.stat(f"{self.sig_dir}/sig")
+ sig_stat = Path(self.sig_dir, "sig").stat()
self.assertTrue(stat.S_ISREG(sig_stat.st_mode))
I_WriteSigSet(p_new_sigfile, ctypes.byref(So))
self.libc.fclose(p_new_sigfile)
diff --git a/lib/init/grass.py b/lib/init/grass.py
index 30c0a1a68ba..233bcf53460 100755
--- a/lib/init/grass.py
+++ b/lib/init/grass.py
@@ -400,7 +400,7 @@ def create_grass_config_dir():
os.makedirs(directory)
except OSError as e:
# Can happen as a race condition
- if not e.errno == errno.EEXIST or not os.path.isdir(directory):
+ if e.errno != errno.EEXIST or not os.path.isdir(directory):
fatal(
_(
"Failed to create configuration directory '{}' with error: {}"
@@ -496,7 +496,7 @@ def create_gisrc(tmpdir, gisrcrc):
def read_gisrc(filename):
kv = {}
try:
- f = open(filename, "r")
+ f = open(filename)
except OSError:
return kv
@@ -522,7 +522,7 @@ def write_gisrcrc(gisrcrc, gisrc, skip_variable=None):
"""Reads gisrc file and write to gisrcrc"""
debug("Reading %s" % gisrc)
number = 0
- with open(gisrc, "r") as f:
+ with open(gisrc) as f:
lines = f.readlines()
for line in lines:
if skip_variable in line:
@@ -535,7 +535,7 @@ def write_gisrcrc(gisrcrc, gisrc, skip_variable=None):
def read_env_file(path):
kv = {}
- f = open(path, "r")
+ f = open(path)
for line in f:
k, v = line.split(":", 1)
kv[k.strip()] = v.strip()
@@ -620,7 +620,7 @@ def create_initial_gisrc(filename):
LOCATION_NAME:
MAPSET:
"""
- % os.getcwd()
+ % Path.cwd()
)
writefile(filename, s)
@@ -789,7 +789,7 @@ def set_mapset(
# non-empty element as the last element (which is good for both mapset
# and location split)
if arg == ".":
- arg = os.getcwd()
+ arg = str(Path.cwd())
elif not os.path.isabs(arg):
arg = os.path.abspath(arg)
if arg.endswith(os.path.sep):
@@ -1098,7 +1098,7 @@ def set_language(grass_config_dir):
# Override value is stored in wxGUI preferences file.
try:
- with open(os.path.join(grass_config_dir, "wx.json"), "r") as json_file:
+ with open(os.path.join(grass_config_dir, "wx.json")) as json_file:
try:
language = json.load(json_file)["language"]["locale"]["lc_all"]
except KeyError:
@@ -1281,65 +1281,6 @@ def set_language(grass_config_dir):
gettext.install("grasslibs", gpath("locale"))
-def lock_mapset(mapset_path, force_gislock_removal, user):
- """Lock the mapset and return name of the lock file
-
- Behavior on error must be changed somehow; now it fatals but GUI case is
- unresolved.
- """
- if not os.path.exists(mapset_path):
- fatal(_("Path '%s' doesn't exist") % mapset_path)
- if not os.access(mapset_path, os.W_OK):
- error = _("Path '%s' not accessible.") % mapset_path
- stat_info = os.stat(mapset_path)
- mapset_uid = stat_info.st_uid
- if mapset_uid != os.getuid():
- # GTC %s is mapset's folder path
- error = "%s\n%s" % (
- error,
- _("You are not the owner of '%s'.") % mapset_path,
- )
- fatal(error)
- # Check for concurrent use
- lockfile = os.path.join(mapset_path, ".gislock")
- ret = call([gpath("etc", "lock"), lockfile, "%d" % os.getpid()])
- msg = None
- if ret == 2:
- if not force_gislock_removal:
- msg = _(
- "%(user)s is currently running GRASS in selected mapset"
- " (file %(file)s found). Concurrent use not allowed.\n"
- "You can force launching GRASS using -f flag"
- " (note that you need permission for this operation)."
- " Have another look in the processor "
- "manager just to be sure..."
- ) % {"user": user, "file": lockfile}
-
- else:
- try_remove(lockfile)
- message(
- _(
- "%(user)s is currently running GRASS in selected mapset"
- " (file %(file)s found). Forcing to launch GRASS..."
- )
- % {"user": user, "file": lockfile}
- )
- elif ret != 0:
- msg = (
- _("Unable to properly access '%s'.\nPlease notify system personnel.")
- % lockfile
- )
-
- if msg:
- raise Exception(msg)
- debug(
- "Mapset <{mapset}> locked using '{lockfile}'".format(
- mapset=mapset_path, lockfile=lockfile
- )
- )
- return lockfile
-
-
# TODO: the gisrcrc here does not make sense, remove it from load_gisrc
def unlock_gisrc_mapset(gisrc, gisrcrc):
"""Unlock mapset from the gisrc file"""
@@ -1690,7 +1631,7 @@ def sh_like_startup(location, location_name, grass_env_file, sh):
# save command history in mapset dir and remember more
# bash history file handled in specific_addition
- if not sh == "bash":
+ if sh != "bash":
os.environ["HISTFILE"] = os.path.join(location, sh_history)
# instead of changing $HOME, start bash with:
@@ -2421,14 +2362,16 @@ def main():
location = mapset_settings.full_mapset
+ from grass.app.data import lock_mapset, MapsetLockingException
+
try:
# check and create .gislock file
lock_mapset(
- mapset_settings.full_mapset,
- user=user,
- force_gislock_removal=params.force_gislock_removal,
+ mapset_path=mapset_settings.full_mapset,
+ force_lock_removal=params.force_gislock_removal,
+ message_callback=message,
)
- except Exception as e:
+ except MapsetLockingException as e:
fatal(e.args[0])
sys.exit(_("Exiting..."))
diff --git a/lib/init/testsuite/test_grass_tmp_mapset.py b/lib/init/testsuite/test_grass_tmp_mapset.py
index 7a488dadb5f..9379957cd3f 100644
--- a/lib/init/testsuite/test_grass_tmp_mapset.py
+++ b/lib/init/testsuite/test_grass_tmp_mapset.py
@@ -18,6 +18,7 @@
import os
import shutil
import subprocess
+from grass.gunittest.utils import xfail_windows
# Note that unlike rest of GRASS GIS, here we are using unittest package
@@ -43,6 +44,7 @@ def tearDown(self):
"""Deletes the location"""
shutil.rmtree(self.location, ignore_errors=True)
+ @xfail_windows
def test_command_runs(self):
"""Check that correct parameters are accepted"""
return_code = subprocess.call(
@@ -57,6 +59,7 @@ def test_command_runs(self):
),
)
+ @xfail_windows
def test_command_fails_without_location(self):
"""Check that the command fails with a nonexistent location"""
return_code = subprocess.call(
@@ -78,6 +81,7 @@ def test_command_fails_without_location(self):
),
)
+ @xfail_windows
def test_mapset_metadata_correct(self):
"""Check that metadata is readable and have expected value (XY CRS)"""
output = subprocess.check_output(
@@ -91,6 +95,7 @@ def test_mapset_metadata_correct(self):
),
)
+ @xfail_windows
def test_mapset_deleted(self):
"""Check that mapset is deleted at the end of execution"""
subprocess.check_call(
diff --git a/lib/raster/mask_info.c b/lib/raster/mask_info.c
index 792510879dc..1a11da972f8 100644
--- a/lib/raster/mask_info.c
+++ b/lib/raster/mask_info.c
@@ -70,3 +70,13 @@ int Rast__mask_info(char *name, char *mapset)
return 1;
}
+
+/**
+ * @brief Check presence of 2D raster mask
+ *
+ * @return true if mask is present, false otherwise
+ */
+bool Rast_mask_is_present(void)
+{
+ return G_find_raster("MASK", G_mapset()) != NULL;
+}
diff --git a/locale/grass_po_stats.py b/locale/grass_po_stats.py
index b5f15026691..0c49e44bc8d 100644
--- a/locale/grass_po_stats.py
+++ b/locale/grass_po_stats.py
@@ -21,10 +21,12 @@
import subprocess
import sys
+from pathlib import Path
+
def read_po_files(inputdirpath):
"""Return a dictionary with for each language the list of *.po files"""
- originalpath = os.getcwd()
+ originalpath = Path.cwd()
os.chdir(inputdirpath)
languages = {}
for pofile in sorted(glob.glob("*.po")):
@@ -144,7 +146,7 @@ def writejson(stats, outfile):
fout.close()
try:
os.remove("messages.mo")
- except:
+ except OSError:
pass
diff --git a/man/build_class_graphical.py b/man/build_class_graphical.py
index 35b2a731cfe..554c950d3a8 100644
--- a/man/build_class_graphical.py
+++ b/man/build_class_graphical.py
@@ -91,10 +91,7 @@
def file_matches(filename, patterns):
- for pattern in patterns:
- if fnmatch.fnmatch(filename, pattern):
- return True
- return False
+ return any(fnmatch.fnmatch(filename, pattern) for pattern in patterns)
def starts_with_module(string, module) -> bool:
diff --git a/man/build_full_index.py b/man/build_full_index.py
index ead5167cbb1..7d2ce03e342 100644
--- a/man/build_full_index.py
+++ b/man/build_full_index.py
@@ -9,6 +9,8 @@
import sys
import os
+from operator import itemgetter
+
from build_html import (
html_dir,
grass_version,
@@ -50,7 +52,7 @@
prefix = cmd.split(".")[0]
if prefix not in [item[0] for item in classes]:
classes.append((prefix, class_labels.get(prefix, prefix)))
-classes.sort(key=lambda tup: tup[0])
+classes.sort(key=itemgetter(0))
# begin full index:
filename = "full_index.html"
diff --git a/man/build_html.py b/man/build_html.py
index daa38b557f4..c89df29e290 100644
--- a/man/build_html.py
+++ b/man/build_html.py
@@ -393,7 +393,7 @@ def check_for_desc_override(basename):
def read_file(name):
- f = open(name, "r")
+ f = open(name)
s = f.read()
f.close()
return s
@@ -476,7 +476,7 @@ def write_html_footer(f, index_url, year=None):
def get_desc(cmd):
- f = open(cmd, "r")
+ f = open(cmd)
while True:
line = f.readline()
if not line:
diff --git a/man/build_keywords.py b/man/build_keywords.py
index 6070fad1a28..b0dfe95e0e3 100644
--- a/man/build_keywords.py
+++ b/man/build_keywords.py
@@ -158,7 +158,7 @@ def get_module_man_html_file_path(module):
for k in sorted(char_list.keys()):
test_length += 1
# toc += '%s' % (char_list[k], k)
- if test_length % 4 == 0 and not test_length == all_keys:
+ if test_length % 4 == 0 and test_length != all_keys:
toc += '\n%s, ' % (char_list[k], k)
elif test_length % 4 == 0 and test_length == all_keys:
toc += '\n%s' % (char_list[k], k)
diff --git a/man/build_manual_gallery.py b/man/build_manual_gallery.py
index 3d381e27877..51a6fbcd312 100755
--- a/man/build_manual_gallery.py
+++ b/man/build_manual_gallery.py
@@ -102,10 +102,7 @@ def img_in_html(filename, imagename) -> bool:
def file_matches(filename, patterns):
- for pattern in patterns:
- if fnmatch.fnmatch(filename, pattern):
- return True
- return False
+ return any(fnmatch.fnmatch(filename, pattern) for pattern in patterns)
def get_files(directory, patterns, exclude_patterns):
diff --git a/man/build_rest.py b/man/build_rest.py
index ea1dd7692d2..473827fcc86 100644
--- a/man/build_rest.py
+++ b/man/build_rest.py
@@ -271,7 +271,7 @@ def check_for_desc_override(basename):
def read_file(name):
- f = open(name, "r")
+ f = open(name)
s = f.read()
f.close()
return s
@@ -337,7 +337,7 @@ def write_rest_footer(f, index_url):
def get_desc(cmd):
- f = open(cmd, "r")
+ f = open(cmd)
while True:
line = f.readline()
if not line:
diff --git a/ps/ps.map/ps_vpoints.c b/ps/ps.map/ps_vpoints.c
index 80b29a349b1..cd320b46f11 100644
--- a/ps/ps.map/ps_vpoints.c
+++ b/ps/ps.map/ps_vpoints.c
@@ -268,5 +268,6 @@ int PS_vpoints_plot(struct Map_info *P_map, int vec)
} /* for (line) */
fprintf(PS.fp, "\n");
+ Vect_destroy_cats_struct(Cats);
return 0;
}
diff --git a/pyproject.toml b/pyproject.toml
index 649e309a73f..c6cfac4185b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -145,7 +145,6 @@ ignore = [
"FBT001", # boolean-type-hint-positional-argument
"FBT002", # boolean-default-value-positional-argument
"FBT003", # boolean-positional-value-in-call
- "FURB118", # reimplemented-operator
"I001", # unsorted-imports
"ISC003", # explicit-string-concatenation
"PERF203", # try-except-in-loop
@@ -195,12 +194,10 @@ ignore = [
"PTH106", # os-rmdir
"PTH107", # os-remove
"PTH108", # os-unlink
- "PTH109", # os-getcwd
"PTH110", # os-path-exists
"PTH111", # os-path-expanduser
"PTH112", # os-path-isdir
"PTH113", # os-path-isfile
- "PTH116", # os-stat
"PTH117", # os-path-isabs
"PTH118", # os-path-join
"PTH119", # os-path-basename
@@ -246,17 +243,12 @@ ignore = [
"S606", # start-process-with-no-shell
"S607", # start-process-with-partial-path
"S608", # hardcoded-sql-expression
- "SIM101", # duplicate-isinstance-call
"SIM102", # collapsible-if
"SIM105", # suppressible-exception
"SIM108", # if-else-block-instead-of-if-exp
- "SIM109", # compare-with-tuple
- "SIM110", # reimplemented-builtin
"SIM113", # enumerate-for-loop
- "SIM114", # if-with-same-arms
"SIM116", # if-else-block-instead-of-dict-lookup
"SIM118", # in-dict-keys
- "SIM201", # negate-equal-op
"SIM223", # expr-and-false
"SIM401", # if-else-block-instead-of-dict-get
"SLF001", # private-member-access
@@ -266,7 +258,6 @@ ignore = [
"TRY201", # verbose-raise
"TRY300", # try-consider-else
"TRY301", # raise-within-try
- "UP015", # redundant-open-modes
"UP030", # format-literals
"UP031", # printf-string-formatting
"UP032", # f-string
diff --git a/python/grass/app/data.py b/python/grass/app/data.py
index 439a6c3c4d1..10997cd59e8 100644
--- a/python/grass/app/data.py
+++ b/python/grass/app/data.py
@@ -15,8 +15,12 @@
import os
import tempfile
import getpass
+import subprocess
import sys
from shutil import copytree, ignore_patterns
+from pathlib import Path
+
+import grass.script as gs
import grass.grassdb.config as cfg
from grass.grassdb.checks import is_location_valid
@@ -162,3 +166,69 @@ def ensure_default_data_hierarchy():
mapset_path = os.path.join(gisdbase, location, mapset)
return gisdbase, location, mapset, mapset_path
+
+
+class MapsetLockingException(Exception):
+ pass
+
+
+def lock_mapset(mapset_path, force_lock_removal, message_callback):
+ """Acquire a lock for a mapset and return name of new lock file
+
+ Raises MapsetLockingException when it is not possible to acquire a lock for the
+ given mapset either because of existing lock or due to insufficient permissions.
+ A corresponding localized message is given in the exception.
+
+ A *message_callback* is a function which will be called to report messages about
+ certain states. Specifically, the function is called when forcibly unlocking the
+ mapset.
+
+ Assumes that the runtime is set up (specifically that GISBASE is in
+ the environment).
+ """
+ if not os.path.exists(mapset_path):
+ raise MapsetLockingException(_("Path '{}' doesn't exist").format(mapset_path))
+ if not os.access(mapset_path, os.W_OK):
+ error = _("Path '{}' not accessible.").format(mapset_path)
+ stat_info = Path(mapset_path).stat()
+ mapset_uid = stat_info.st_uid
+ if mapset_uid != os.getuid():
+ error = "{error}\n{detail}".format(
+ error=error,
+ detail=_("You are not the owner of '{}'.").format(mapset_path),
+ )
+ raise MapsetLockingException(error)
+ # Check for concurrent use
+ lockfile = os.path.join(mapset_path, ".gislock")
+ locker_path = os.path.join(os.environ["GISBASE"], "etc", "lock")
+ ret = subprocess.run(
+ [locker_path, lockfile, "%d" % os.getpid()], check=False
+ ).returncode
+ msg = None
+ if ret == 2:
+ if not force_lock_removal:
+ msg = _(
+ "{user} is currently running GRASS in selected mapset"
+ " (file {file} found). Concurrent use of one mapset not allowed.\n"
+ "You can force launching GRASS using -f flag"
+ " (assuming your have sufficient access permissions)."
+ " Confirm in a process manager "
+ "that there is no other process using the mapset."
+ ).format(user=Path(lockfile).owner(), file=lockfile)
+ else:
+ message_callback(
+ _(
+ "{user} is currently running GRASS in selected mapset"
+ " (file {file} found), but forcing to launch GRASS anyway..."
+ ).format(user=Path(lockfile).owner(), file=lockfile)
+ )
+ gs.try_remove(lockfile)
+ elif ret != 0:
+ msg = _(
+ "Unable to properly access lock file '{name}'.\n"
+ "Please resolve this with your system administrator."
+ ).format(name=lockfile)
+
+ if msg:
+ raise MapsetLockingException(msg)
+ return lockfile
diff --git a/python/grass/grassdb/checks.py b/python/grass/grassdb/checks.py
index 4de65a4cba3..6916ea2748a 100644
--- a/python/grass/grassdb/checks.py
+++ b/python/grass/grassdb/checks.py
@@ -113,7 +113,7 @@ def is_current_user_mapset_owner(mapset_path):
# Mapset needs to be owned by user.
if sys.platform == "win32":
return True
- stat_info = os.stat(mapset_path)
+ stat_info = Path(mapset_path).stat()
mapset_uid = stat_info.st_uid
return mapset_uid == os.getuid()
@@ -159,7 +159,7 @@ def is_first_time_user():
genv = gisenv()
if "LAST_MAPSET_PATH" in genv.keys():
return genv["LAST_MAPSET_PATH"] == os.path.join(
- os.getcwd(), cfg.unknown_location, cfg.unknown_mapset
+ Path.cwd(), cfg.unknown_location, cfg.unknown_mapset
)
return False
diff --git a/python/grass/grassdb/history.py b/python/grass/grassdb/history.py
index 60b5459700d..d7c22eb36fc 100644
--- a/python/grass/grassdb/history.py
+++ b/python/grass/grassdb/history.py
@@ -81,9 +81,7 @@ def _read_from_plain_text(history_path):
stores only executed commands."""
content_list = []
try:
- with open(
- history_path, encoding="utf-8", mode="r", errors="replace"
- ) as file_history:
+ with open(history_path, encoding="utf-8", errors="replace") as file_history:
content_list = [
{"command": line.strip(), "command_info": None} for line in file_history
]
@@ -287,7 +285,7 @@ def _add_entry_to_JSON(history_path, entry):
:param dict entry: entry consisting of 'command' and 'command_info' keys
"""
try:
- with open(history_path, encoding="utf-8", mode="r") as file_history:
+ with open(history_path, encoding="utf-8") as file_history:
existing_data = json.load(file_history)
except (OSError, ValueError):
existing_data = []
diff --git a/python/grass/grassdb/testsuite/test_manage.py b/python/grass/grassdb/testsuite/test_manage.py
index a2788d4f1a8..43b8945bd31 100644
--- a/python/grass/grassdb/testsuite/test_manage.py
+++ b/python/grass/grassdb/testsuite/test_manage.py
@@ -18,6 +18,7 @@
from grass.gunittest.case import TestCase
from grass.gunittest.gmodules import call_module
from grass.gunittest.main import test
+from grass.gunittest.utils import xfail_windows
class TestMapsetPath(TestCase):
@@ -38,6 +39,7 @@ def test_mapset_from_path_object(self):
self.assertEqual(mapset_path.mapset, mapset_name)
self.assertEqual(mapset_path.path, Path(path) / location_name / mapset_name)
+ @xfail_windows
def test_mapset_from_str(self):
"""Check with path from str and database directory as Path"""
path = "does/not/exist"
@@ -60,6 +62,7 @@ def test_mapset_from_str(self):
class TestSplitMapsetPath(TestCase):
"""Check that split works with different parameters"""
+ @xfail_windows
def test_split_path(self):
"""Check that pathlib.Path is correctly split"""
ref_db = "does/not/exist"
@@ -71,6 +74,7 @@ def test_split_path(self):
self.assertEqual(new_location, ref_location)
self.assertEqual(new_mapset, ref_mapset)
+ @xfail_windows
def test_split_str(self):
"""Check that path as str is correctly split"""
ref_db = "does/not/exist"
@@ -82,6 +86,7 @@ def test_split_str(self):
self.assertEqual(new_location, ref_location)
self.assertEqual(new_mapset, ref_mapset)
+ @xfail_windows
def test_split_str_trailing_slash(self):
"""Check that path as str with a trailing slash is correctly split"""
ref_db = "does/not/exist"
diff --git a/python/grass/gunittest/case.py b/python/grass/gunittest/case.py
index da2c7486d16..b5563e47e9e 100644
--- a/python/grass/gunittest/case.py
+++ b/python/grass/gunittest/case.py
@@ -695,7 +695,7 @@ def assertFileMd5(self, filename, md5, text=False, msg=None):
actual = text_file_md5(filename)
else:
actual = file_md5(filename)
- if not actual == md5:
+ if actual != md5:
standardMsg = (
"File <{name}> does not have the right MD5 sum.\n"
"Expected is <{expected}>,"
diff --git a/python/grass/gunittest/checkers.py b/python/grass/gunittest/checkers.py
index fb46a1deb66..c63f6d08ff0 100644
--- a/python/grass/gunittest/checkers.py
+++ b/python/grass/gunittest/checkers.py
@@ -638,7 +638,7 @@ def text_file_md5(
if prepend_lines:
for line in prepend_lines:
hasher.update(encode(line))
- with open(filename, "r") as f:
+ with open(filename) as f:
for line in f:
# replace platform newlines by standard newline
if os.linesep != "\n":
diff --git a/python/grass/gunittest/reporters.py b/python/grass/gunittest/reporters.py
index 650480a234c..9535e07f871 100644
--- a/python/grass/gunittest/reporters.py
+++ b/python/grass/gunittest/reporters.py
@@ -51,7 +51,7 @@ def replace_in_file(file_path, pattern, repl):
"""
# using tmp file to store the replaced content
tmp_file_path = file_path + ".tmp"
- with open(file_path, "r") as old_file, open(tmp_file_path, "w") as new_file:
+ with open(file_path) as old_file, open(tmp_file_path, "w") as new_file:
for line in old_file:
new_file.write(re.sub(pattern=pattern, string=line, repl=repl))
# remove old file since it must not exist for rename/move
diff --git a/python/grass/gunittest/testsuite/test_assertions.py b/python/grass/gunittest/testsuite/test_assertions.py
index 3af538132f3..ed27c0e54fb 100644
--- a/python/grass/gunittest/testsuite/test_assertions.py
+++ b/python/grass/gunittest/testsuite/test_assertions.py
@@ -11,6 +11,7 @@
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
from grass.gunittest.gmodules import SimpleModule
+from grass.gunittest.utils import xfail_windows
class TestTextAssertions(TestCase):
@@ -34,6 +35,7 @@ def test_assertLooksLike(self):
def test_assertLooksLike_multiline(self):
self.assertLooksLike("a=123\nb=456\nc=789", "a=...\nb=...\nc=...")
+ @xfail_windows
def test_assertLooksLike_multiline_platform_dependent(self):
self.assertLooksLike(
"a=123\nb=456\nc=789", "a=...{nl}b=...{nl}c=...".format(nl=os.linesep)
@@ -384,6 +386,7 @@ def test_assertFileExists_empty_file(self):
self.failureException, self.assertFileExists, filename=self.emtpy_file
)
+ @xfail_windows
def test_assertFileMd5(self):
self.assertFileMd5(filename=self.file_with_md5, md5=self.file_md5)
self.assertRaises(
diff --git a/python/grass/gunittest/testsuite/test_assertions_vect.py b/python/grass/gunittest/testsuite/test_assertions_vect.py
index 8b3288ad2ca..0a73580ba58 100644
--- a/python/grass/gunittest/testsuite/test_assertions_vect.py
+++ b/python/grass/gunittest/testsuite/test_assertions_vect.py
@@ -5,6 +5,7 @@
from grass.exceptions import CalledModuleError
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
+from grass.gunittest.utils import xfail_windows
V_UNIVAR_SCHOOLS_WIDTH_SUBSET = """n=144
@@ -281,6 +282,7 @@ def test_assertVectorAsciiEqualsVectorAscii_diff_content(self):
self.assertFileExists(self.simple_base_file)
self.assertFileExists(self.simple_modified_file)
+ @xfail_windows
def test_assertVectorEqualsAscii_by_import(self):
amap = "simple_vector_map_imported_base"
self.runModule(
diff --git a/python/grass/gunittest/testsuite/test_checkers.py b/python/grass/gunittest/testsuite/test_checkers.py
index d35ffcb19d8..b11b25778a3 100644
--- a/python/grass/gunittest/testsuite/test_checkers.py
+++ b/python/grass/gunittest/testsuite/test_checkers.py
@@ -24,6 +24,7 @@
file_md5,
text_file_md5,
)
+from grass.gunittest.utils import xfail_windows
class TestValuesEqual(TestCase):
@@ -386,6 +387,7 @@ def tearDownClass(cls):
try_remove(cls.correct_file_name_unix_nl)
try_remove(cls.wrong_file_name)
+ @xfail_windows
def test_text_file_binary(self):
r"""File with ``\n`` (LF) newlines as binary (MD5 has ``\n``)."""
self.assertEqual(
diff --git a/python/grass/gunittest/utils.py b/python/grass/gunittest/utils.py
index c1afea3d5ad..5313dbb9bc8 100644
--- a/python/grass/gunittest/utils.py
+++ b/python/grass/gunittest/utils.py
@@ -14,6 +14,8 @@
from pathlib import Path
import shutil
import sys
+from unittest import expectedFailure
+import warnings
def ensure_dir(directory):
@@ -80,3 +82,17 @@ def safe_repr(obj, short=False):
if not short or len(result) < _MAX_LENGTH:
return result
return result[:_MAX_LENGTH] + " [truncated]..."
+
+
+def xfail_windows(test_item):
+ """Marks a test as an expected failure or error only on Windows
+ Equivalent to applying @unittest.expectedFailure only when running
+ on Windows.
+ """
+ if not sys.platform.startswith("win"):
+ return lambda func: func
+ warnings.warn(
+ "Once the test is fixed and passing, remove the @xfail_windows decorator",
+ stacklevel=2,
+ )
+ return expectedFailure(test_item)
diff --git a/python/grass/imaging/images2ims.py b/python/grass/imaging/images2ims.py
index 31088a22f2e..662e3eb46e0 100644
--- a/python/grass/imaging/images2ims.py
+++ b/python/grass/imaging/images2ims.py
@@ -31,6 +31,7 @@
"""
import os
+from operator import itemgetter
try:
import numpy as np
@@ -214,7 +215,7 @@ def readIms(filename, asNumpy=True):
images.append((im.copy(), nr))
# Sort images
- images.sort(key=lambda x: x[1])
+ images.sort(key=itemgetter(1))
images = [im[0] for im in images]
# Convert to numpy if needed
diff --git a/python/grass/jupyter/baseseriesmap.py b/python/grass/jupyter/baseseriesmap.py
index 634e172f3c5..4497b16593d 100644
--- a/python/grass/jupyter/baseseriesmap.py
+++ b/python/grass/jupyter/baseseriesmap.py
@@ -25,7 +25,7 @@
import grass.script as gs
from .map import Map
-from .utils import get_number_of_cores
+from .utils import get_number_of_cores, save_gif
class BaseSeriesMap:
@@ -210,3 +210,49 @@ def change_image(index):
width="100%", display="inline-flex", flex_flow="row wrap"
)
return widgets.HBox([play, slider, out_img], layout=layout)
+
+ def save(
+ self,
+ filename,
+ duration=500,
+ label=True,
+ font=None,
+ text_size=12,
+ text_color="gray",
+ ):
+ """
+ Creates a GIF animation of rendered layers.
+
+ Text color must be in a format accepted by PIL ImageColor module. For supported
+ formats, visit:
+ https://pillow.readthedocs.io/en/stable/reference/ImageColor.html#color-names
+
+ param str filename: name of output GIF file
+ param int duration: time to display each frame; milliseconds
+ param bool label: include label on each frame
+ param str font: font file
+ param int text_size: size of label text
+ param str text_color: color to use for the text.
+ """
+
+ # Render images if they have not been already
+ if not self._layers_rendered:
+ self.render()
+
+ input_files = []
+ for index in self._indices:
+ input_files.append(self._base_filename_dict[index])
+
+ save_gif(
+ input_files,
+ filename,
+ duration=duration,
+ label=label,
+ labels=self._labels,
+ font=font,
+ text_size=text_size,
+ text_color=text_color,
+ )
+
+ # Display the GIF
+ return filename
diff --git a/python/grass/jupyter/interactivemap.py b/python/grass/jupyter/interactivemap.py
index 085c2843fe7..d843ac3b6b6 100644
--- a/python/grass/jupyter/interactivemap.py
+++ b/python/grass/jupyter/interactivemap.py
@@ -178,7 +178,7 @@ def add_to(self, interactive_map):
else:
import ipyleaflet # pylint: disable=import-outside-toplevel
- with open(self._filename, "r", encoding="utf-8") as file:
+ with open(self._filename, encoding="utf-8") as file:
data = json.load(file)
# allow using opacity directly to keep interface
# consistent for both backends
diff --git a/python/grass/jupyter/seriesmap.py b/python/grass/jupyter/seriesmap.py
index 8615ad488fb..ae6bde911b6 100644
--- a/python/grass/jupyter/seriesmap.py
+++ b/python/grass/jupyter/seriesmap.py
@@ -20,7 +20,6 @@
from .map import Map
from .region import RegionManagerForSeries
-from .utils import save_gif
from .baseseriesmap import BaseSeriesMap
@@ -165,49 +164,3 @@ def render(self):
)
tasks = [(i,) for i in range(self.baseseries)]
self._render(tasks)
-
- def save(
- self,
- filename,
- duration=500,
- label=True,
- font=None,
- text_size=12,
- text_color="gray",
- ):
- """
- Creates a GIF animation of rendered layers.
-
- Text color must be in a format accepted by PIL ImageColor module. For supported
- formats, visit:
- https://pillow.readthedocs.io/en/stable/reference/ImageColor.html#color-names
-
- param str filename: name of output GIF file
- param int duration: time to display each frame; milliseconds
- param bool label: include label on each frame
- param str font: font file
- param int text_size: size of label text
- param str text_color: color to use for the text
- """
-
- # Render images if they have not been already
- if not self._layers_rendered:
- self.render()
-
- tmp_files = []
- for file in self._base_filename_dict.values():
- tmp_files.append(file)
-
- save_gif(
- tmp_files,
- filename,
- duration=duration,
- label=label,
- labels=self._labels,
- font=font,
- text_size=text_size,
- text_color=text_color,
- )
-
- # Display the GIF
- return filename
diff --git a/python/grass/jupyter/tests/grass_jupyter_session_test.py b/python/grass/jupyter/tests/grass_jupyter_session_test.py
index 7b6bc89e4ed..17b6e7fbf5d 100644
--- a/python/grass/jupyter/tests/grass_jupyter_session_test.py
+++ b/python/grass/jupyter/tests/grass_jupyter_session_test.py
@@ -27,8 +27,8 @@ def test_init_finish(tmp_path):
import os
import grass.script as gs
import grass.jupyter as gj
-gs.core._create_location_xy("{tmp_path}", "{location}")
-session = gj.init("{tmp_path / location}")
+gs.core._create_location_xy(r"{tmp_path}", r"{location}")
+session = gj.init(r"{tmp_path / location}")
gs.read_command("g.region", flags="p")
print(os.environ["GISRC"])
session.finish()
@@ -49,8 +49,8 @@ def test_init_with_auto_finish(tmp_path):
import os
import grass.script as gs
import grass.jupyter as gj
-gs.core._create_location_xy("{tmp_path}", "{location}")
-session = gj.init("{tmp_path / location}")
+gs.core._create_location_xy(r"{tmp_path}", r"{location}")
+session = gj.init(r"{tmp_path / location}")
print(os.environ["GISRC"])
"""
diff --git a/python/grass/jupyter/testsuite/interactivemap_test.py b/python/grass/jupyter/testsuite/interactivemap_test.py
index 6b8a7379548..493578596b5 100644
--- a/python/grass/jupyter/testsuite/interactivemap_test.py
+++ b/python/grass/jupyter/testsuite/interactivemap_test.py
@@ -103,24 +103,26 @@ def test_query_button(self):
# Create InteractiveMap with ipyleaflet backend
interactive_map = gj.InteractiveMap(map_backend="ipyleaflet")
interactive_map.add_raster("elevation")
- interactive_map.add_vector("roadsmajor")
- interactive_map.add_query_button()
- self.assertIsNotNone(interactive_map.map)
- self.assertTrue(interactive_map.query_mode is False)
- # Toggle query button to activate
- interactive_map.query_mode = True
- self.assertTrue(interactive_map.query_mode)
- # Toggle query button to deactivate
- interactive_map.query_mode = False
- self.assertFalse(interactive_map.query_mode)
+ button = interactive_map.setup_query_interface()
+ self.assertIsNotNone(interactive_map._controllers[button].query_raster((0, 0)))
+
+ @unittest.skipIf(not can_import_ipyleaflet(), "Cannot import ipyleaflet")
+ def test_draw(self):
+ """Test the draw_computational_region method."""
+ # Create InteractiveMap
+ interactive_map = gj.InteractiveMap(map_backend="ipyleaflet")
+ button = interactive_map.setup_drawing_interface()
+ interactive_map._controllers[button].activate()
+ self.assertIsNotNone(interactive_map._controllers[button].save_button_control)
@unittest.skipIf(not can_import_ipyleaflet(), "Cannot import ipyleaflet")
def test_draw_computational_region(self):
"""Test the draw_computational_region method."""
# Create InteractiveMap
- interactive_map = gj.InteractiveMap()
- interactive_map.draw_computational_region()
- self.assertTrue(callable(interactive_map.draw_computational_region))
+ interactive_map = gj.InteractiveMap(map_backend="ipyleaflet")
+ button = interactive_map.setup_computational_region_interface()
+ interactive_map._controllers[button].activate()
+ self.assertIsNotNone(interactive_map._controllers[button].save_button_control)
if __name__ == "__main__":
diff --git a/python/grass/jupyter/testsuite/map3d_test.py b/python/grass/jupyter/testsuite/map3d_test.py
index b28b6a03a5b..5f88fad24e7 100644
--- a/python/grass/jupyter/testsuite/map3d_test.py
+++ b/python/grass/jupyter/testsuite/map3d_test.py
@@ -26,6 +26,7 @@
import grass.jupyter as gj
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
+from grass.gunittest.utils import xfail_windows
def can_import_ipython():
@@ -81,12 +82,14 @@ def tearDown(self):
else:
file.unlink(missing_ok=True)
+ @xfail_windows
def test_defaults(self):
"""Check that default settings work"""
renderer = gj.Map3D()
renderer.render(elevation_map="elevation", color_map="elevation")
self.assertFileExists(renderer.filename)
+ @xfail_windows
def test_filename(self):
"""Check that custom filename works"""
custom_filename = "test_filename.png"
@@ -96,12 +99,14 @@ def test_filename(self):
renderer.render(elevation_map="elevation", color_map="elevation")
self.assertFileExists(custom_filename)
+ @xfail_windows
def test_hw(self):
"""Check that custom width and height works"""
renderer = gj.Map3D(width=200, height=400)
renderer.render(elevation_map="elevation", color_map="elevation")
self.assertFileExists(renderer.filename)
+ @xfail_windows
def test_overlay(self):
"""Check that overlay works"""
renderer = gj.Map3D()
diff --git a/python/grass/jupyter/timeseriesmap.py b/python/grass/jupyter/timeseriesmap.py
index 3ac94bea93e..2acfa575126 100644
--- a/python/grass/jupyter/timeseriesmap.py
+++ b/python/grass/jupyter/timeseriesmap.py
@@ -20,7 +20,6 @@
from .map import Map
from .region import RegionManagerForTimeSeries
-from .utils import save_gif
from .baseseriesmap import BaseSeriesMap
@@ -312,49 +311,3 @@ def render(self):
filename = os.path.join(self._tmpdir.name, f"{layer}.png")
tasks.append((date, layer, filename))
self._render(tasks)
-
- def save(
- self,
- filename,
- duration=500,
- label=True,
- font="DejaVuSans.ttf",
- text_size=12,
- text_color="gray",
- ):
- """
- Creates a GIF animation of rendered layers.
-
- Text color must be in a format accepted by PIL ImageColor module. For supported
- formats, visit:
- https://pillow.readthedocs.io/en/stable/reference/ImageColor.html#color-names
-
- param str filename: name of output GIF file
- param int duration: time to display each frame; milliseconds
- param bool label: include date/time stamp on each frame
- param str font: font file
- param int text_size: size of date/time text
- param str text_color: color to use for the text.
- """
-
- # Render images if they have not been already
- if not self._layers_rendered:
- self.render()
-
- input_files = []
- for date in self._labels:
- input_files.append(self._base_filename_dict[date])
-
- save_gif(
- input_files,
- filename,
- duration=duration,
- label=label,
- labels=self._labels,
- font=font,
- text_size=text_size,
- text_color=text_color,
- )
-
- # Display the GIF
- return filename
diff --git a/python/grass/pygrass/gis/__init__.py b/python/grass/pygrass/gis/__init__.py
index 3f380368d28..d31bc31b972 100644
--- a/python/grass/pygrass/gis/__init__.py
+++ b/python/grass/pygrass/gis/__init__.py
@@ -430,7 +430,7 @@ def __iter__(self):
def read(self):
"""Return the mapsets in the search path"""
try:
- with open(self.spath, "r") as f:
+ with open(self.spath) as f:
lines = f.readlines()
if lines:
return [line.strip() for line in lines]
diff --git a/python/grass/pygrass/gis/region.py b/python/grass/pygrass/gis/region.py
index 98c75a15631..892a3664b49 100644
--- a/python/grass/pygrass/gis/region.py
+++ b/python/grass/pygrass/gis/region.py
@@ -331,10 +331,7 @@ def __eq__(self, reg):
"zone",
"proj",
]
- for attr in attrs:
- if getattr(self, attr) != getattr(reg, attr):
- return False
- return True
+ return all(getattr(self, attr) == getattr(reg, attr) for attr in attrs)
def __ne__(self, other):
return not self == other
diff --git a/python/grass/pygrass/modules/grid/grid.py b/python/grass/pygrass/modules/grid/grid.py
index fb338ca973a..e0dbd309656 100644
--- a/python/grass/pygrass/modules/grid/grid.py
+++ b/python/grass/pygrass/modules/grid/grid.py
@@ -121,7 +121,7 @@ def read_gisrc(gisrc):
... genv['GISDBASE']))
True
"""
- with open(gisrc, "r") as gfile:
+ with open(gisrc) as gfile:
gis = dict(
[(k.strip(), v.strip()) for k, v in [row.split(":", 1) for row in gfile]]
)
diff --git a/python/grass/pygrass/modules/interface/env.py b/python/grass/pygrass/modules/interface/env.py
index ad6e4c4800e..1f0519d23c5 100644
--- a/python/grass/pygrass/modules/interface/env.py
+++ b/python/grass/pygrass/modules/interface/env.py
@@ -13,7 +13,7 @@ def get_env():
gisrc = os.environ.get("GISRC")
if gisrc is None:
raise RuntimeError("You are not in a GRASS session, GISRC not found.")
- with open(gisrc, mode="r") as grc:
+ with open(gisrc) as grc:
return dict(
[
(k.strip(), v.strip())
diff --git a/python/grass/pygrass/modules/interface/parameter.py b/python/grass/pygrass/modules/interface/parameter.py
index 798efde56b7..3b58ea2b372 100644
--- a/python/grass/pygrass/modules/interface/parameter.py
+++ b/python/grass/pygrass/modules/interface/parameter.py
@@ -216,7 +216,7 @@ def __init__(self, xparameter=None, diz=None):
#
if "gisprompt" in diz and diz["gisprompt"]:
self.typedesc = diz["gisprompt"].get("prompt", "")
- self.input = not diz["gisprompt"]["age"] == "new"
+ self.input = diz["gisprompt"]["age"] != "new"
else:
self.input = True
diff --git a/python/grass/pygrass/raster/category.py b/python/grass/pygrass/raster/category.py
index cfeb84ddde2..f439d51dcdf 100644
--- a/python/grass/pygrass/raster/category.py
+++ b/python/grass/pygrass/raster/category.py
@@ -299,7 +299,7 @@ def read_rules(self, filename, sep=":"):
"""
self.reset()
- with open(filename, "r") as f:
+ with open(filename) as f:
for row in f:
cat = row.strip().split(sep)
if len(cat) == 2:
diff --git a/python/grass/pygrass/raster/history.py b/python/grass/pygrass/raster/history.py
index 5ec855cf1bb..8f227affabc 100644
--- a/python/grass/pygrass/raster/history.py
+++ b/python/grass/pygrass/raster/history.py
@@ -59,10 +59,7 @@ def __del__(self):
"""Rast_free_history"""
def __eq__(self, hist):
- for attr in self.attrs:
- if getattr(self, attr) != getattr(hist, attr):
- return False
- return True
+ return all(getattr(self, attr) == getattr(hist, attr) for attr in self.attrs)
def __len__(self):
return self.length()
diff --git a/python/grass/pygrass/raster/testsuite/test_category.py b/python/grass/pygrass/raster/testsuite/test_category.py
index 527c43b894f..113264c82f6 100644
--- a/python/grass/pygrass/raster/testsuite/test_category.py
+++ b/python/grass/pygrass/raster/testsuite/test_category.py
@@ -6,6 +6,7 @@
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
+from grass.gunittest.utils import xfail_windows
from grass.pygrass.raster import RasterRow
from grass.pygrass.raster.category import Category
@@ -76,6 +77,7 @@ def testFirstCat(self):
self.assertEqual(cats[7], cat7)
self.assertEqual(cats[15], cat15)
+ @xfail_windows
def testWrite(self):
tmpfile = tempfile(False)
cats = Category(self.name)
diff --git a/python/grass/pygrass/raster/testsuite/test_numpy.py b/python/grass/pygrass/raster/testsuite/test_numpy.py
index 5f0b2309544..b23926d8ba7 100644
--- a/python/grass/pygrass/raster/testsuite/test_numpy.py
+++ b/python/grass/pygrass/raster/testsuite/test_numpy.py
@@ -6,6 +6,7 @@
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
+from grass.gunittest.utils import xfail_windows
from numpy.random import default_rng
from grass.pygrass.raster import raster2numpy, numpy2raster, RasterRow
@@ -48,6 +49,7 @@ def test_len(self):
self.assertTrue(len(self.numpy_obj), 40)
self.assertTrue(len(self.numpy_obj[0]), 60)
+ @xfail_windows
def test_write(self):
rng = default_rng()
numpy2raster(rng.random([40, 60]), "FCELL", self.name, True)
diff --git a/python/grass/pygrass/raster/testsuite/test_raster_img.py b/python/grass/pygrass/raster/testsuite/test_raster_img.py
index bbc1cb2dabd..b873b16a86b 100644
--- a/python/grass/pygrass/raster/testsuite/test_raster_img.py
+++ b/python/grass/pygrass/raster/testsuite/test_raster_img.py
@@ -3,6 +3,7 @@
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
+from grass.gunittest.utils import xfail_windows
from grass.pygrass.raster import raster2numpy_img
from grass.pygrass.gis.region import Region
@@ -149,6 +150,7 @@ def test_resampling_to_numpy_img_1(self):
self.assertEqual(len(a), region.rows * region.cols * 4)
+ @xfail_windows
def test_resampling_to_numpy_img_2(self):
region = Region()
region.ewres = 1
@@ -159,6 +161,7 @@ def test_resampling_to_numpy_img_2(self):
self.assertEqual(len(a), region.rows * region.cols * 4)
+ @xfail_windows
def test_resampling_to_numpy_img_3(self):
region = Region()
region.ewres = 0.4
@@ -169,6 +172,7 @@ def test_resampling_to_numpy_img_3(self):
self.assertEqual(len(a), region.rows * region.cols * 1)
+ @xfail_windows
def test_resampling_to_numpy_img_4(self):
region = Region()
region.ewres = 0.1
diff --git a/python/grass/pygrass/tests/benchmark.py b/python/grass/pygrass/tests/benchmark.py
index 75e32541a59..fa7f0c01a65 100644
--- a/python/grass/pygrass/tests/benchmark.py
+++ b/python/grass/pygrass/tests/benchmark.py
@@ -12,11 +12,11 @@
import copy
import cProfile
import sys
-import os
from jinja2 import Template
+from pathlib import Path
-sys.path.append(os.getcwd())
-sys.path.append("%s/.." % (os.getcwd()))
+sys.path.append(str(Path.cwd()))
+sys.path.append("%s/.." % (str(Path.cwd())))
import grass.lib.gis as libgis
import grass.lib.raster as libraster
diff --git a/python/grass/pygrass/tests/set_mapset.py b/python/grass/pygrass/tests/set_mapset.py
index f9d4a96fabf..105735efa5a 100644
--- a/python/grass/pygrass/tests/set_mapset.py
+++ b/python/grass/pygrass/tests/set_mapset.py
@@ -13,7 +13,7 @@
def read_gisrc(gisrcpath):
- gisrc = open(gisrcpath, "r")
+ gisrc = open(gisrcpath)
diz = {}
for row in gisrc:
key, val = row.split(":")
diff --git a/python/grass/pygrass/vector/table.py b/python/grass/pygrass/vector/table.py
index 4b7e99c0a83..634c440a583 100644
--- a/python/grass/pygrass/vector/table.py
+++ b/python/grass/pygrass/vector/table.py
@@ -791,10 +791,7 @@ def __eq__(self, link):
False
"""
attrs = ["layer", "name", "table_name", "key", "driver"]
- for attr in attrs:
- if getattr(self, attr) != getattr(link, attr):
- return False
- return True
+ return all(getattr(self, attr) == getattr(link, attr) for attr in attrs)
def __ne__(self, other):
return not self == other
diff --git a/python/grass/script/core.py b/python/grass/script/core.py
index a86738926f0..ee90a41d4ef 100644
--- a/python/grass/script/core.py
+++ b/python/grass/script/core.py
@@ -1066,7 +1066,7 @@ def _text_to_key_value_dict(
{'a': ['Hello'], 'c': [1, 2, 3, 4, 5], 'b': [1.0], 'd': ['hello', 8, 0.1]}
"""
- text = open(filename, "r").readlines()
+ text = open(filename).readlines()
kvdict = KeyValue()
for line in text:
@@ -1276,7 +1276,7 @@ def region_env(region3d=False, flags=None, env=None, **kwargs):
windfile = os.path.join(
gis_env["GISDBASE"], gis_env["LOCATION_NAME"], gis_env["MAPSET"], "WIND"
)
- with open(windfile, "r") as fd:
+ with open(windfile) as fd:
grass_region = ""
for line in fd:
key, value = (x.strip() for x in line.split(":", 1))
@@ -1894,7 +1894,7 @@ def _create_location_xy(database, location):
:param database: GRASS database where to create new location
:param location: location name
"""
- cur_dir = os.getcwd()
+ cur_dir = Path.cwd()
try:
os.chdir(database)
os.mkdir(location)
diff --git a/python/grass/script/task.py b/python/grass/script/task.py
index 35234ba65e6..8e1f0a12844 100644
--- a/python/grass/script/task.py
+++ b/python/grass/script/task.py
@@ -247,11 +247,7 @@ def get_options(self):
def has_required(self):
"""Check if command has at least one required parameter"""
- for p in self.params:
- if p.get("required", False):
- return True
-
- return False
+ return any(p.get("required", False) for p in self.params)
def set_param(self, aParam, aValue, element="value"):
"""Set param value/values."""
diff --git a/python/grass/script/testsuite/test_start_command_functions.py b/python/grass/script/testsuite/test_start_command_functions.py
index 414a68cd270..368669263a8 100644
--- a/python/grass/script/testsuite/test_start_command_functions.py
+++ b/python/grass/script/testsuite/test_start_command_functions.py
@@ -4,6 +4,7 @@
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
+from grass.gunittest.utils import xfail_windows
from grass.script.core import start_command, PIPE, run_command, write_command
from grass.script.core import read_command, find_program
@@ -85,6 +86,7 @@ def setUpClass(cls):
def tearDownClass(cls):
cls.runModule("g.remove", type="raster", name=cls.raster, flags="f")
+ @xfail_windows
def test_write_labels_unicode(self):
"""This tests if Python module works"""
find_program("ls", "--version")
@@ -99,6 +101,7 @@ def test_write_labels_unicode(self):
self.assertEqual(res, "1:kůň\n2:kráva\n3:ovečka\n4:býk")
self.assertIsInstance(res, str)
+ @xfail_windows
def test_write_labels_bytes(self):
"""This tests if Python module works"""
write_command(
diff --git a/python/grass/script/testsuite/test_utils.py b/python/grass/script/testsuite/test_utils.py
index db12041d66d..67d2c59dd69 100644
--- a/python/grass/script/testsuite/test_utils.py
+++ b/python/grass/script/testsuite/test_utils.py
@@ -2,6 +2,7 @@
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
+from grass.gunittest.utils import xfail_windows
from grass.script import utils
@@ -39,6 +40,7 @@ def test_bytes(self):
def test_unicode(self):
self.assertEqual(b"text", utils.encode("text"))
+ @xfail_windows
def test_bytes_garbage_in_out(self):
"""If the input is bytes we should not touch it for encoding"""
self.assertEqual(
diff --git a/python/grass/script/utils.py b/python/grass/script/utils.py
index 13d049412a4..aa7d53375dd 100644
--- a/python/grass/script/utils.py
+++ b/python/grass/script/utils.py
@@ -28,6 +28,8 @@
import random
import string
+from pathlib import Path
+
def float_or_dms(s):
"""Convert DMS to float.
@@ -89,8 +91,8 @@ def diff_files(filename_a, filename_b):
import difflib
differ = difflib.Differ()
- fh_a = open(filename_a, "r")
- fh_b = open(filename_b, "r")
+ fh_a = open(filename_a)
+ fh_b = open(filename_b)
return list(differ.compare(fh_a.readlines(), fh_b.readlines()))
@@ -371,10 +373,9 @@ def get_lib_path(modname, libname=None):
getenv("GRASS_ADDON_BASE")
and libname
and isdir(join(getenv("GRASS_ADDON_BASE"), "etc", modname, libname))
- ):
- path = join(getenv("GRASS_ADDON_BASE"), "etc", modname)
- elif getenv("GRASS_ADDON_BASE") and isdir(
- join(getenv("GRASS_ADDON_BASE"), "etc", modname)
+ ) or (
+ getenv("GRASS_ADDON_BASE")
+ and isdir(join(getenv("GRASS_ADDON_BASE"), "etc", modname))
):
path = join(getenv("GRASS_ADDON_BASE"), "etc", modname)
elif getenv("GRASS_ADDON_BASE") and isdir(
@@ -383,7 +384,7 @@ def get_lib_path(modname, libname=None):
path = join(os.getenv("GRASS_ADDON_BASE"), modname, modname)
else:
# used by g.extension compilation process
- cwd = os.getcwd()
+ cwd = str(Path.cwd())
idx = cwd.find(modname)
if idx < 0:
return None
@@ -463,10 +464,10 @@ def set_path(modulename, dirname=None, path="."):
import sys
# TODO: why dirname is checked first - the logic should be revised
- pathlib = None
+ _pathlib = None
if dirname:
- pathlib = os.path.join(path, dirname)
- if pathlib and os.path.exists(pathlib):
+ _pathlib = os.path.join(path, dirname)
+ if _pathlib and os.path.exists(_pathlib):
# we are running the script from the script directory, therefore
# we add the path to sys.path to reach the directory (dirname)
sys.path.append(os.path.abspath(path))
@@ -477,7 +478,7 @@ def set_path(modulename, dirname=None, path="."):
pathname = os.path.join(modulename, dirname) if dirname else modulename
raise ImportError(
"Not able to find the path '%s' directory "
- "(current dir '%s')." % (pathname, os.getcwd())
+ "(current dir '%s')." % (pathname, Path.cwd())
)
sys.path.insert(0, path)
diff --git a/python/grass/temporal/metadata.py b/python/grass/temporal/metadata.py
index 6094bbbe146..369fadc03cf 100644
--- a/python/grass/temporal/metadata.py
+++ b/python/grass/temporal/metadata.py
@@ -251,6 +251,26 @@ def get_max(self):
min = property(fget=get_min, fset=set_min)
max = property(fget=get_max, fset=set_max)
+ def print_info(self):
+ """Print information about this class in human readable style"""
+ self._print_info_body(shell=False)
+
+ def print_shell_info(self):
+ """Print information about this class in shell style"""
+ self._print_info_body(shell=True)
+
+ def _print_info_head(self, shell=False):
+ """Print information about this class (head part).
+
+ No header printed in shell style mode.
+
+ :param bool shell: True for human readable style otherwise shell style
+ """
+ if not shell:
+ print(
+ " +-------------------- Metadata information ----------------------------------+" # noqa: E501
+ )
+
def _print_info_body(self, shell=False):
"""Print information about this class (body part).
@@ -331,6 +351,7 @@ class RasterMetadata(RasterMetadataBase):
| East-west resolution:....... 0.1
| Minimum value:.............. 0.0
| Maximum value:.............. 100.0
+ | Semantic label:............. None
>>> meta.print_shell_info()
datatype=CELL
cols=100
@@ -340,6 +361,7 @@ class RasterMetadata(RasterMetadataBase):
ewres=0.1
min=0.0
max=100.0
+ semantic_label=None
"""
@@ -384,17 +406,19 @@ def get_semantic_label(self):
semantic_label = property(fget=get_semantic_label, fset=set_semantic_label)
- def _print_info_body(self, shell=False):
- """Print information about this class (body part).
+ def print_info(self):
+ """Print information about this class."""
+ self._print_info_head(shell=False)
+ self._print_info_body(shell=False)
+ # semantic label section (raster specific only)
+ print(" | Semantic label:............. " + str(self.get_semantic_label()))
- :param bool shell: True for human readable style otherwise shell style
- """
- super()._print_info_body(shell)
+ def print_shell_info(self):
+ """Print information about this class in shell style"""
+ self._print_info_head(shell=True)
+ self._print_info_body(shell=True)
# semantic label section (raster specific only)
- if shell:
- print("semantic_label=" + str(self.get_semantic_label()))
- else:
- print(" | Semantic label:............. " + str(self.get_semantic_label()))
+ print("semantic_label=" + str(self.get_semantic_label()))
###############################################################################
@@ -539,18 +563,19 @@ def get_tbres(self):
depths = property(fget=get_depths, fset=set_depths)
tbres = property(fget=get_tbres, fset=set_tbres)
- def _print_info_body(self, shell=False):
- """Print information about this class (body part).
+ def print_info(self):
+ """Print information about this class."""
+ self._print_info_head(shell=False)
+ self._print_info_body(shell=False)
+ print(" | Number of depths:........... " + str(self.get_depths()))
+ print(" | Top-Bottom resolution:...... " + str(self.get_tbres()))
- :param bool shell: True for human readable style otherwise shell style
- """
- super()._print_info_body(shell)
- if shell:
- print("depths=" + str(self.get_depths()))
- print("tbres=" + str(self.get_tbres()))
- else:
- print(" | Number of depths:........... " + str(self.get_depths()))
- print(" | Top-Bottom resolution:...... " + str(self.get_tbres()))
+ def print_shell_info(self):
+ """Print information about this class in shell style"""
+ self._print_info_head(shell=True)
+ self._print_info_body(shell=True)
+ print("depths=" + str(self.get_depths()))
+ print("tbres=" + str(self.get_tbres()))
###############################################################################
@@ -870,53 +895,40 @@ def get_number_of_volumes(self):
number_of_holes = property(fget=get_number_of_holes, fset=set_number_of_holes)
number_of_volumes = property(fget=get_number_of_volumes, fset=set_number_of_volumes)
- def _print_info_body(self, shell=False):
- """Print information about this class (body part).
+ def print_info(self):
+ """Print information about this class in human readable style"""
+ print(
+ " +-------------------- Metadata information ----------------------------------+" # noqa: E501
+ )
+ print(" | Is map 3d .................. " + str(self.get_3d_info()))
+ print(" | Number of points ........... " + str(self.get_number_of_points()))
+ print(" | Number of lines ............ " + str(self.get_number_of_lines()))
+ print(" | Number of boundaries ....... " + str(self.get_number_of_boundaries()))
+ print(" | Number of centroids ........ " + str(self.get_number_of_centroids()))
+ print(" | Number of faces ............ " + str(self.get_number_of_faces()))
+ print(" | Number of kernels .......... " + str(self.get_number_of_kernels()))
+ print(" | Number of primitives ....... " + str(self.get_number_of_primitives()))
+ print(" | Number of nodes ............ " + str(self.get_number_of_nodes()))
+ print(" | Number of areas ............ " + str(self.get_number_of_areas()))
+ print(" | Number of islands .......... " + str(self.get_number_of_islands()))
+ print(" | Number of holes ............ " + str(self.get_number_of_holes()))
+ print(" | Number of volumes .......... " + str(self.get_number_of_volumes()))
- :param bool shell: True for human readable style otherwise shell style
- """
- if shell:
- print("is_3d=" + str(self.get_3d_info()))
- print("points=" + str(self.get_number_of_points()))
- print("lines=" + str(self.get_number_of_lines()))
- print("boundaries=" + str(self.get_number_of_boundaries()))
- print("centroids=" + str(self.get_number_of_centroids()))
- print("faces=" + str(self.get_number_of_faces()))
- print("kernels=" + str(self.get_number_of_kernels()))
- print("primitives=" + str(self.get_number_of_primitives()))
- print("nodes=" + str(self.get_number_of_nodes()))
- print("areas=" + str(self.get_number_of_areas()))
- print("islands=" + str(self.get_number_of_islands()))
- print("holes=" + str(self.get_number_of_holes()))
- print("volumes=" + str(self.get_number_of_volumes()))
- else:
- print(" | Is map 3d .................. " + str(self.get_3d_info()))
- print(" | Number of points ........... " + str(self.get_number_of_points()))
- print(" | Number of lines ............ " + str(self.get_number_of_lines()))
- print(
- " | Number of boundaries ....... "
- + str(self.get_number_of_boundaries())
- )
- print(
- " | Number of centroids ........ " + str(self.get_number_of_centroids())
- )
- print(" | Number of faces ............ " + str(self.get_number_of_faces()))
- print(
- " | Number of kernels .......... " + str(self.get_number_of_kernels())
- )
- print(
- " | Number of primitives ....... "
- + str(self.get_number_of_primitives())
- )
- print(" | Number of nodes ............ " + str(self.get_number_of_nodes()))
- print(" | Number of areas ............ " + str(self.get_number_of_areas()))
- print(
- " | Number of islands .......... " + str(self.get_number_of_islands())
- )
- print(" | Number of holes ............ " + str(self.get_number_of_holes()))
- print(
- " | Number of volumes .......... " + str(self.get_number_of_volumes())
- )
+ def print_shell_info(self):
+ """Print information about this class in shell style"""
+ print("is_3d=" + str(self.get_3d_info()))
+ print("points=" + str(self.get_number_of_points()))
+ print("lines=" + str(self.get_number_of_lines()))
+ print("boundaries=" + str(self.get_number_of_boundaries()))
+ print("centroids=" + str(self.get_number_of_centroids()))
+ print("faces=" + str(self.get_number_of_faces()))
+ print("kernels=" + str(self.get_number_of_kernels()))
+ print("primitives=" + str(self.get_number_of_primitives()))
+ print("nodes=" + str(self.get_number_of_nodes()))
+ print("areas=" + str(self.get_number_of_areas()))
+ print("islands=" + str(self.get_number_of_islands()))
+ print("holes=" + str(self.get_number_of_holes()))
+ print("volumes=" + str(self.get_number_of_volumes()))
###############################################################################
@@ -1034,13 +1046,11 @@ def get_number_of_maps(self):
def print_info(self):
"""Print information about this class in human readable style"""
- self._print_info_head(shell=False)
self._print_info_body(shell=False)
self._print_info_tail(shell=False)
def print_shell_info(self):
"""Print information about this class in shell style"""
- self._print_info_head(shell=True)
self._print_info_body(shell=True)
self._print_info_tail(shell=True)
@@ -1056,6 +1066,12 @@ def _print_info_head(self, shell=False):
" +-------------------- Metadata information ----------------------------------+" # noqa: E501
)
+ def _print_info_body(self, shell=False):
+ """Print information about this class (body part).
+
+ :param bool shell: True for human readable style otherwise shell style
+ """
+
def _print_info_tail(self, shell=False):
"""Print information about this class (tail part).
@@ -1476,6 +1492,16 @@ def get_semantic_labels(self):
number_of_semantic_labels = property(fget=get_number_of_semantic_labels)
semantic_labels = property(fget=get_semantic_labels)
+ def print_info(self):
+ """Print information about this class in human readable style"""
+ self._print_info_head(shell=False)
+ super().print_info()
+
+ def print_shell_info(self):
+ """Print information about this class in shell style"""
+ self._print_info_head(shell=True)
+ super().print_shell_info()
+
def _print_info_body(self, shell=False):
"""Print information about this class (body part).
@@ -1562,6 +1588,8 @@ class STR3DSMetadata(STDSRasterMetadataBase):
| Command history:
>>> meta.print_shell_info()
raster3d_register=None
+ tbres_min=None
+ tbres_max=None
nsres_min=None
nsres_max=None
ewres_min=None
@@ -1570,8 +1598,6 @@ class STR3DSMetadata(STDSRasterMetadataBase):
min_max=None
max_min=None
max_max=None
- tbres_min=None
- tbres_max=None
aggregation_type=None
number_of_maps=None
@@ -1624,6 +1650,16 @@ def get_tbres_max(self):
tbres_min = property(fget=get_tbres_min)
tbres_max = property(fget=get_tbres_max)
+ def print_info(self):
+ """Print information about this class in human readable style"""
+ self._print_info_head(shell=False)
+ super().print_info()
+
+ def print_shell_info(self):
+ """Print information about this class in shell style"""
+ self._print_info_head(shell=True)
+ super().print_shell_info()
+
def _print_info_body(self, shell=False):
"""Print information about this class (body part).
@@ -1889,6 +1925,16 @@ def get_number_of_volumes(self):
number_of_holes = property(fget=get_number_of_holes)
number_of_volumes = property(fget=get_number_of_volumes)
+ def print_info(self):
+ """Print information about this class in human readable style"""
+ self._print_info_head(shell=False)
+ super().print_info()
+
+ def print_shell_info(self):
+ """Print information about this class in shell style"""
+ self._print_info_head(shell=True)
+ super().print_shell_info()
+
def _print_info_body(self, shell=False):
"""Print information about this class (body part).
diff --git a/python/grass/temporal/register.py b/python/grass/temporal/register.py
index 0cd339946b9..8b7852ab557 100644
--- a/python/grass/temporal/register.py
+++ b/python/grass/temporal/register.py
@@ -161,7 +161,7 @@ def register_maps_in_space_time_dataset(
if hasattr(file, "readline"):
fd = file
else:
- fd = open(file, "r")
+ fd = open(file)
line = True
while True:
diff --git a/python/grass/temporal/spatial_extent.py b/python/grass/temporal/spatial_extent.py
index 329a0a4a684..26498e0385e 100644
--- a/python/grass/temporal/spatial_extent.py
+++ b/python/grass/temporal/spatial_extent.py
@@ -875,7 +875,7 @@ def cover_2d(self, extent) -> bool:
if eS > S and eS < N:
edge_count += 1
- return not edge_count == 0
+ return edge_count != 0
def cover(self, extent) -> bool:
"""Return True if this extent covers the provided spatial
@@ -956,7 +956,7 @@ def cover(self, extent) -> bool:
if eB > B and eB < T:
edge_count += 1
- return not edge_count == 0
+ return edge_count != 0
def covered_2d(self, extent):
"""Return True if this extent is covered by the provided spatial
diff --git a/python/grass/temporal/stds_export.py b/python/grass/temporal/stds_export.py
index ff2731b102a..a01b94e1071 100644
--- a/python/grass/temporal/stds_export.py
+++ b/python/grass/temporal/stds_export.py
@@ -348,7 +348,7 @@ def export_stds(
"""
# Save current working directory path
- old_cwd = os.getcwd()
+ old_cwd = Path.cwd()
# Create the temporary directory and jump into it
new_cwd = tempfile.mkdtemp(dir=directory)
diff --git a/python/grass/temporal/stds_import.py b/python/grass/temporal/stds_import.py
index ae587cadb0f..006ee6a387e 100644
--- a/python/grass/temporal/stds_import.py
+++ b/python/grass/temporal/stds_import.py
@@ -286,7 +286,7 @@ def import_stds(
# We use a new list file name for map registration
new_list_file_name = list_file_name + "_new"
# Save current working directory path
- old_cwd = os.getcwd()
+ old_cwd = Path.cwd()
# Switch into the data directory
os.chdir(directory)
@@ -372,7 +372,7 @@ def import_stds(
fs = "|"
maplist = []
mapset = get_current_mapset()
- list_file = open(list_file_name, "r")
+ list_file = open(list_file_name)
new_list_file = open(new_list_file_name, "w")
# get number of lines to correctly form the suffix
@@ -426,7 +426,7 @@ def import_stds(
# Read the init file
fs = "="
init = {}
- init_file = open(init_file_name, "r")
+ init_file = open(init_file_name)
while True:
line = init_file.readline()
if not line:
diff --git a/python/grass/temporal/temporal_algebra.py b/python/grass/temporal/temporal_algebra.py
index 1b9a126c635..1cf1cfd7366 100644
--- a/python/grass/temporal/temporal_algebra.py
+++ b/python/grass/temporal/temporal_algebra.py
@@ -1275,15 +1275,9 @@ def check_stds(self, input, clear=False, stds_type=None, check_type=True):
self.temporaltype = "absolute"
elif map_i.is_time_relative() and self.temporaltype is None:
self.temporaltype = "relative"
- elif map_i.is_time_absolute() and self.temporaltype == "relative":
- self.msgr.fatal(
- _(
- "Wrong temporal type of space time dataset "
- "<%s> <%s> time is required"
- )
- % (id_input, self.temporaltype)
- )
- elif map_i.is_time_relative() and self.temporaltype == "absolute":
+ elif (
+ map_i.is_time_absolute() and self.temporaltype == "relative"
+ ) or (map_i.is_time_relative() and self.temporaltype == "absolute"):
self.msgr.fatal(
_(
"Wrong temporal type of space time dataset "
@@ -1299,13 +1293,9 @@ def check_stds(self, input, clear=False, stds_type=None, check_type=True):
maplist = input
# Create map_value as empty list item.
for map_i in maplist:
- if "map_value" not in dir(map_i):
+ if ("map_value" not in dir(map_i)) or clear:
map_i.map_value = []
- elif clear:
- map_i.map_value = []
- if "condition_value" not in dir(map_i):
- map_i.condition_value = []
- elif clear:
+ if ("condition_value" not in dir(map_i)) or clear:
map_i.condition_value = []
else:
self.msgr.fatal(_("Wrong type of input " + str(input)))
diff --git a/python/grass/temporal/testsuite/unittests_temporal_raster_algebra_equal_ts.py b/python/grass/temporal/testsuite/unittests_temporal_raster_algebra_equal_ts.py
index 92397cd825c..f96ed12899f 100644
--- a/python/grass/temporal/testsuite/unittests_temporal_raster_algebra_equal_ts.py
+++ b/python/grass/temporal/testsuite/unittests_temporal_raster_algebra_equal_ts.py
@@ -12,6 +12,7 @@
import grass.temporal as tgis
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
+from grass.gunittest.utils import xfail_windows
class TestTemporalRasterAlgebraImplicitAggregation(TestCase):
@@ -64,6 +65,7 @@ def tearDownClass(cls):
cls.runModule("t.unregister", maps="singletmap", quiet=True)
cls.del_temp_region()
+ @xfail_windows
def test_simple_operator(self):
"""Test implicit aggregation
@@ -149,6 +151,7 @@ def test_single_map_complex_operator(self):
self.assertEqual(D.check_temporal_topology(), True)
self.assertEqual(D.get_granularity(), None)
+ @xfail_windows
def test_single_map_simple_operator(self):
"""Test implicit aggregation
diff --git a/raster/r.contour/testsuite/test_r_contour.py b/raster/r.contour/testsuite/test_r_contour.py
index 9fc0fc17469..c9ee4c9876a 100644
--- a/raster/r.contour/testsuite/test_r_contour.py
+++ b/raster/r.contour/testsuite/test_r_contour.py
@@ -62,7 +62,7 @@ def test_raster_contour(self):
self.assertModule("v.db.select", map=self.output, file="testReport")
self.assertFileExists("testReport", msg="testReport file was not created")
if os.path.isfile("testReport"):
- file = open("testReport", "r")
+ file = open("testReport")
fileData = file.read()
self.assertMultiLineEqual(fileData, self.test_ref_str)
file.close()
@@ -87,7 +87,7 @@ def test_raster_contour_cut(self):
self.assertModule("v.db.select", map=self.output + "_cut", file="testReportCut")
self.assertFileExists("testReportCut", msg="testReportCut file was not created")
if os.path.isfile("testReportCut"):
- file = open("testReportCut", "r")
+ file = open("testReportCut")
fileData = file.read()
self.assertMultiLineEqual(fileData, self.test_ref_str)
file.close()
diff --git a/raster/r.fill.dir/filldir.c b/raster/r.fill.dir/filldir.c
index 55380b7dfc5..cfe0ae69586 100644
--- a/raster/r.fill.dir/filldir.c
+++ b/raster/r.fill.dir/filldir.c
@@ -145,14 +145,20 @@ void filldir(int fe, int fd, int nl, struct band3 *bnd)
CELL *dir;
/* fill single-cell depressions, except on outer rows and columns */
- lseek(fe, 0, SEEK_SET);
+ if (lseek(fe, 0, SEEK_SET) == -1) {
+ G_fatal_error(_("Unable to seek: %s"), strerror(errno));
+ }
advance_band3(fe, bnd);
advance_band3(fe, bnd);
for (i = 1; i < nl - 1; i += 1) {
- lseek(fe, (off_t)(i + 1) * bnd->sz, SEEK_SET);
+ if (lseek(fe, (off_t)(i + 1) * bnd->sz, SEEK_SET) == -1) {
+ G_fatal_error(_("Unable to seek: %s"), strerror(errno));
+ }
advance_band3(fe, bnd);
if (fill_row(nl, bnd->ns, bnd)) {
- lseek(fe, (off_t)i * bnd->sz, SEEK_SET);
+ if (lseek(fe, (off_t)i * bnd->sz, SEEK_SET) == -1) {
+ G_fatal_error(_("Unable to seek: %s"), strerror(errno));
+ }
if (write(fe, bnd->b[1], bnd->sz) < 0)
G_fatal_error(_("File writing error in %s() %d:%s"), __func__,
errno, strerror(errno));
@@ -172,8 +178,12 @@ void filldir(int fe, int fd, int nl, struct band3 *bnd)
dir = G_calloc(bnd->ns, sizeof(CELL));
bufsz = bnd->ns * sizeof(CELL);
- lseek(fe, 0, SEEK_SET);
- lseek(fd, 0, SEEK_SET);
+ if (lseek(fe, 0, SEEK_SET) == -1) {
+ G_fatal_error(_("Unable to seek: %s"), strerror(errno));
+ }
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ G_fatal_error(_("Unable to seek: %s"), strerror(errno));
+ }
advance_band3(fe, bnd);
for (i = 0; i < nl; i += 1) {
advance_band3(fe, bnd);
diff --git a/raster/r.in.gdal/testsuite/test_r_in_gdal.py b/raster/r.in.gdal/testsuite/test_r_in_gdal.py
index 355e9b74eda..477ffca89c8 100644
--- a/raster/r.in.gdal/testsuite/test_r_in_gdal.py
+++ b/raster/r.in.gdal/testsuite/test_r_in_gdal.py
@@ -4,6 +4,7 @@
"""
import unittest
+import sys
from subprocess import check_output
@@ -306,12 +307,13 @@ def test_netCDF_3d_5(self):
test_gdal_import_map.0000000105
"""
- text_from_file = open("map_names_file.txt", "r").read()
+ text_from_file = open("map_names_file.txt").read()
self.assertLooksLike(map_list, text_from_file)
@unittest.skipIf(
- tuple(
+ not sys.platform.startswith("win")
+ and tuple(
map(
int,
check_output(["gdal-config", "--version"])
diff --git a/raster/r.kappa/testsuite/test_r_kappa.py b/raster/r.kappa/testsuite/test_r_kappa.py
index 56e356ca20b..620a4f4979b 100644
--- a/raster/r.kappa/testsuite/test_r_kappa.py
+++ b/raster/r.kappa/testsuite/test_r_kappa.py
@@ -21,6 +21,7 @@
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
from grass.gunittest.checkers import keyvalue_equals
+from grass.gunittest.utils import xfail_windows
class MatrixCorrectnessTest(TestCase):
@@ -474,6 +475,7 @@ def test_stdout(self):
keyvalue_equals(self.expected_outputs[i], json_out, precision=4)
)
+ @xfail_windows
def test_file(self):
for i in range(len(self.references)):
f = NamedTemporaryFile()
diff --git a/raster/r.mfilter/main.c b/raster/r.mfilter/main.c
index 17ecd2182be..ad0a9dd35ae 100644
--- a/raster/r.mfilter/main.c
+++ b/raster/r.mfilter/main.c
@@ -132,8 +132,8 @@ int main(int argc, char **argv)
"threads setting."));
nprocs = 1;
#endif
- if (nprocs > 1 && G_find_raster("MASK", G_mapset()) != NULL) {
- G_warning(_("Parallel processing disabled due to active MASK."));
+ if (nprocs > 1 && Rast_mask_is_present()) {
+ G_warning(_("Parallel processing disabled due to active mask."));
nprocs = 1;
}
out_name = opt2->answer;
diff --git a/raster/r.mfilter/testsuite/test_r_mfilter.py b/raster/r.mfilter/testsuite/test_r_mfilter.py
index 3d1764b4894..c18aa7c9273 100644
--- a/raster/r.mfilter/testsuite/test_r_mfilter.py
+++ b/raster/r.mfilter/testsuite/test_r_mfilter.py
@@ -1,6 +1,7 @@
from tempfile import NamedTemporaryFile
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
+from grass.gunittest.utils import xfail_windows
class TestNeighbors(TestCase):
@@ -202,6 +203,7 @@ def tearDownClass(cls):
"g.remove", flags="f", type="raster", name=",".join(cls.to_remove)
)
+ @xfail_windows
def test_sequential(self):
"""Test output with sequential filter type."""
test_case = "test_sequential"
@@ -235,6 +237,7 @@ def test_sequential(self):
precision=1e-5,
)
+ @xfail_windows
def test_parallel(self):
"""Test output with parallel filter type."""
test_case = "test_parallel"
@@ -268,6 +271,7 @@ def test_parallel(self):
precision=1e-5,
)
+ @xfail_windows
def test_sequential_null(self):
"""Test output with sequential filter type with null mode enabled."""
test_case = "test_sequential_null"
@@ -301,6 +305,7 @@ def test_sequential_null(self):
precision=1e-5,
)
+ @xfail_windows
def test_parallel_null(self):
"""Test output with parallel filter type with null mode enabled."""
test_case = "test_parallel_null"
@@ -361,6 +366,7 @@ def test_parallel_null(self):
precision=1e-5,
)
+ @xfail_windows
def test_multiple_filters(self):
"""Test output with multiple filters."""
test_case = "test_multiple_filters"
@@ -394,6 +400,7 @@ def test_multiple_filters(self):
precision=1e-5,
)
+ @xfail_windows
def test_repeated_filters(self):
"""Test output with repeated filters."""
test_case = "test_repeated_filters"
diff --git a/raster/r.neighbors/main.c b/raster/r.neighbors/main.c
index 93cbaa9e3da..8885460a564 100644
--- a/raster/r.neighbors/main.c
+++ b/raster/r.neighbors/main.c
@@ -310,8 +310,8 @@ int main(int argc, char *argv[])
"threads setting."));
ncb.threads = 1;
#endif
- if (ncb.threads > 1 && G_find_raster("MASK", G_mapset()) != NULL) {
- G_warning(_("Parallel processing disabled due to active MASK."));
+ if (ncb.threads > 1 && Rast_mask_is_present()) {
+ G_warning(_("Parallel processing disabled due to active mask."));
ncb.threads = 1;
}
if (strcmp(parm.weighting_function->answer, "none") && flag.circle->answer)
diff --git a/raster/r.patch/main.c b/raster/r.patch/main.c
index 3835e287440..c54482db13a 100644
--- a/raster/r.patch/main.c
+++ b/raster/r.patch/main.c
@@ -113,8 +113,8 @@ int main(int argc, char *argv[])
"threads setting."));
nprocs = 1;
#endif
- if (nprocs > 1 && G_find_raster("MASK", G_mapset()) != NULL) {
- G_warning(_("Parallel processing disabled due to active MASK."));
+ if (nprocs > 1 && Rast_mask_is_present()) {
+ G_warning(_("Parallel processing disabled due to active mask."));
nprocs = 1;
}
diff --git a/raster/r.report/testsuite/test_r_report.py b/raster/r.report/testsuite/test_r_report.py
index 53e49fdc174..86a16c27adb 100644
--- a/raster/r.report/testsuite/test_r_report.py
+++ b/raster/r.report/testsuite/test_r_report.py
@@ -17,6 +17,7 @@
from grass.gunittest.case import TestCase
from grass.gunittest.gmodules import SimpleModule
+from grass.gunittest.utils import xfail_windows
class TestRasterreport(TestCase):
@@ -294,6 +295,7 @@ def test_json(self):
data = json.loads(module.outputs.stdout)
self._assert_report_equal(reference, data)
+ @xfail_windows
def test_json2(self):
"""Test JSON format with more options"""
reference = {
diff --git a/raster/r.resamp.filter/main.c b/raster/r.resamp.filter/main.c
index b1f6ed3e530..e74f3598629 100644
--- a/raster/r.resamp.filter/main.c
+++ b/raster/r.resamp.filter/main.c
@@ -494,8 +494,8 @@ int main(int argc, char *argv[])
"threads setting."));
nprocs = 1;
#endif
- if (nprocs > 1 && G_find_raster("MASK", G_mapset()) != NULL) {
- G_warning(_("Parallel processing disabled due to active MASK."));
+ if (nprocs > 1 && Rast_mask_is_present()) {
+ G_warning(_("Parallel processing disabled due to active make."));
nprocs = 1;
}
if (parm.radius->answer) {
diff --git a/raster/r.resamp.interp/main.c b/raster/r.resamp.interp/main.c
index 734f3306bf8..2319400b0fc 100644
--- a/raster/r.resamp.interp/main.c
+++ b/raster/r.resamp.interp/main.c
@@ -132,8 +132,8 @@ int main(int argc, char *argv[])
"threads setting."));
threads = 1;
#endif
- if (threads > 1 && G_find_raster("MASK", G_mapset()) != NULL) {
- G_warning(_("Parallel processing disabled due to active MASK."));
+ if (threads > 1 && Rast_mask_is_present()) {
+ G_warning(_("Parallel processing disabled due to active mask."));
threads = 1;
}
bufrows = atoi(memory->answer) * (((1 << 20) / sizeof(DCELL)) / dst_w.cols);
diff --git a/raster/r.series/main.c b/raster/r.series/main.c
index bfecbf117c0..ade93d8f185 100644
--- a/raster/r.series/main.c
+++ b/raster/r.series/main.c
@@ -227,8 +227,8 @@ int main(int argc, char *argv[])
"threads setting."));
nprocs = 1;
#endif
- if (nprocs > 1 && G_find_raster("MASK", G_mapset()) != NULL) {
- G_warning(_("Parallel processing disabled due to active MASK."));
+ if (nprocs > 1 && Rast_mask_is_present()) {
+ G_warning(_("Parallel processing disabled due to active mask."));
nprocs = 1;
}
lo = -INFINITY;
diff --git a/raster/r.sim/r.sim.sediment/Makefile b/raster/r.sim/r.sim.sediment/Makefile
index 4bc07478ec9..13ca19d09dc 100644
--- a/raster/r.sim/r.sim.sediment/Makefile
+++ b/raster/r.sim/r.sim.sediment/Makefile
@@ -4,8 +4,8 @@ PGM=r.sim.sediment
EXTRA_CLEAN_DIRS=doxygenhtml
-LIBES = $(SIMLIB) $(GMATHLIB) $(GISLIB) $(OPENMP_LIBPATH) $(OPENMP_LIB)
-DEPENDENCIES = $(SIMDEP) $(GMATHDEP) $(GISDEP)
+LIBES = $(SIMLIB) $(GMATHLIB) $(GISLIB) $(RASTERLIB) $(OPENMP_LIBPATH) $(OPENMP_LIB)
+DEPENDENCIES = $(SIMDEP) $(GMATHDEP) $(GISDEP) $(RASTERDEP)
EXTRA_INC = $(OPENMP_INCPATH) $(VECT_INC)
EXTRA_CFLAGS = -I ../simlib $(VECT_CFLAGS) $(OPENMP_CFLAGS)
diff --git a/raster/r.sim/r.sim.sediment/main.c b/raster/r.sim/r.sim.sediment/main.c
index fcdd9e98f89..dd082fe0f81 100644
--- a/raster/r.sim/r.sim.sediment/main.c
+++ b/raster/r.sim/r.sim.sediment/main.c
@@ -70,6 +70,7 @@
#endif
#include
#include
+#include
#include
#include
#include
@@ -380,8 +381,8 @@ int main(int argc, char *argv[])
#else
threads = 1;
#endif
- if (threads > 1 && G_find_raster("MASK", G_mapset()) != NULL) {
- G_warning(_("Parallel processing disabled due to active MASK."));
+ if (threads > 1 && Rast_mask_is_present()) {
+ G_warning(_("Parallel processing disabled due to active mask."));
threads = 1;
}
G_message(_("Number of threads: %d"), threads);
diff --git a/raster/r.sim/r.sim.water/Makefile b/raster/r.sim/r.sim.water/Makefile
index 36121ebf82e..47037823fae 100644
--- a/raster/r.sim/r.sim.water/Makefile
+++ b/raster/r.sim/r.sim.water/Makefile
@@ -4,8 +4,8 @@ PGM=r.sim.water
EXTRA_CLEAN_DIRS=doxygenhtml
-LIBES = $(SIMLIB) $(GMATHLIB) $(GISLIB) $(OPENMP_LIBPATH) $(OPENMP_LIB)
-DEPENDENCIES = $(SIMDEP) $(GMATHDEP) $(GISDEP)
+LIBES = $(SIMLIB) $(GMATHLIB) $(GISLIB) $(RASTERLIB) $(OPENMP_LIBPATH) $(OPENMP_LIB)
+DEPENDENCIES = $(SIMDEP) $(GMATHDEP) $(GISDEP) $(RASTERDEP)
EXTRA_INC = $(VECT_INC) $(OPENMP_INCPATH)
EXTRA_CFLAGS = -I ../simlib $(VECT_CFLAGS) $(OPENMP_CFLAGS)
diff --git a/raster/r.sim/r.sim.water/main.c b/raster/r.sim/r.sim.water/main.c
index 3998e5524ac..61f128f5504 100644
--- a/raster/r.sim/r.sim.water/main.c
+++ b/raster/r.sim/r.sim.water/main.c
@@ -77,6 +77,7 @@
#endif
#include
#include
+#include
#include
#include
#include
@@ -407,8 +408,8 @@ int main(int argc, char *argv[])
#else
threads = 1;
#endif
- if (threads > 1 && G_find_raster("MASK", G_mapset()) != NULL) {
- G_warning(_("Parallel processing disabled due to active MASK."));
+ if (threads > 1 && Rast_mask_is_present()) {
+ G_warning(_("Parallel processing disabled due to active mask."));
threads = 1;
}
G_message(_("Number of threads: %d"), threads);
diff --git a/raster/r.slope.aspect/main.c b/raster/r.slope.aspect/main.c
index 86b4d5f5607..d302e02b22c 100644
--- a/raster/r.slope.aspect/main.c
+++ b/raster/r.slope.aspect/main.c
@@ -305,8 +305,8 @@ int main(int argc, char *argv[])
"threads setting."));
nprocs = 1;
#endif
- if (nprocs > 1 && G_find_raster("MASK", G_mapset()) != NULL) {
- G_warning(_("Parallel processing disabled due to active MASK."));
+ if (nprocs > 1 && Rast_mask_is_present()) {
+ G_warning(_("Parallel processing disabled due to active mask."));
nprocs = 1;
}
radians_to_degrees = 180.0 / M_PI;
diff --git a/raster/r.sun/main.c b/raster/r.sun/main.c
index 9e45e184dac..e325cb637f0 100644
--- a/raster/r.sun/main.c
+++ b/raster/r.sun/main.c
@@ -591,8 +591,8 @@ int main(int argc, char *argv[])
#else
threads = 1;
#endif
- if (threads > 1 && G_find_raster("MASK", G_mapset()) != NULL) {
- G_warning(_("Parallel processing disabled due to active MASK."));
+ if (threads > 1 && Rast_mask_is_present()) {
+ G_warning(_("Parallel processing disabled due to active mask."));
threads = 1;
}
G_message(_("Number of threads <%d>"), threads);
diff --git a/raster/r.thin/io.c b/raster/r.thin/io.c
index d5ba76245c3..3b2d0bd8aa3 100644
--- a/raster/r.thin/io.c
+++ b/raster/r.thin/io.c
@@ -67,13 +67,17 @@ int put_a_row(int row, CELL *buf)
static int read_row(int file, void *buf, int row, int buf_len)
{
- lseek(file, ((off_t)row) * buf_len, 0);
+ if (lseek(file, ((off_t)row) * buf_len, 0) == -1) {
+ G_fatal_error(_("Unable to seek: %s"), strerror(errno));
+ }
return (read(file, buf, buf_len) == buf_len);
}
static int write_row(int file, const void *buf, int row, int buf_len)
{
- lseek(file, ((off_t)row) * buf_len, 0);
+ if (lseek(file, ((off_t)row) * buf_len, 0) == -1) {
+ G_fatal_error(_("Unable to seek: %s"), strerror(errno));
+ }
return (write(file, buf, buf_len) == buf_len);
}
diff --git a/raster/r.univar/r.univar_main.c b/raster/r.univar/r.univar_main.c
index 805acfff27b..e56301a1364 100644
--- a/raster/r.univar/r.univar_main.c
+++ b/raster/r.univar/r.univar_main.c
@@ -192,8 +192,8 @@ int main(int argc, char *argv[])
sscanf(param.nprocs->answer, "%d", &nprocs);
if (nprocs < 1)
G_fatal_error(_("<%d> is not valid number of nprocs."), nprocs);
- if (nprocs > 1 && G_find_raster("MASK", G_mapset()) != NULL) {
- G_warning(_("Parallel processing disabled due to active MASK."));
+ if (nprocs > 1 && Rast_mask_is_present()) {
+ G_warning(_("Parallel processing disabled due to active mask."));
nprocs = 1;
}
#if defined(_OPENMP)
diff --git a/raster/r.what/testsuite/test_r_what.py b/raster/r.what/testsuite/test_r_what.py
index fcc312e6284..68bfdeea376 100644
--- a/raster/r.what/testsuite/test_r_what.py
+++ b/raster/r.what/testsuite/test_r_what.py
@@ -510,7 +510,7 @@ def test_raster_what_csv(self):
)
self.assertFileExists(filename="result.csv", msg="CSV file was not created")
if os.path.isfile("result.csv"):
- file = open("result.csv", "r")
+ file = open("result.csv")
fileData = file.read()
self.assertLooksLike(
actual=fileData,
diff --git a/raster3d/r3.in.v5d/v5d.c b/raster3d/r3.in.v5d/v5d.c
index 445d6f29b9f..54f75f2361a 100644
--- a/raster3d/r3.in.v5d/v5d.c
+++ b/raster3d/r3.in.v5d/v5d.c
@@ -63,6 +63,9 @@
#include
#include
#include
+#include
+#include
+
#include "binio.h"
#include "v5d.h"
#include "vis5d.h"
@@ -1232,7 +1235,10 @@ static int read_comp_header(int f, v5dstruct *v)
unsigned int id;
/* reset file position to start of file */
- lseek(f, 0, SEEK_SET);
+ if (lseek(f, 0, SEEK_SET) == -1) {
+ G_warning(_("Unable to seek: %s"), strerror(errno));
+ return 0;
+ }
/* read file ID */
read_int4(f, (int *)&id);
@@ -1334,8 +1340,8 @@ static int read_comp_header(int f, v5dstruct *v)
/* skip ahead by 'gridsize' bytes */
if (lseek(f, gridsize, SEEK_CUR) == -1) {
- printf("Error: Unexpected end of file, ");
- printf("file may be corrupted.\n");
+ G_warning(_("Error: Unexpected end of file, file may be "
+ "corrupted."));
return 0;
}
min = -(125.0 + gb) / ga;
@@ -1478,7 +1484,10 @@ static int read_comp_grid(v5dstruct *v, int time, int var, float *ga, float *gb,
/* move to position in file */
pos = grid_position(v, time, var);
- lseek(f, pos, SEEK_SET);
+ if (lseek(f, pos, SEEK_SET) == -1) {
+ G_warning(_("Unable to seek: %s"), strerror(errno));
+ return 0;
+ }
if (v->FileFormat == 0x80808083) {
/* read McIDAS grid and file numbers */
@@ -1551,7 +1560,13 @@ static int read_comp_grid(v5dstruct *v, int time, int var, float *ga, float *gb,
*/
static int read_v5d_header(v5dstruct *v)
{
-#define SKIP(N) lseek(f, N, SEEK_CUR)
+#define SKIP(N) \
+ do { \
+ if (lseek(f, N, SEEK_CUR) == -1) { \
+ G_warning(_("Unable to seek: %s"), strerror(errno)); \
+ return 0; \
+ } \
+ } while (0)
int end_of_header = 0;
unsigned int id;
int idlen, var, numargs;
@@ -1870,13 +1885,19 @@ static int read_v5d_header(v5dstruct *v)
case TAG_END:
/* end of header */
end_of_header = 1;
- lseek(f, length, SEEK_CUR);
+ if (lseek(f, length, SEEK_CUR) == -1) {
+ G_warning(_("Unable to seek: %s"), strerror(errno));
+ return 0;
+ }
break;
default:
/* unknown tag, skip to next tag */
printf("Unknown tag: %d length=%d\n", tag, length);
- lseek(f, length, SEEK_CUR);
+ if (lseek(f, length, SEEK_CUR) == -1) {
+ G_warning(_("Unable to seek: %s"), strerror(errno));
+ return 0;
+ }
break;
}
}
@@ -1966,7 +1987,10 @@ int v5dReadCompressedGrid(v5dstruct *v, int time, int var, float *ga, float *gb,
/* move to position in file */
pos = grid_position(v, time, var);
- lseek(v->FileDesc, pos, SEEK_SET);
+ if (lseek(v->FileDesc, pos, SEEK_SET) == -1) {
+ G_warning(_("Unable to seek: %s"), strerror(errno));
+ return 0;
+ }
/* read ga, gb arrays */
read_float4_array(v->FileDesc, ga, v->Nl[var]);
@@ -2118,7 +2142,10 @@ static int write_v5d_header(v5dstruct *v)
}
/* set file pointer to start of file */
- lseek(f, 0, SEEK_SET);
+ if (lseek(f, 0, SEEK_SET) == -1) {
+ G_warning(_("Unable to seek: %s"), strerror(errno));
+ return 0;
+ }
v->CurPos = 0;
/*
@@ -2222,7 +2249,10 @@ static int write_v5d_header(v5dstruct *v)
/* We're writing to a brand new file. Reserve 10000 bytes */
/* for future header growth. */
WRITE_TAG(v, TAG_END, 10000);
- lseek(f, 10000, SEEK_CUR);
+ if (lseek(f, 10000, SEEK_CUR) == -1) {
+ G_warning(_("Unable to seek: %s"), strerror(errno));
+ return 0;
+ }
/* Let file pointer indicate where first grid is stored */
v->FirstGridPos = ltell(f);
@@ -2336,7 +2366,7 @@ int v5dWriteCompressedGrid(const v5dstruct *v, int time, int var,
pos = grid_position(v, time, var);
if (lseek(v->FileDesc, pos, SEEK_SET) < 0) {
/* lseek failed, return error */
- printf("Error in v5dWrite[Compressed]Grid: seek failed, disk full?\n");
+ G_warning(_("Unable to seek: %s"), strerror(errno));
return 0;
}
@@ -2452,9 +2482,15 @@ int v5dCloseFile(v5dstruct *v)
if (v->Mode == 'w') {
/* rewrite header because writing grids updates the minval and */
/* maxval fields */
- lseek(v->FileDesc, 0, SEEK_SET);
+ if (lseek(v->FileDesc, 0, SEEK_SET) == -1) {
+ G_warning(_("Unable to seek: %s"), strerror(errno));
+ return 0;
+ }
status = write_v5d_header(v);
- lseek(v->FileDesc, 0, SEEK_END);
+ if (lseek(v->FileDesc, 0, SEEK_END) == -1) {
+ G_warning(_("Unable to seek: %s"), strerror(errno));
+ return 0;
+ }
close(v->FileDesc);
}
else if (v->Mode == 'r') {
diff --git a/scripts/d.correlate/d.correlate.py b/scripts/d.correlate/d.correlate.py
index af681cce545..1068733a11b 100755
--- a/scripts/d.correlate/d.correlate.py
+++ b/scripts/d.correlate/d.correlate.py
@@ -73,7 +73,7 @@ def main():
gcore.run_command("r.stats", flags="cnA", input=(i, j), stdout=ofile)
ofile.close()
- ifile = open(tmpfile, "r")
+ ifile = open(tmpfile)
first = True
for line in ifile:
f = line.rstrip("\r\n").split(" ")
@@ -95,7 +95,7 @@ def main():
p = gcore.feed_command("d.graph", color=color)
ofile = p.stdin
- ifile = open(tmpfile, "r")
+ ifile = open(tmpfile)
for line in ifile:
f = line.rstrip("\r\n").split(" ")
x = float(f[0])
diff --git a/scripts/d.frame/d.frame.py b/scripts/d.frame/d.frame.py
index e9b44b21650..ff77316f0ec 100755
--- a/scripts/d.frame/d.frame.py
+++ b/scripts/d.frame/d.frame.py
@@ -91,7 +91,7 @@ def check_monitor():
def read_monitor_file(monitor, ftype="env"):
mfile = check_monitor_file(monitor, ftype)
try:
- fd = open(mfile, "r")
+ fd = open(mfile)
except OSError as e:
fatal(_("Unable to get monitor info. %s"), e)
diff --git a/scripts/db.univar/db.univar.py b/scripts/db.univar/db.univar.py
index f941be42140..97dfbdb7dbe 100755
--- a/scripts/db.univar/db.univar.py
+++ b/scripts/db.univar/db.univar.py
@@ -77,7 +77,7 @@ def cleanup():
def sortfile(infile, outfile):
- inf = open(infile, "r")
+ inf = open(infile)
outf = open(outfile, "w")
if gs.find_program("sort", "--help"):
diff --git a/scripts/g.extension.all/g.extension.all.py b/scripts/g.extension.all/g.extension.all.py
index 7f0b36f4dc6..1b0a2a88108 100644
--- a/scripts/g.extension.all/g.extension.all.py
+++ b/scripts/g.extension.all/g.extension.all.py
@@ -64,7 +64,7 @@ def get_extensions():
return []
# read XML file
- fo = open(fXML, "r")
+ fo = open(fXML)
try:
tree = ET.fromstring(fo.read())
except Exception as e:
@@ -104,7 +104,7 @@ def download_modules_xml_file(url, response_format, *args, **kwargs):
try:
response = urlopen(url, *args, **kwargs)
- if not response.code == 200:
+ if response.code != 200:
index = HTTP_STATUS_CODES.index(response.code)
desc = HTTP_STATUS_CODES[index].description
gs.fatal(
diff --git a/scripts/g.extension/testsuite/test_addons_modules.py b/scripts/g.extension/testsuite/test_addons_modules.py
index c1cab5c954a..9b99e7ac2ae 100644
--- a/scripts/g.extension/testsuite/test_addons_modules.py
+++ b/scripts/g.extension/testsuite/test_addons_modules.py
@@ -15,7 +15,7 @@
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
from grass.gunittest.gmodules import SimpleModule
-from grass.gunittest.utils import silent_rmtree
+from grass.gunittest.utils import silent_rmtree, xfail_windows
from grass.script.utils import decode
import os
@@ -52,6 +52,7 @@
class TestModulesMetadata(TestCase):
url = "file://" + os.path.abspath("data")
+ @xfail_windows
def test_listing(self):
"""List individual extensions/modules/addons"""
module = SimpleModule("g.extension", flags="l", url=self.url)
@@ -90,6 +91,7 @@ def tearDown(self):
"""Remove created files"""
silent_rmtree(self.install_prefix)
+ @xfail_windows
def test_directory_install(self):
"""Test installing extension from directory"""
self.assertModule(
@@ -102,6 +104,7 @@ def test_directory_install(self):
for file in self.files:
self.assertFileExists(file)
+ @xfail_windows
def test_targz_install(self):
"""Test installing extension from local .tar.gz"""
self.assertModule(
@@ -113,6 +116,7 @@ def test_targz_install(self):
for file in self.files:
self.assertFileExists(file)
+ @xfail_windows
def test_remote_targz_without_dir_install(self):
"""Test installing extension from (remote) .tar.gz without main dir"""
self.assertModule(
@@ -125,6 +129,7 @@ def test_remote_targz_without_dir_install(self):
for file in self.files:
self.assertFileExists(file)
+ @xfail_windows
def test_remote_zip_install(self):
"""Test installing extension from .zip specified by URL (local)"""
self.assertModule(
diff --git a/scripts/g.extension/testsuite/test_addons_toolboxes.py b/scripts/g.extension/testsuite/test_addons_toolboxes.py
index 5cbc0454a41..17d7e4b5b74 100644
--- a/scripts/g.extension/testsuite/test_addons_toolboxes.py
+++ b/scripts/g.extension/testsuite/test_addons_toolboxes.py
@@ -15,9 +15,11 @@
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
from grass.gunittest.gmodules import SimpleModule
+from grass.gunittest.utils import xfail_windows
import os
+
FULL_TOOLBOXES_OUTPUT = """\
Hydrology (HY)
* r.stream.basins
@@ -39,6 +41,7 @@
class TestToolboxesMetadata(TestCase):
url = "file://" + os.path.abspath("data")
+ @xfail_windows
def test_listing(self):
"""List toolboxes and their content"""
module = SimpleModule("g.extension", flags="lt", url=self.url)
diff --git a/scripts/g.search.modules/g.search.modules.py b/scripts/g.search.modules/g.search.modules.py
index 85247517898..cb40cbdced5 100755
--- a/scripts/g.search.modules/g.search.modules.py
+++ b/scripts/g.search.modules/g.search.modules.py
@@ -66,6 +66,8 @@
import os
import sys
+from operator import itemgetter
+
from grass.script import core as grass
from grass.exceptions import CalledModuleError
@@ -199,7 +201,7 @@ def _search_module(
WXGUIDIR = os.path.join(os.getenv("GISBASE"), "gui", "wxpython")
filename = os.path.join(WXGUIDIR, "xml", "module_items.xml")
- menudata_file = open(filename, "r")
+ menudata_file = open(filename)
menudata = ET.parse(menudata_file)
menudata_file.close()
@@ -210,7 +212,7 @@ def _search_module(
if os.getenv("GRASS_ADDON_BASE"):
filename_addons = os.path.join(os.getenv("GRASS_ADDON_BASE"), "modules.xml")
if os.path.isfile(filename_addons):
- addon_menudata_file = open(filename_addons, "r")
+ addon_menudata_file = open(filename_addons)
addon_menudata = ET.parse(addon_menudata_file)
addon_menudata_file.close()
addon_items = addon_menudata.findall("task")
@@ -219,7 +221,7 @@ def _search_module(
# add system-wide installed addons to modules list
filename_addons_s = os.path.join(os.getenv("GISBASE"), "modules.xml")
if os.path.isfile(filename_addons_s):
- addon_menudata_file_s = open(filename_addons_s, "r")
+ addon_menudata_file_s = open(filename_addons_s)
addon_menudata_s = ET.parse(addon_menudata_file_s)
addon_menudata_file_s.close()
addon_items_s = addon_menudata_s.findall("task")
@@ -283,7 +285,7 @@ def _search_module(
}
)
- return sorted(found_modules, key=lambda k: k["name"])
+ return sorted(found_modules, key=itemgetter("name"))
def _basic_search(pattern, name, description, module_keywords) -> bool:
@@ -311,10 +313,7 @@ def _exact_search(keyword, module_keywords):
:param module_keywords: comma separated list of keywords
"""
module_keywords = module_keywords.split(",")
- for current in module_keywords:
- if keyword == current:
- return True
- return False
+ return keyword in module_keywords
def _manpage_search(pattern, name):
diff --git a/scripts/g.search.modules/testsuite/test_g_search_modules.py b/scripts/g.search.modules/testsuite/test_g_search_modules.py
index d280ffc1441..75537eb8d78 100644
--- a/scripts/g.search.modules/testsuite/test_g_search_modules.py
+++ b/scripts/g.search.modules/testsuite/test_g_search_modules.py
@@ -15,6 +15,7 @@
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
from grass.gunittest.gmodules import SimpleModule
+from grass.gunittest.utils import xfail_windows
from grass.script.utils import decode
import unittest
@@ -65,6 +66,7 @@ def test_colored_terminal(self):
stdout = decode(module.outputs.stdout).split()
self.assertEqual(stdout[0], termcolor.colored("r.basins.fill", attrs=["bold"]))
+ @xfail_windows
def test_manual_pages(self):
module = SimpleModule("g.search.modules", keyword="kapri", flags="gm")
self.assertModule(module)
diff --git a/scripts/r.in.wms/wms_drv.py b/scripts/r.in.wms/wms_drv.py
index 186157ad6db..dd666573cfe 100644
--- a/scripts/r.in.wms/wms_drv.py
+++ b/scripts/r.in.wms/wms_drv.py
@@ -736,9 +736,7 @@ def _findTileMats(self, tile_mats, region, bbox):
best_diff = best_scale_den - scale_den
mat_diff = mat_scale_den - scale_den
- if (best_diff < mat_diff and mat_diff < 0) or (
- best_diff > mat_diff and best_diff > 0
- ):
+ if (best_diff < mat_diff < 0) or (best_diff > mat_diff and best_diff > 0):
best_t_mat = t_mat
best_scale_den = mat_scale_den
@@ -1020,9 +1018,7 @@ def _parseTilePattern(self, group_t_patts, bbox, region):
best_diff = best_res - res[comp_res]
tile_diff = t_res[comp_res] - res[comp_res]
- if (best_diff < tile_diff and tile_diff < 0) or (
- best_diff > tile_diff and best_diff > 0
- ):
+ if (best_diff < tile_diff < 0) or (best_diff > tile_diff and best_diff > 0):
best_res = t_res[comp_res]
best_patt = pattern
diff --git a/scripts/r.in.wms/wms_gdal_drv.py b/scripts/r.in.wms/wms_gdal_drv.py
index 69a49ca5429..830be2b593e 100644
--- a/scripts/r.in.wms/wms_gdal_drv.py
+++ b/scripts/r.in.wms/wms_gdal_drv.py
@@ -155,7 +155,7 @@ def _download(self):
xml_file = self._createXML()
# print xml file content for debug level 1
- file = open(xml_file, "r")
+ file = open(xml_file)
gs.debug("WMS request XML:\n%s" % file.read(), 1)
file.close()
diff --git a/scripts/r.pack/r.pack.py b/scripts/r.pack/r.pack.py
index eb11547322c..5029f93c6d2 100644
--- a/scripts/r.pack/r.pack.py
+++ b/scripts/r.pack/r.pack.py
@@ -37,6 +37,8 @@
import atexit
import tarfile
+from pathlib import Path
+
from grass.script.utils import try_rmdir, try_remove
from grass.script import core as grass
@@ -80,7 +82,7 @@ def main():
grass.message(_("Packing <%s> to <%s>...") % (gfile["fullname"], outfile))
basedir = os.path.sep.join(os.path.normpath(gfile["file"]).split(os.path.sep)[:-2])
- olddir = os.getcwd()
+ olddir = Path.cwd()
# copy elements
info = grass.parse_command("r.info", flags="e", map=infile)
@@ -93,7 +95,7 @@ def main():
if map_file["file"]:
vrt = os.path.join(map_file["file"], "vrt")
if os.path.exists(vrt):
- with open(vrt, "r") as f:
+ with open(vrt) as f:
for r in f:
map, mapset = r.split("@")
map_basedir = os.path.sep.join(
diff --git a/scripts/r.semantic.label/testsuite/test_r_semantic_label.py b/scripts/r.semantic.label/testsuite/test_r_semantic_label.py
index b7456e22633..990954dbb5f 100644
--- a/scripts/r.semantic.label/testsuite/test_r_semantic_label.py
+++ b/scripts/r.semantic.label/testsuite/test_r_semantic_label.py
@@ -27,7 +27,7 @@ def read_semantic_label(self):
return rast.info.semantic_label
def test_semantic_label_assign_not_current_mapset(self):
- if not self.mapset == "PERMANENT":
+ if self.mapset != "PERMANENT":
self.mapset.name = "PERMANENT"
a_map = self.mapset.glist(type="raster")[0]
module = SimpleModule(
diff --git a/scripts/r.tileset/testsuite/test_r_tileset.py b/scripts/r.tileset/testsuite/test_r_tileset.py
index 7f1c62ed1f5..cba9908d900 100644
--- a/scripts/r.tileset/testsuite/test_r_tileset.py
+++ b/scripts/r.tileset/testsuite/test_r_tileset.py
@@ -7,6 +7,7 @@
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
from grass.gunittest.gmodules import SimpleModule
+from grass.gunittest.utils import xfail_windows
from grass.script.utils import decode
@@ -36,6 +37,7 @@ def tearDownClass(cls):
"""!Remove the temporary region"""
cls.del_temp_region()
+ @xfail_windows
def test_tiling(self):
"""Produce tiling test"""
module = SimpleModule(
diff --git a/scripts/v.centroids/testsuite/test_v_centroids.py b/scripts/v.centroids/testsuite/test_v_centroids.py
index 4b0654748a5..a7f56464ef6 100644
--- a/scripts/v.centroids/testsuite/test_v_centroids.py
+++ b/scripts/v.centroids/testsuite/test_v_centroids.py
@@ -12,37 +12,47 @@
class TestVCentroids(TestCase):
"""Test v.centroids script"""
- mapName = "busroute11"
- outRouteMap = "busroute11_boundary"
- fromType = "line"
- toType = "boundary"
- outAreaMap = "busroute11_area"
+ region_line = "region_line"
+ region_boundary = "region_boundary"
+ region_area = "region_area"
+ output = "output"
@classmethod
def setUpClass(cls):
"""Create an area from a closed line"""
+ cls.runModule("v.in.region", output=cls.region_line, type="line")
+ cls.runModule("v.in.region", output=cls.region_area, type="area")
cls.runModule(
"v.type",
- input=cls.mapName,
- output=cls.outRouteMap,
- from_type=cls.fromType,
- to_type=cls.toType,
+ input=cls.region_line,
+ output=cls.region_boundary,
+ from_type="line",
+ to_type="boundary",
)
@classmethod
def tearDownClass(cls):
"""Remove the generated maps"""
cls.runModule(
- "g.remove", flags="f", type="vector", name=(cls.outRouteMap, cls.outAreaMap)
+ "g.remove",
+ flags="f",
+ type="vector",
+ name=(cls.region_line, cls.region_area, cls.region_boundary),
)
+ def tearDown(self):
+ """Remove the generated maps"""
+ self.runModule("g.remove", flags="f", type="vector", name=self.output)
+
def test_area(self):
"""Adds missing centroids to closed boundaries test"""
module = SimpleModule(
- "v.centroids", input=self.outRouteMap, output=self.outAreaMap
+ "v.centroids", input=self.region_boundary, output=self.output
)
self.assertModule(module)
- self.assertVectorExists(self.outAreaMap)
+ self.assertVectorInfoEqualsVectorInfo(
+ self.output, self.region_area, precision=1e-6
+ )
if __name__ == "__main__":
diff --git a/scripts/v.pack/v.pack.py b/scripts/v.pack/v.pack.py
index cd8d96b86cd..4468c888bcb 100755
--- a/scripts/v.pack/v.pack.py
+++ b/scripts/v.pack/v.pack.py
@@ -38,6 +38,8 @@
import tarfile
import atexit
+from pathlib import Path
+
from grass.script.utils import try_rmdir, try_remove
from grass.script import core as grass
from grass.script import vector
@@ -128,7 +130,7 @@ def main():
tar.add(path, "PROJ_" + support)
tar.close()
- grass.message(_("Pack file <%s> created") % os.path.join(os.getcwd(), outfile))
+ grass.message(_("Pack file <%s> created") % Path(outfile).resolve())
if __name__ == "__main__":
diff --git a/scripts/v.rast.stats/testsuite/test_v_rast_stats.py b/scripts/v.rast.stats/testsuite/test_v_rast_stats.py
index ca3111d14d8..56acbce5964 100644
--- a/scripts/v.rast.stats/testsuite/test_v_rast_stats.py
+++ b/scripts/v.rast.stats/testsuite/test_v_rast_stats.py
@@ -5,6 +5,7 @@
from grass.gunittest.case import TestCase
from grass.gunittest.gmodules import SimpleModule
+from grass.gunittest.utils import xfail_windows
from grass.pygrass.vector import VectorTopo
from grass.pygrass.vector.geometry import Line
from grass.pygrass.vector.geometry import Boundary
@@ -71,6 +72,7 @@ def setUp(self):
vt.table.conn.commit()
vt.close()
+ @xfail_windows
def test_1(self):
# Output of v.rast.stats
univar_string = """cat|value|label|a_minimum|a_maximum|a_sum
@@ -91,6 +93,7 @@ def test_1(self):
self.runModule(v_db_select)
self.assertLooksLike(univar_string, str(v_db_select.outputs.stdout))
+ @xfail_windows
def test_line_d(self):
output_str = """cat|name|a_median|a_number|a_range
1|first|192|3|1
@@ -109,6 +112,7 @@ def test_line_d(self):
self.runModule(v_db_select)
self.assertLooksLike(output_str, str(v_db_select.outputs.stdout))
+ @xfail_windows
def test_line(self):
output_str = """cat|name|a_median|a_number|a_range
1|first|192|5|2
@@ -128,6 +132,7 @@ def test_line(self):
self.runModule(v_db_select)
self.assertLooksLike(output_str, str(v_db_select.outputs.stdout))
+ @xfail_windows
def test_zone_all(self):
# Output of v.rast.stats
univar_string = """cat|value|label|a_number|a_null_cells|a_minimum|a_maximum|a_range|a_average|a_stddev|a_variance|a_coeff_var|a_sum|a_first_quartile|a_median|a_third_quartile|a_percentile_90
@@ -143,6 +148,7 @@ def test_zone_all(self):
self.runModule(v_db_select)
self.assertLooksLike(univar_string, str(v_db_select.outputs.stdout))
+ @xfail_windows
def test_small_area_with_centroid(self):
# Output of v.rast.stats
univar_string = """cat|name|a_number|a_null_cells|a_minimum|a_maximum|a_range|a_average|a_stddev|a_variance|a_coeff_var|a_sum|a_first_quartile|a_median|a_third_quartile|a_percentile_90
diff --git a/scripts/v.report/v.report.py b/scripts/v.report/v.report.py
index 18752a697cb..9da9a8b1de7 100755
--- a/scripts/v.report/v.report.py
+++ b/scripts/v.report/v.report.py
@@ -55,6 +55,8 @@
import sys
import os
+from operator import itemgetter
+
import grass.script as gs
from grass.script.utils import separator, decode
@@ -134,7 +136,7 @@ def main():
if p.returncode != 0:
sys.exit(1)
- records1.sort(key=lambda r: r[catcol])
+ records1.sort(key=itemgetter(catcol))
if len(records1) == 0:
try:
diff --git a/scripts/v.unpack/v.unpack.py b/scripts/v.unpack/v.unpack.py
index 419e133b195..ea75d443b57 100644
--- a/scripts/v.unpack/v.unpack.py
+++ b/scripts/v.unpack/v.unpack.py
@@ -218,7 +218,7 @@ def main():
todb = dbconn["database"]
# return the list of old connection for extract layer number and key
- dbln = open(os.path.join(new_dir, "dbln"), "r")
+ dbln = open(os.path.join(new_dir, "dbln"))
dbnlist = dbln.readlines()
dbln.close()
# check if dbf or sqlite directory exists
diff --git a/scripts/v.what.vect/testsuite/test_v_what_vect.py b/scripts/v.what.vect/testsuite/test_v_what_vect.py
index 09ade33668f..2385a71c400 100644
--- a/scripts/v.what.vect/testsuite/test_v_what_vect.py
+++ b/scripts/v.what.vect/testsuite/test_v_what_vect.py
@@ -9,7 +9,6 @@
from grass.gunittest.gmodules import SimpleModule
from grass.script.core import run_command
-from grass.script.utils import decode
class TestVWhatVect(TestCase):
@@ -29,20 +28,20 @@ def tearDownClass(cls):
def test_what_vect(self):
"""Uploads vector values"""
- run_command("v.db.addcolumn", map=self.mapName, columns="urb_name varchar(25)")
+ run_command("v.db.addcolumn", map=self.mapName, columns="geology_cat integer")
module = SimpleModule(
"v.what.vect",
map=self.mapName,
- query_map="urbanarea",
- column="urb_name",
- query_column="NAME",
+ query_map="geology",
+ column="geology_cat",
+ query_column="cat",
)
self.assertModule(module)
-
- m = SimpleModule("v.db.select", map=self.mapName)
- self.assertModule(m)
- self.assertRegex(decode(m.outputs.stdout), "urb_name")
+ minmax = "min=11\nmax=1810"
+ self.assertVectorFitsUnivar(
+ map=self.mapName, column="geology_cat", reference=minmax
+ )
if __name__ == "__main__":
diff --git a/temporal/t.rast.gapfill/testsuite/test_gapfill.py b/temporal/t.rast.gapfill/testsuite/test_gapfill.py
index 63cd2b83c26..7b083617d08 100644
--- a/temporal/t.rast.gapfill/testsuite/test_gapfill.py
+++ b/temporal/t.rast.gapfill/testsuite/test_gapfill.py
@@ -12,6 +12,7 @@
from grass.gunittest.case import TestCase
from grass.gunittest.gmodules import SimpleModule
+from grass.gunittest.utils import xfail_windows
class TestRasterToVector(TestCase):
@@ -75,6 +76,7 @@ def tearDown(self):
"""Remove generated data"""
self.runModule("t.remove", flags="df", type="strds", inputs="A")
+ @xfail_windows
def test_simple_2procs(self):
self.assertModule(
"t.rast.gapfill",
@@ -125,6 +127,7 @@ def test_simple_2procs(self):
self.assertModule(rast_list)
self.assertLooksLike(text, rast_list.outputs.stdout)
+ @xfail_windows
def test_simple_where(self):
self.assertModule(
"t.rast.gapfill",
@@ -173,6 +176,7 @@ def test_simple_where(self):
self.assertModule(rast_list)
self.assertLooksLike(text, rast_list.outputs.stdout)
+ @xfail_windows
def test_simple_where_2(self):
self.assertModule(
"t.rast.gapfill",
@@ -216,6 +220,7 @@ def test_simple_where_2(self):
self.assertModule(rast_list)
self.assertLooksLike(text, rast_list.outputs.stdout)
+ @xfail_windows
def test_simple_empty(self):
self.assertModule(
"t.rast.gapfill",
@@ -302,6 +307,7 @@ def test_simple_gran(self):
self.assertModule(rast_list)
self.assertLooksLike(text, rast_list.outputs.stdout)
+ @xfail_windows
def test_simple_gran(self):
self.assertModule(
"t.rast.gapfill",
diff --git a/temporal/t.rast.series/testsuite/test_series.py b/temporal/t.rast.series/testsuite/test_series.py
index cfb17a338cd..99fe69d4352 100644
--- a/temporal/t.rast.series/testsuite/test_series.py
+++ b/temporal/t.rast.series/testsuite/test_series.py
@@ -14,6 +14,7 @@
import grass.temporal as tgis
from grass.gunittest.case import TestCase
from grass.gunittest.gmodules import SimpleModule
+from grass.gunittest.utils import xfail_windows
class TestSnapAbsoluteSTRDS(TestCase):
@@ -146,6 +147,7 @@ def test_minimum_where(self):
map="series_minimum_2", refmin=300, refmax=300, msg="Minimum must be 300"
)
+ @xfail_windows
def test_quantile(self):
self.assertModule(
"t.rast.series",
diff --git a/temporal/t.rast.univar/testsuite/test_t_rast_univar.py b/temporal/t.rast.univar/testsuite/test_t_rast_univar.py
index 11a91614b6c..dca870d6a98 100644
--- a/temporal/t.rast.univar/testsuite/test_t_rast_univar.py
+++ b/temporal/t.rast.univar/testsuite/test_t_rast_univar.py
@@ -11,6 +11,7 @@
from pathlib import Path
from grass.gunittest.case import TestCase
from grass.gunittest.gmodules import SimpleModule
+from grass.gunittest.utils import xfail_windows
class TestRasterUnivar(TestCase):
@@ -152,6 +153,7 @@ def tearDownClass(cls):
cls.del_temp_region()
+ @xfail_windows
def test_with_all_maps(self):
t_rast_univar = SimpleModule(
"t.rast.univar",
@@ -177,6 +179,7 @@ def test_with_all_maps(self):
res_line = res.split("|", 1)[1]
self.assertLooksLike(ref_line, res_line)
+ @xfail_windows
def test_with_subset_of_maps(self):
t_rast_univar = SimpleModule(
"t.rast.univar",
@@ -201,6 +204,7 @@ def test_with_subset_of_maps(self):
res_line = res.split("|", 1)[1]
self.assertLooksLike(ref_line, res_line)
+ @xfail_windows
def test_coarser_resolution(self):
t_rast_univar = SimpleModule(
"t.rast.univar",
@@ -316,6 +320,7 @@ def test_error_handling_no_input(self):
# No input
self.assertModuleFail("t.rast.univar", output="out.txt")
+ @xfail_windows
def test_with_zones(self):
"""Test use of zones"""
@@ -353,6 +358,7 @@ def test_with_zones(self):
res_line = res.split("|", 1)[1]
self.assertLooksLike(ref_line, res_line)
+ @xfail_windows
def test_with_semantic_label(self):
"""Test semantic labels"""
t_rast_univar = SimpleModule(
@@ -379,6 +385,7 @@ def test_with_semantic_label(self):
res_line = res.split("|", 1)[1]
self.assertLooksLike(ref_line, res_line)
+ @xfail_windows
def test_with_semantic_label_parallel(self):
"""Test semantic labels"""
t_rast_univar = SimpleModule(
@@ -406,6 +413,7 @@ def test_with_semantic_label_parallel(self):
res_line = res.split("|", 1)[1]
self.assertLooksLike(ref_line, res_line)
+ @xfail_windows
def test_with_spatial_filter_intersects(self):
"""Test spatial filter overlaps"""
t_rast_univar = SimpleModule(
@@ -434,6 +442,7 @@ def test_with_spatial_filter_intersects(self):
res_line = res.split("|", 1)[1]
self.assertLooksLike(ref_line, res_line)
+ @xfail_windows
def test_with_spatial_filter_contains(self):
"""Test spatial filter contains"""
t_rast_univar = SimpleModule(
@@ -460,6 +469,7 @@ def test_with_spatial_filter_contains(self):
res_line = res.split("|", 1)[1]
self.assertLooksLike(ref_line, res_line)
+ @xfail_windows
def test_with_spatial_filter_is_contained(self):
"""Test spatial filter is_contained"""
t_rast_univar = SimpleModule(
diff --git a/temporal/t.rast.what/t.rast.what.py b/temporal/t.rast.what/t.rast.what.py
index 39e63180abf..b3cec73a538 100755
--- a/temporal/t.rast.what/t.rast.what.py
+++ b/temporal/t.rast.what/t.rast.what.py
@@ -389,7 +389,7 @@ def one_point_per_row_output(
file_name = output_files[count]
gs.verbose(_("Transforming r.what output file %s") % (file_name))
map_list = output_time_list[count]
- in_file = open(file_name, "r")
+ in_file = open(file_name)
for line in in_file:
line = line.split(separator)
if vcat:
@@ -466,7 +466,7 @@ def one_point_per_col_output(
for count in range(len(output_files)):
file_name = output_files[count]
gs.verbose(_("Transforming r.what output file %s") % (file_name))
- in_file = open(file_name, "r")
+ in_file = open(file_name)
lines = in_file.readlines()
matrix = []
@@ -563,7 +563,7 @@ def one_point_per_timerow_output(
file_name = output_files[count]
gs.verbose("Transforming r.what output file %s" % (file_name))
map_list = output_time_list[count]
- in_file = open(file_name, "r")
+ in_file = open(file_name)
if write_header:
if first is True:
diff --git a/temporal/t.rast.what/testsuite/test_what.py b/temporal/t.rast.what/testsuite/test_what.py
index 96432eff4e1..18c7af63807 100644
--- a/temporal/t.rast.what/testsuite/test_what.py
+++ b/temporal/t.rast.what/testsuite/test_what.py
@@ -10,6 +10,7 @@
from grass.gunittest.case import TestCase
from grass.gunittest.gmodules import SimpleModule
+from grass.gunittest.utils import xfail_windows
class TestRasterWhat(TestCase):
@@ -218,6 +219,7 @@ def test_timerow_output_coords(self):
"out_timerow_coords.txt", "ca4ee0e7e4aaca170d6034e0d57d292d", text=True
)
+ @xfail_windows
def test_row_stdout_where_parallel(self):
t_rast_what = SimpleModule(
"t.rast.what",
@@ -245,6 +247,7 @@ def test_row_stdout_where_parallel(self):
"""
self.assertLooksLike(text, str(t_rast_what.outputs.stdout))
+ @xfail_windows
def test_row_stdout_where_parallel_cat(self):
t_rast_what = SimpleModule(
"t.rast.what",
@@ -272,6 +275,7 @@ def test_row_stdout_where_parallel_cat(self):
"""
self.assertLooksLike(text, str(t_rast_what.outputs.stdout))
+ @xfail_windows
def test_row_stdout_where_parallel2(self):
"""Here without output definition, the default is used then"""
@@ -385,6 +389,7 @@ def tearDownClass(cls):
cls.runModule("t.remove", flags="df", type="strds", inputs="A")
cls.del_temp_region()
+ @xfail_windows
def test_null_value(self):
"""Test setting the null value"""
diff --git a/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py b/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py
index 7c8ab1e4100..4ca64e88442 100644
--- a/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py
+++ b/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py
@@ -11,6 +11,7 @@
from pathlib import Path
from grass.gunittest.case import TestCase
from grass.gunittest.gmodules import SimpleModule
+from grass.gunittest.utils import xfail_windows
class TestRasterUnivar(TestCase):
@@ -58,6 +59,7 @@ def tearDownClass(cls):
cls.runModule("g.remove", flags="f", type="raster_3d", name="zones")
cls.del_temp_region()
+ @xfail_windows
def test_with_all_maps(self):
t_rast3d_univar = SimpleModule(
"t.rast3d.univar",
@@ -82,6 +84,7 @@ def test_with_all_maps(self):
res_line = res.split("|", 1)[1]
self.assertLooksLike(ref_line, res_line)
+ @xfail_windows
def test_with_subset_of_maps(self):
t_rast3d_univar = SimpleModule(
"t.rast3d.univar",
@@ -166,6 +169,7 @@ def test_error_handling_no_input(self):
# No input
self.assertModuleFail("t.rast3d.univar", output="out.txt")
+ @xfail_windows
def test_with_zones(self):
"""Test use of zones"""
@@ -203,6 +207,7 @@ def test_with_zones(self):
res_line = res.split("|", 1)[1]
self.assertLooksLike(ref_line, res_line)
+ @xfail_windows
def test_with_zones_parallel(self):
"""Test use of zones"""
diff --git a/temporal/t.remove/t.remove.py b/temporal/t.remove/t.remove.py
index b5c1164cef4..91fa58dde7a 100755
--- a/temporal/t.remove/t.remove.py
+++ b/temporal/t.remove/t.remove.py
@@ -102,7 +102,7 @@ def main():
# Read the dataset list from file
if file:
- fd = open(file, "r")
+ fd = open(file)
line = True
while True:
diff --git a/temporal/t.unregister/t.unregister.py b/temporal/t.unregister/t.unregister.py
index 7aef44ed12b..b39b7b9819f 100755
--- a/temporal/t.unregister/t.unregister.py
+++ b/temporal/t.unregister/t.unregister.py
@@ -108,7 +108,7 @@ def main():
# Read the map list from file
if file:
- fd = open(file, "r")
+ fd = open(file)
line = True
while True:
diff --git a/utils/g.html2man/rest.py b/utils/g.html2man/rest.py
index 2df87db8e27..c320f18e397 100644
--- a/utils/g.html2man/rest.py
+++ b/utils/g.html2man/rest.py
@@ -1,4 +1,5 @@
import sys
+from operator import itemgetter
def match(node, tag, attr=None, val=None):
@@ -26,8 +27,7 @@ def find(node, tag, attr=None, val=None):
raise ValueError("child not found")
-def children(node):
- return node[2]
+children = itemgetter(2)
def text(node):
diff --git a/utils/gitlog2changelog.py b/utils/gitlog2changelog.py
index 43aedcfb491..5ad459aa2b1 100755
--- a/utils/gitlog2changelog.py
+++ b/utils/gitlog2changelog.py
@@ -78,14 +78,12 @@
dateFound = True
except Exception as e:
print(f"Could not parse dateList = '{line}'. Error: {e!s}")
- # The Fossil-IDs are ignored:
- elif line.startswith((" Fossil-ID:", " [[SVN:")):
- continue
- # The svn-id lines are ignored
- elif " git-svn-id:" in line:
- continue
- # The sign off line is ignored too
- elif "Signed-off-by" in line:
+ # The Fossil-IDs, svn-id, ad sign off lines are ignored:
+ elif (
+ line.startswith((" Fossil-ID:", " [[SVN:"))
+ or " git-svn-id:" in line
+ or "Signed-off-by" in line
+ ):
continue
# Extract the actual commit message for this commit
elif authorFound & dateFound & messageFound is False:
diff --git a/utils/mkhtml.py b/utils/mkhtml.py
index d36ea78663d..f64f6d7d97a 100644
--- a/utils/mkhtml.py
+++ b/utils/mkhtml.py
@@ -159,7 +159,7 @@ def download_git_commit(url, response_format, *args, **kwargs):
"""
try:
response = urlopen(url, *args, **kwargs)
- if not response.code == 200:
+ if response.code != 200:
index = HTTP_STATUS_CODES.index(response.code)
desc = HTTP_STATUS_CODES[index].description
gs.fatal(
@@ -366,7 +366,7 @@ def has_src_code_git(src_dir, is_addon):
if core module or addon
source code has Git
"""
- actual_dir = os.getcwd()
+ actual_dir = Path.cwd()
if is_addon:
os.chdir(src_dir)
else:
diff --git a/vector/v.colors/scan_attr.c b/vector/v.colors/scan_attr.c
index 18c51635f81..e805cd74a7f 100644
--- a/vector/v.colors/scan_attr.c
+++ b/vector/v.colors/scan_attr.c
@@ -42,6 +42,8 @@ int scan_attr(struct Map_info *Map, int layer, const char *column_name,
&cvarr);
if (nrec < 1) {
G_important_message(_("No data selected"));
+ Vect_destroy_field_info(fi);
+ db_close_database(driver);
return 0;
}
@@ -100,6 +102,7 @@ int scan_attr(struct Map_info *Map, int layer, const char *column_name,
}
db_close_database(driver);
+ Vect_destroy_field_info(fi);
return is_fp;
}
diff --git a/vector/v.generalize/displacement.c b/vector/v.generalize/displacement.c
index bc7a3f15170..a4d599080e4 100644
--- a/vector/v.generalize/displacement.c
+++ b/vector/v.generalize/displacement.c
@@ -310,6 +310,8 @@ int snakes_displacement(struct Map_info *In, struct Map_info *Out,
matrix_free(&fy);
matrix_free(&dx_old);
matrix_free(&dy_old);
+ Vect_destroy_cats_struct(Cats);
+ Vect_destroy_line_struct(Points);
return 0;
}
diff --git a/vector/v.out.lidar/testsuite/test_v_out_lidar.py b/vector/v.out.lidar/testsuite/test_v_out_lidar.py
index 9be4f57d3b6..613cb14b61a 100644
--- a/vector/v.out.lidar/testsuite/test_v_out_lidar.py
+++ b/vector/v.out.lidar/testsuite/test_v_out_lidar.py
@@ -12,6 +12,7 @@
import os
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
+from grass.gunittest.utils import xfail_windows
class BasicTest(TestCase):
@@ -60,6 +61,7 @@ def test_module_runs_output_created(self):
self.assertModule("v.out.lidar", input=self.vector_points, output=self.las_file)
self.assertFileExists(self.las_file)
+ @xfail_windows
def test_output_identical(self):
"""Test to see if the standard outputs are created
diff --git a/vector/v.select/testsuite/test_v_select.py b/vector/v.select/testsuite/test_v_select.py
index c74cdb53761..b8564505a6a 100644
--- a/vector/v.select/testsuite/test_v_select.py
+++ b/vector/v.select/testsuite/test_v_select.py
@@ -13,22 +13,9 @@
class TestRasterReport(TestCase):
- binput = "bridges"
+ binput = "zipcodes"
ainput = "geology"
output = "testvselect"
- overlap = "geonames_wake"
- disjoint = "schools_wake"
- equals = "streets_wake"
- touches = "zipcodes_wake"
- within = "geonames_wake"
-
- @classmethod
- def setUpClass(cls):
- cls.use_temp_region()
-
- @classmethod
- def tearDownClass(cls):
- cls.del_temp_region()
def tearDown(cls):
cls.runModule("g.remove", type="vector", flags="f", name=cls.output)
@@ -42,8 +29,8 @@ def test_opo(self):
output=self.output,
operator="overlap",
)
- topology = {"points": 1088, "lines": 0, "areas": 0}
- self.assertVectorFitsTopoInfo(self.overlap, topology)
+ topology = {"areas": 97}
+ self.assertVectorFitsTopoInfo(self.output, topology)
def test_opd(self):
"""Testign operator disjoint"""
@@ -54,8 +41,8 @@ def test_opd(self):
output=self.output,
operator="disjoint",
)
- topology = {"points": 167, "lines": 0, "areas": 0}
- self.assertVectorFitsTopoInfo(self.disjoint, topology)
+ topology = {"areas": 1770}
+ self.assertVectorFitsTopoInfo(self.output, topology)
def test_ope(self):
"""Testing operator equals"""
@@ -66,8 +53,7 @@ def test_ope(self):
output=self.output,
operator="equals",
)
- topology = {"points": 0, "lines": 49746, "areas": 0}
- self.assertVectorFitsTopoInfo(self.equals, topology)
+ self.assertVectorDoesNotExist(self.output)
def test_opt(self):
"""Testing operator touches"""
@@ -78,8 +64,7 @@ def test_opt(self):
output=self.output,
operator="touches",
)
- topology = {"points": 0, "lines": 0, "areas": 48}
- self.assertVectorFitsTopoInfo(self.touches, topology)
+ self.assertVectorDoesNotExist(self.output)
def test_opw(self):
"""Testing operator within"""
@@ -90,8 +75,8 @@ def test_opw(self):
output=self.output,
operator="within",
)
- topology = {"points": 1088, "lines": 0, "areas": 0}
- self.assertVectorFitsTopoInfo(self.within, topology)
+ topology = {"areas": 17}
+ self.assertVectorFitsTopoInfo(self.output, topology)
if __name__ == "__main__":
diff --git a/vector/v.surf.rst/Makefile b/vector/v.surf.rst/Makefile
index 3b5d8eca836..414ebbd85a0 100644
--- a/vector/v.surf.rst/Makefile
+++ b/vector/v.surf.rst/Makefile
@@ -4,9 +4,9 @@ PGM=v.surf.rst
EXTRA_CLEAN_DIRS=doxygenhtml
-LIBES = $(INTERPFLLIB) $(QTREELIB) $(INTERPDATALIB) $(GMATHLIB) $(VECTORLIB) $(DBMILIB) $(GISLIB) $(MATHLIB)
+LIBES = $(INTERPFLLIB) $(QTREELIB) $(INTERPDATALIB) $(GMATHLIB) $(RASTERLIB) $(VECTORLIB) $(DBMILIB) $(GISLIB) $(MATHLIB)
EXTRA_LIBS = $(OPENMP_LIBPATH) $(OPENMP_LIB)
-DEPENDENCIES = $(INTERPFLDEP) $(QTREEDEP) $(INTERPDATADEP) $(GMATHDEP) $(VECTORDEP) $(DBMIDEP) $(GISDEP)
+DEPENDENCIES = $(INTERPFLDEP) $(QTREEDEP) $(INTERPDATADEP) $(GMATHDEP) $(RASTERDEP) $(VECTORDEP) $(DBMIDEP) $(GISDEP)
EXTRA_INC = $(VECT_INC) $(OPENMP_INCPATH)
EXTRA_CFLAGS = $(VECT_CFLAGS) $(OPENMP_CFLAGS)
diff --git a/vector/v.surf.rst/main.c b/vector/v.surf.rst/main.c
index c4581737eed..7b42e269d0f 100644
--- a/vector/v.surf.rst/main.c
+++ b/vector/v.surf.rst/main.c
@@ -33,6 +33,7 @@
#include
#include
+#include
#include
#include
#include
@@ -419,8 +420,8 @@ int main(int argc, char *argv[])
G_warning(_("GRASS GIS is not compiled with OpenMP support, parallel "
"computation is disabled."));
#endif
- if (threads > 1 && G_find_raster("MASK", G_mapset()) != NULL) {
- G_warning(_("Parallel processing disabled due to active MASK."));
+ if (threads > 1 && Rast_mask_is_present()) {
+ G_warning(_("Parallel processing disabled due to active mask."));
threads = 1;
}
if (devi) {
diff --git a/vector/v.transform/main.c b/vector/v.transform/main.c
index 400cd3ded91..06a7d105881 100644
--- a/vector/v.transform/main.c
+++ b/vector/v.transform/main.c
@@ -188,8 +188,17 @@ int main(int argc, char *argv[])
if (G_parser(argc, argv))
exit(EXIT_FAILURE);
- strcpy(Current.name, vold->answer);
- strcpy(Trans.name, vnew->answer);
+ if (G_strlcpy(Current.name, vold->answer, sizeof(Current.name)) >=
+ sizeof(Current.name)) {
+ G_fatal_error(_("Input vector map name <%s> is too long"),
+ vold->answer);
+ }
+
+ if (G_strlcpy(Trans.name, vnew->answer, sizeof(Trans.name)) >=
+ sizeof(Trans.name)) {
+ G_fatal_error(_("Output vector map name <%s> is too long"),
+ vnew->answer);
+ }
Vect_check_input_output_name(vold->answer, vnew->answer, G_FATAL_EXIT);
diff --git a/vector/v.univar/testsuite/test_v_univar.py b/vector/v.univar/testsuite/test_v_univar.py
index 766871d951c..e5fc2685cac 100644
--- a/vector/v.univar/testsuite/test_v_univar.py
+++ b/vector/v.univar/testsuite/test_v_univar.py
@@ -15,9 +15,11 @@
from grass.gunittest.case import TestCase
from grass.gunittest.main import test
from grass.gunittest.gmodules import SimpleModule
+from grass.gunittest.utils import xfail_windows
class TestProfiling(TestCase):
+ @xfail_windows
def test_flagg(self):
"""Testing flag g with map lakes"""
output_str = """n=15279
@@ -40,6 +42,7 @@ def test_flagg(self):
v_univar.run()
self.assertLooksLike(actual=v_univar.outputs.stdout, reference=output_str)
+ @xfail_windows
def test_flage(self):
"""Testing flag e with map geology"""
output_str = """number of features with non NULL attribute: 1832
@@ -68,6 +71,7 @@ def test_flage(self):
v_univar.run()
self.assertLooksLike(actual=v_univar.outputs.stdout, reference=output_str)
+ @xfail_windows
def test_flagw(self):
"""Testing flag w with map lakes"""
output_str = """number of features with non NULL attribute: 15279
@@ -83,6 +87,7 @@ def test_flagw(self):
v_univar.run()
self.assertLooksLike(actual=v_univar.outputs.stdout, reference=output_str)
+ @xfail_windows
def test_flagd(self):
"""Testing flag d with map hospitals"""
univar_string = """number of primitives: 160
diff --git a/vector/v.vol.rst/user1.c b/vector/v.vol.rst/user1.c
index f47324dff36..6a1c2f441e6 100644
--- a/vector/v.vol.rst/user1.c
+++ b/vector/v.vol.rst/user1.c
@@ -220,6 +220,7 @@ int INPUT(struct Map_info *In, char *column, char *scol, char *wheresql)
if (a < 0) {
G_warning(_("Can't insert %lf,%lf,%lf,%lf,%lf a=%d"), x, y, z,
w, sm, a);
+ Vect_destroy_field_info(Fi);
return -1;
}
@@ -322,6 +323,7 @@ int INPUT(struct Map_info *In, char *column, char *scol, char *wheresql)
}
else {
fprintf(stderr, "ERROR: zero points in the given region!\n");
+ Vect_destroy_field_info(Fi);
return -1;
}
}
@@ -332,6 +334,7 @@ int INPUT(struct Map_info *In, char *column, char *scol, char *wheresql)
KMIN, KMAX);
fprintf(stderr, "for smooth connection of segments, npmin > segmax "
"(see manual) \n");
+ Vect_destroy_field_info(Fi);
return -1;
}
@@ -382,6 +385,7 @@ int INPUT(struct Map_info *In, char *column, char *scol, char *wheresql)
}
G_message(_("Bitmap mask created"));
}
+ Vect_destroy_field_info(Fi);
return 1;
}