diff --git a/.coveragerc b/.coveragerc index 65e13c2234b..409fde55643 100644 --- a/.coveragerc +++ b/.coveragerc @@ -9,6 +9,7 @@ omit = ${INITIAL_PWD-.}/.github/* ${INITIAL_PWD-.}/bin.*/* ${INITIAL_PWD-.}/dist.*/* + **/gui/wxpython/*/** **/OBJ.*/* source = . diff --git a/.flake8 b/.flake8 index 5126c042439..ffa7d162eda 100644 --- a/.flake8 +++ b/.flake8 @@ -21,23 +21,14 @@ per-file-ignores = # E741 ambiguous variable name 'l' __init__.py: F401, F403 man/build_html.py: E501 - imagery/i.atcorr/create_iwave.py: F632, F821, W293 doc/python/m.distance.py: E501 - doc/gui/wxpython/example/dialogs.py: F401 gui/scripts/d.wms.py: E501 - gui/wxpython/image2target/*: F841, E722 - gui/wxpython/image2target/g.gui.image2target.py: E501, F841 - gui/wxpython/lmgr/frame.py: E722 - # layertree still includes some formatting issues (it is ignored by Black) - gui/wxpython/lmgr/layertree.py: E722 - gui/wxpython/modules/*: F841, E722 - gui/wxpython/nviz/*: F841, E266, E722, F403, F405 + gui/wxpython/image2target/g.gui.image2target.py: E501 + gui/wxpython/nviz/*: E722 gui/wxpython/photo2image/*: F841, E722, E265 gui/wxpython/photo2image/g.gui.photo2image.py: E501, F841 - gui/wxpython/psmap/*: F841, E266, E722, F405, F403 + gui/wxpython/psmap/*: F841, E266, E722 gui/wxpython/vdigit/*: F841, E722, F405, F403 - gui/wxpython/vnet/*: F841 - gui/wxpython/wxgui.py: F841 gui/wxpython/animation/g.gui.animation.py: E501 gui/wxpython/tplot/frame.py: F841, E722 gui/wxpython/tplot/g.gui.tplot.py: E501 @@ -74,7 +65,6 @@ per-file-ignores = # TODO: Is this really needed? python/grass/pygrass/vector/__init__.py: E402 python/grass/pygrass/raster/__init__.py: E402 - python/grass/gunittest/invoker.py: E721 python/grass/pygrass/vector/__init__.py: E402 python/grass/pygrass/modules/interface/*.py: F401 python/grass/pygrass/modules/grid/*.py: F401 @@ -90,13 +80,11 @@ per-file-ignores = python/grass/temporal/temporal_granularity.py: E722 python/grass/temporal/temporal_raster_base_algebra.py: E722 python/grass/temporal/temporal_topology_dataset_connector.py: E722 - python/grass/temporal/univar_statistics.py: E231 # Current benchmarks/tests are changing sys.path before import. # Possibly, a different approach should be taken there anyway. python/grass/pygrass/tests/benchmark.py: E402, F401, F821 # Configuration file for Sphinx: # Ignoring import/code mix and line length. - python/grass/docs/conf.py: E402 # Files not managed by Black python/grass/imaging/images2gif.py: E226 # Unused imports in init files @@ -120,14 +108,12 @@ per-file-ignores = scripts/i.pansharpen/i.pansharpen.py: E722, E501 scripts/r.in.srtm/r.in.srtm.py: E722 scripts/r.fillnulls/r.fillnulls.py: E722 - scripts/d.rast.edit/d.rast.edit.py: E722 - scripts/v.what.strds/v.what.strds.py: E722, E501 + scripts/v.what.strds/v.what.strds.py: E501 # Line too long (esp. module interface definitions) scripts/*/*.py: E501 temporal/t.rast.to.vect/t.rast.to.vect.py: E501 temporal/t.vect.algebra/t.vect.algebra.py: E501 - # ## used (##% key: r etc) - temporal/t.rast.what/t.rast.what.py: E265, E266, E501 + temporal/t.rast.what/t.rast.what.py: E501 # Line too long (esp. module interface definitions) temporal/*/*.py: E501 diff --git a/.github/actions/create-upload-suggestions/action.yml b/.github/actions/create-upload-suggestions/action.yml index 181babe11e9..b80c08c1b46 100644 --- a/.github/actions/create-upload-suggestions/action.yml +++ b/.github/actions/create-upload-suggestions/action.yml @@ -177,7 +177,7 @@ runs: echo "diff-file-name=${INPUT_DIFF_FILE_NAME}" >> "${GITHUB_OUTPUT}" env: INPUT_DIFF_FILE_NAME: ${{ steps.tool-name-safe.outputs.diff-file-name }} - - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 id: upload-diff if: >- ${{ (steps.files_changed.outputs.files_changed == 'true') && @@ -200,7 +200,7 @@ runs: echo 'Suggestions can only be added near to lines changed in this PR.' echo 'If any fixes can be added as code suggestions, they will be added shortly from another workflow.' } >> "${GITHUB_STEP_SUMMARY}" - - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 id: upload-changes if: >- ${{ always() && diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 88158341f13..6672287794c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 + uses: github/codeql-action/init@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 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@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 + uses: github/codeql-action/analyze@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 7b4e7eb18e2..41ac51df8ff 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -107,7 +107,7 @@ jobs: nc_spm_full_v2alpha2.tar.gz" - name: Make HTML test report available if: ${{ !cancelled() }} - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: testreport-macOS path: testreport diff --git a/.github/workflows/milestones.yml b/.github/workflows/milestones.yml new file mode 100644 index 00000000000..e2ade4eb091 --- /dev/null +++ b/.github/workflows/milestones.yml @@ -0,0 +1,70 @@ +--- +name: Assign Milestone + +on: + pull_request_target: + types: [closed] + +jobs: + assign-milestone: + runs-on: ubuntu-latest + if: github.event.pull_request.merged + steps: + # Retreiving the current milestoone from API instead of github context, + # so up-to-date information is used when running after being queued or for reruns + # Otherwise, the information should be available using + # ${{ github.event.pull_request.milestone.title }} + - name: Get current milestone title + id: current-milestone + run: | + echo "milestone<> "${GITHUB_OUTPUT}" + gh pr view ${{ github.event.pull_request.html_url }} --json milestone \ + --jq .milestone.title >> "${GITHUB_OUTPUT}" + echo 'EOF' >> "${GITHUB_OUTPUT}" + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + - name: PR already has a milestone + run: echo "PR already has a milestone" + if: ${{ steps.current-milestone.outputs.milestone }} + - name: PR does not have a milestone + run: echo "PR does not have a milestone" + if: ${{ !steps.current-milestone.outputs.milestone }} + - name: Get VERSION file + if: ${{ !steps.current-milestone.outputs.milestone }} + id: version-file + run: | + echo "version<> "${GITHUB_OUTPUT}" + gh api \ + -H "Accept: application/vnd.github.raw" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "/repos/{owner}/{repo}/contents/include/VERSION?ref=${{ github.sha }}" >> "${GITHUB_OUTPUT}" + echo "EOF" >> "${GITHUB_OUTPUT}" + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + - name: Show version file + if: ${{ !steps.current-milestone.outputs.milestone }} + run: echo "${VERSIONFILE}" + env: + VERSIONFILE: ${{ steps.version-file.outputs.version }} + - name: Get milestone title from VERSION file + if: ${{ !steps.current-milestone.outputs.milestone }} + id: milestone + run: | + version=$(echo "$VERSIONFILE" | head -n 3 | xargs | sed 's/ /./g; s/\(RC[0-9]*\|dev\)//g') + echo "title=$version" >> "${GITHUB_OUTPUT}" + env: + VERSIONFILE: ${{ steps.version-file.outputs.version }} + - name: Show milestone title + if: ${{ !steps.current-milestone.outputs.milestone }} + run: echo "${MILESTONE}" + env: + MILESTONE: ${{ steps.milestone.outputs.title }} + - name: Set PR milestone + if: ${{ !steps.current-milestone.outputs.milestone }} + run: gh pr edit ${{ github.event.pull_request.html_url }} --milestone "${MILESTONE}" + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + MILESTONE: ${{ steps.milestone.outputs.title }} diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index 180cc79ec08..a7694fa9b30 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -120,7 +120,7 @@ jobs: - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: testreport-${{ matrix.os }} path: testreport diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 60ddb223074..e1b3b84b54e 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -83,8 +83,13 @@ jobs: run: | export PYTHONPATH=`grass --config python_path`:$PYTHONPATH export LD_LIBRARY_PATH=$(grass --config path)/lib:$LD_LIBRARY_PATH + export INITIAL_GISBASE="$(grass --config path)" + export INITIAL_PWD="${PWD}" pytest --verbose --color=yes --durations=0 --durations-min=0.5 \ - --numprocesses auto -ra . \ + --numprocesses auto \ + --cov \ + --cov-context=test \ + -ra . \ -m 'not needs_solo_run' - name: Run pytest with a single worker (for tests marked with needs_solo_run) @@ -92,9 +97,11 @@ jobs: export PYTHONPATH=`grass --config python_path`:$PYTHONPATH export LD_LIBRARY_PATH=$(grass --config path)/lib:$LD_LIBRARY_PATH export INITIAL_GISBASE="$(grass --config path)" - INITIAL_PWD="${PWD}" pytest --verbose --color=yes --durations=0 --durations-min=0.5 \ + export INITIAL_PWD="${PWD}" + pytest --verbose --color=yes --durations=0 --durations-min=0.5 \ --cov \ --cov-context=test \ + --cov-append \ -ra . \ -m 'needs_solo_run' - name: Fix non-standard installed script paths in coverage data @@ -116,7 +123,7 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} - name: Make python-only code coverage test report available if: ${{ !cancelled() }} - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: python-codecoverage-report-${{ matrix.os }}-${{ matrix.python-version }} path: coverage_html_report diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 5da86464470..9f71c28e922 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.10" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.6.9" + RUFF_VERSION: "0.7.0" runs-on: ${{ matrix.os }} permissions: @@ -129,13 +129,13 @@ jobs: bandit -c pyproject.toml -iii -r . -f sarif -o bandit.sarif --exit-zero - name: Upload Bandit Scan Results - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: bandit.sarif path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 + uses: github/codeql-action/upload-sarif@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 with: sarif_file: bandit.sarif @@ -201,7 +201,7 @@ jobs: cp -rp dist.$ARCH/docs/html/libpython sphinx-grass - name: Make Sphinx documentation available - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: sphinx-grass path: sphinx-grass diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 88ddd0d31b2..966dbfe21ab 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -149,7 +149,7 @@ jobs: - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: testreport-${{ matrix.os }}-${{ matrix.config }}-${{ matrix.extra-include }} path: testreport diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2c136da40ff..4c82e138950 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.9 + rev: v0.7.0 hooks: # Run the linter. - id: ruff diff --git a/Dockerfile b/Dockerfile index 41fb3264c07..a5c6ab1ee1d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ ARG GUI=without -FROM ubuntu:22.04@sha256:58b87898e82351c6cf9cf5b9f3c20257bb9e2dcf33af051e12ce532d7f94e3fe as common_start +FROM ubuntu:22.04@sha256:0e5e4a57c2499249aafc3b40fcd541e9a456aab7296681a3994d631587203f97 as common_start LABEL authors="Carmen Tawalika,Markus Neteler,Anika Weinmann,Stefan Blumentrath" LABEL maintainer="tawalika@mundialis.de,neteler@mundialis.de,weinmann@mundialis.de" diff --git a/display/d.histogram/d.histogram.html b/display/d.histogram/d.histogram.html index 6e99c940290..43cca2d7223 100644 --- a/display/d.histogram/d.histogram.html +++ b/display/d.histogram/d.histogram.html @@ -10,7 +10,7 @@

DESCRIPTION

NOTES

d.histogram respects the current geographic region settings -and the current MASK (if one exists). +and the current raster mask (if mask is active).

d.histogram uses the colors in the map's color look-up table (i.e., the map's colr or colr2 file). diff --git a/display/d.vect/attr.c b/display/d.vect/attr.c index 5a28807cbca..747b98c59af 100644 --- a/display/d.vect/attr.c +++ b/display/d.vect/attr.c @@ -35,9 +35,11 @@ int display_attr(struct Map_info *Map, int type, char *attrcol, db_init_string(&text); fi = Vect_get_field(Map, lattr->field); - if (fi == NULL) + if (fi == NULL) { + Vect_destroy_line_struct(Points); + Vect_destroy_cats_struct(Cats); return 1; - + } driver = db_start_driver_open_database(fi->driver, fi->database); if (driver == NULL) G_fatal_error(_("Unable to open database <%s> by driver <%s>"), @@ -140,6 +142,7 @@ int display_attr(struct Map_info *Map, int type, char *attrcol, db_close_database_shutdown_driver(driver); Vect_destroy_line_struct(Points); Vect_destroy_cats_struct(Cats); + Vect_destroy_field_info(fi); return 0; } diff --git a/doc/gui/wxpython/example/dialogs.py b/doc/gui/wxpython/example/dialogs.py index 425ea57a4b2..98760e7202d 100644 --- a/doc/gui/wxpython/example/dialogs.py +++ b/doc/gui/wxpython/example/dialogs.py @@ -20,7 +20,7 @@ # So we need to import it before any of the GUI code. # NOTE: in this particular case, we don't really need the grass library; # NOTE: we import it just for the side effects of gettext.install() -import grass +import grass # noqa: F401 from core import globalvar from gui_core.dialogs import SimpleDialog diff --git a/doc/python/raster_example_ctypes.py b/doc/python/raster_example_ctypes.py index 67f7a2cf4cb..54de0aab30b 100644 --- a/doc/python/raster_example_ctypes.py +++ b/doc/python/raster_example_ctypes.py @@ -40,10 +40,7 @@ sys.exit("You must be in GRASS GIS to run this program") # parse command line arguments, prompt user for a raster map name if one wasn't given -if len(sys.argv) == 2: - input = sys.argv[1] -else: - input = input("Name of raster map? ") +input = sys.argv[1] if len(sys.argv) == 2 else input("Name of raster map? ") # initialize GRASS library G_gisinit("") diff --git a/doc/python/vector_example_ctypes.py b/doc/python/vector_example_ctypes.py index ae3877224d0..27053d653c9 100644 --- a/doc/python/vector_example_ctypes.py +++ b/doc/python/vector_example_ctypes.py @@ -33,10 +33,7 @@ if "GISBASE" not in os.environ: sys.exit("You must be in GRASS GIS to run this program.") -if len(sys.argv) == 2: - input = sys.argv[1] -else: - input = input("Name of vector map? ") +input = sys.argv[1] if len(sys.argv) == 2 else input("Name of vector map? ") # initialize GRASS library G_gisinit("") diff --git a/docker/ubuntu/Dockerfile b/docker/ubuntu/Dockerfile index 41fb3264c07..a5c6ab1ee1d 100644 --- a/docker/ubuntu/Dockerfile +++ b/docker/ubuntu/Dockerfile @@ -5,7 +5,7 @@ ARG GUI=without -FROM ubuntu:22.04@sha256:58b87898e82351c6cf9cf5b9f3c20257bb9e2dcf33af051e12ce532d7f94e3fe as common_start +FROM ubuntu:22.04@sha256:0e5e4a57c2499249aafc3b40fcd541e9a456aab7296681a3994d631587203f97 as common_start LABEL authors="Carmen Tawalika,Markus Neteler,Anika Weinmann,Stefan Blumentrath" LABEL maintainer="tawalika@mundialis.de,neteler@mundialis.de,weinmann@mundialis.de" diff --git a/docker/ubuntu_wxgui/Dockerfile b/docker/ubuntu_wxgui/Dockerfile index e15d5dcf478..0d1ead788a3 100644 --- a/docker/ubuntu_wxgui/Dockerfile +++ b/docker/ubuntu_wxgui/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04@sha256:58b87898e82351c6cf9cf5b9f3c20257bb9e2dcf33af051e12ce532d7f94e3fe +FROM ubuntu:22.04@sha256:0e5e4a57c2499249aafc3b40fcd541e9a456aab7296681a3994d631587203f97 LABEL authors="Carmen Tawalika,Markus Neteler,Anika Weinmann,Tomas Zigo" LABEL maintainer="tawalika@mundialis.de,neteler@mundialis.de,weinmann@mundialis.de" diff --git a/flake.lock b/flake.lock index 51f61fb86f9..eacc92d3c4f 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1726153070, - "narHash": "sha256-HO4zgY0ekfwO5bX0QH/3kJ/h4KvUDFZg8YpkNwIbg1U=", + "lastModified": 1727826117, + "narHash": "sha256-K5ZLCyfO/Zj9mPFldf3iwS6oZStJcU4tSpiXTMYaaL0=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "bcef6817a8b2aa20a5a6dbb19b43e63c5bf8619a", + "rev": "3d04084d54bedc3d6b8b736c70ef449225c361b1", "type": "github" }, "original": { @@ -19,11 +19,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1727648392, - "narHash": "sha256-VTlVv1nSxImFxY6RPQpNZxvEOQ0u5s1wBFDgixySNDo=", + "lastModified": 1728538411, + "narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "4e0c36e4dd53f35d5a6385bdae88895ec5832f70", + "rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221", "type": "github" }, "original": { @@ -35,14 +35,14 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1725233747, - "narHash": "sha256-Ss8QWLXdr2JCBPcYChJhz4xJm+h/xjl4G0c0XlP6a74=", + "lastModified": 1727825735, + "narHash": "sha256-0xHYkMkeLVQAMa7gvkddbPqpxph+hDzdu1XdGPJR+Os=", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/fb192fec7cc7a4c26d51779e9bab07ce6fa5597a.tar.gz" }, "original": { "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/fb192fec7cc7a4c26d51779e9bab07ce6fa5597a.tar.gz" } }, "root": { diff --git a/flake.nix b/flake.nix index 51174e1da74..a105607417f 100644 --- a/flake.nix +++ b/flake.nix @@ -34,6 +34,8 @@ pytest ]; + LOCALE_ARCHIVE = "${pkgs.glibcLocales}/lib/locale/locale-archive"; + shellHook = '' function dev-help { echo -e "\nWelcome to a GRASS development environment !" diff --git a/general/g.gisenv/g.gisenv.html b/general/g.gisenv/g.gisenv.html index 833e5051b36..4f5d9e165b3 100644 --- a/general/g.gisenv/g.gisenv.html +++ b/general/g.gisenv/g.gisenv.html @@ -149,6 +149,32 @@

NOTES

variables are stored in <gisdbase>/<project>/<mapset>/VAR after the current GRASS session is closed. +

EXAMPLES

+ +

Cache for raster operations

+ +The maximum memory to be used, i.e. the cache size for raster rows, is set +to 300 MB by default (GRASS variable MEMORYMB). To speed up +raster operations, it is recommended to increase this setting if enough RAM +is available. It is important to note that parallel processes will each +consume this amount of RAM. + +Set the maximum memory to be used (in MB), i.e. the cache size for raster rows: + +
+# set to 6 GB (default: 300 MB)
+g.gisenv set="MEMORYMB=6000"
+
+ +

Number of threads for parallel computing

+ +Set the number of threads for parallel computing: + +
+# set to use 12 threads (default: 1)
+g.gisenv set="NPROCS=12"
+
+

GRASS Debugging

To print debugging messages, the variable DEBUG must be set to level @@ -189,7 +215,8 @@

SEE ALSO

-See also variables list +See also +list of selected GRASS gisenv variables

AUTHOR

diff --git a/gui/wxpython/core/utils.py b/gui/wxpython/core/utils.py index 3c1b4a7dd14..2b1f3f4997a 100644 --- a/gui/wxpython/core/utils.py +++ b/gui/wxpython/core/utils.py @@ -20,6 +20,7 @@ import re import inspect import operator +from string import digits from grass.script import core as grass from grass.script import task as gtask @@ -936,7 +937,7 @@ def SetAddOnPath(addonPath=None, key="PATH"): def color_resolve(color): - if len(color) > 0 and color[0] in "0123456789": + if len(color) > 0 and color[0] in digits: rgb = tuple(map(int, color.split(":"))) label = color else: diff --git a/gui/wxpython/gmodeler/g.gui.gmodeler.html b/gui/wxpython/gmodeler/g.gui.gmodeler.html index 631c06de9d8..a840768abeb 100644 --- a/gui/wxpython/gmodeler/g.gui.gmodeler.html +++ b/gui/wxpython/gmodeler/g.gui.gmodeler.html @@ -466,7 +466,7 @@

SEE ALSO

See also selected user models available from -GRASS Addons repository. +GRASS Addons repository.

See also diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index 23e308037ff..fdf6ac63b36 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -2285,7 +2285,6 @@ def _processComments(self): "size": size, "text": text, "id": int(node.get("id", -1)), - "text": text, } ) diff --git a/gui/wxpython/gui_core/goutput.py b/gui/wxpython/gui_core/goutput.py index 3c8927f2233..6676eb69935 100644 --- a/gui/wxpython/gui_core/goutput.py +++ b/gui/wxpython/gui_core/goutput.py @@ -20,6 +20,7 @@ """ import textwrap +from string import digits import wx from wx import stc @@ -624,7 +625,7 @@ def AddStyledMessage(self, message, style=None): self.linePos = self.GetCurrentPos() if c != " ": last_c = c - if last_c not in ("0123456789"): + if last_c not in (digits): self.AddTextWrapped("\n", wrap=None) self.linePos = -1 else: diff --git a/gui/wxpython/iclass/frame.py b/gui/wxpython/iclass/frame.py index 606c364c76f..b19d8d7d3ac 100644 --- a/gui/wxpython/iclass/frame.py +++ b/gui/wxpython/iclass/frame.py @@ -515,7 +515,7 @@ def ActivateSecondMap(self, event=None): def GetMapToolbar(self): """Returns toolbar with zooming tools""" - return self.toolbars["iClassMap"] if "iClassMap" in self.toolbars else None + return self.toolbars.get("iClassMap", None) def GetClassColor(self, cat): """Get class color as string diff --git a/gui/wxpython/lmgr/frame.py b/gui/wxpython/lmgr/frame.py index 80f373239f8..963a36e3a58 100644 --- a/gui/wxpython/lmgr/frame.py +++ b/gui/wxpython/lmgr/frame.py @@ -271,7 +271,7 @@ def show_menu_errors(messages): y = client_disp[1] self.SetPosition((x, y)) self.SetSize((w, h)) - except: + except (ValueError, IndexError): pass else: # does center (of screen) make sense for lmgr? @@ -884,7 +884,7 @@ def OnCBPageChanged(self, event): try: self.GetMapDisplay().SetFocus() self.GetMapDisplay().Raise() - except: + except AttributeError: pass event.Skip() @@ -1123,7 +1123,7 @@ def GetMenuCmd(self, event): try: cmdlist = cmd.split(" ") - except: # already list? + except AttributeError: # already list? cmdlist = cmd # check list of dummy commands for GUI modules that do not have GRASS @@ -1135,7 +1135,7 @@ def GetMenuCmd(self, event): layer = self.GetLayerTree().layer_selected name = self.GetLayerTree().GetLayerInfo(layer, key="maplayer").name type = self.GetLayerTree().GetLayerInfo(layer, key="type") - except: + except (AttributeError, TypeError): layer = None if layer and len(cmdlist) == 1: # only if no parameters given @@ -1183,7 +1183,7 @@ def OnVDigit(self, event): # available only for vector map layers try: mapLayer = tree.GetLayerInfo(layer, key="maplayer") - except: + except (AttributeError, TypeError): mapLayer = None if not mapLayer or mapLayer.GetType() != "vector": @@ -1860,7 +1860,7 @@ def OnShowAttributeTable(self, event, selection=None): # available only for vector map layers try: maptype = tree.GetLayerInfo(layer, key="maplayer").type - except: + except (AttributeError, TypeError): maptype = None if not maptype or maptype != "vector": diff --git a/gui/wxpython/lmgr/giface.py b/gui/wxpython/lmgr/giface.py index eac9a7fb2d8..67669039d5d 100644 --- a/gui/wxpython/lmgr/giface.py +++ b/gui/wxpython/lmgr/giface.py @@ -54,7 +54,9 @@ def __init__(self, tree): self._tree = tree def __len__(self): - return len(list(self)) + # The list constructor calls __len__ as an optimization if available, + # causing a RecursionError + return len([layer for layer in self]) # noqa: C416 def __iter__(self): """Iterates over the contents of the list.""" diff --git a/gui/wxpython/lmgr/layertree.py b/gui/wxpython/lmgr/layertree.py index daefab44223..855896e35ab 100644 --- a/gui/wxpython/lmgr/layertree.py +++ b/gui/wxpython/lmgr/layertree.py @@ -1742,7 +1742,7 @@ def OnDeleteLayer(self, event): try: item.properties.Close(True) - except: + except AttributeError: pass if item != self.root: @@ -1758,7 +1758,7 @@ def OnDeleteLayer(self, event): try: if self.GetLayerInfo(item, key="type") != "group": self.Map.DeleteLayer(self.GetLayerInfo(item, key="maplayer")) - except: + except (AttributeError, TypeError): pass # redraw map if auto-rendering is enabled @@ -1997,10 +1997,7 @@ def OnEndDrag(self, event): def OnDrop(self, dropTarget, dragItem): # save everything associated with item to drag - try: - old = dragItem # make sure this member exists - except: - return + old = dragItem # make sure this member exists Debug.msg(4, "LayerTree.OnDrop(): layer=%s" % (self.GetItemText(dragItem))) @@ -2046,7 +2043,7 @@ def RecreateItem(self, dragItem, dropTarget, parent=None): newctrl.SetValue( self.GetLayerInfo(dragItem, key="maplayer").GetCmd(string=True) ) - except: + except Exception: pass newctrl.Bind(wx.EVT_TEXT_ENTER, self.OnCmdChanged) data = self.GetPyData(dragItem) @@ -2402,7 +2399,7 @@ def __FindSubItemByName(self, item, value): while item and item.IsOk(): try: itemLayer = self.GetLayerInfo(item, key="maplayer") - except KeyError: + except (KeyError, TypeError): return None if itemLayer and value == itemLayer.GetName(): diff --git a/gui/wxpython/lmgr/workspace.py b/gui/wxpython/lmgr/workspace.py index 774c22bf4ed..b1e7aa400f0 100644 --- a/gui/wxpython/lmgr/workspace.py +++ b/gui/wxpython/lmgr/workspace.py @@ -519,12 +519,20 @@ def CreateRecentFilesMenu(self, menu=None): :return None """ if menu: - file_menu = menu.GetMenu( - menuIndex=menu.FindMenu(title=_("File")), - ) - workspace_item = file_menu.FindItem( - id=file_menu.FindItem(itemString=_("Workspace")), - )[0] + menu_index = menu.FindMenu(_("File")) + if menu_index == wx.NOT_FOUND: + # try untranslated version + menu_index = menu.FindMenu("File") + if menu_index == wx.NOT_FOUND: + return + file_menu = menu.GetMenu(menu_index) + workspace_index = file_menu.FindItem(_("Workspace")) + if workspace_index == wx.NOT_FOUND: + workspace_index = file_menu.FindItem("Workspace") + if workspace_index == wx.NOT_FOUND: + return + workspace_item = file_menu.FindItemById(workspace_index) + self._recent_files = RecentFilesMenu( app_name="main", parent_menu=workspace_item.GetSubMenu(), diff --git a/gui/wxpython/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py index d8b6b4180ba..1f8de66dda6 100644 --- a/gui/wxpython/location_wizard/wizard.py +++ b/gui/wxpython/location_wizard/wizard.py @@ -1604,9 +1604,12 @@ def __init__(self, wizard, parent): self, data=None, columns=[_("Code"), _("Description"), _("Parameters")] ) - # epsg.io hyperlink + # A hyperlink to a CRS database (PROJ related) self.tlink = HyperlinkCtrl( - self, id=wx.ID_ANY, label="epsg.io", url="https://epsg.io/" + self, + id=wx.ID_ANY, + label="spatialreference.org", + url="https://spatialreference.org/", ) self.tlink.SetNormalColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)) self.tlink.SetVisitedColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)) @@ -1688,14 +1691,14 @@ def EnableNext(self, enable=True): def OnTextChange(self, event): value = self.searchb.GetValue() if value == "": - self.tlink.SetURL("https://epsg.io/") + self.tlink.SetURL("https://spatialreference.org/") self.epsgcode = None self.epsgdesc = self.epsgparams = "" self.searchb.ChangeValue("") self.OnBrowseCodes(None) self.EnableNext(False) else: - self.tlink.SetURL(str("https://epsg.io/?q={0}".format(value))) + self.tlink.SetURL(f"https://spatialreference.org/ref/?&search={value}") data = self.epsglist.Search(index=[0, 1, 2], pattern=value, firstOnly=False) if data: index = 0 diff --git a/gui/wxpython/mapdisp/frame.py b/gui/wxpython/mapdisp/frame.py index c9b5d86e4ba..0a1660ff06a 100644 --- a/gui/wxpython/mapdisp/frame.py +++ b/gui/wxpython/mapdisp/frame.py @@ -1569,7 +1569,7 @@ def SetProperties( def GetMapToolbar(self): """Returns toolbar with zooming tools""" - return self.toolbars["map"] if "map" in self.toolbars else None + return self.toolbars.get("map", None) def GetDialog(self, name): """Get selected dialog if exist""" diff --git a/gui/wxpython/mapswipe/frame.py b/gui/wxpython/mapswipe/frame.py index a7dd0df8f56..b7b81636605 100644 --- a/gui/wxpython/mapswipe/frame.py +++ b/gui/wxpython/mapswipe/frame.py @@ -545,15 +545,15 @@ class _onDone: we are pasting together 2 rendered images, so we need to know when both are finished.""" - def __init__(self2): + def __init__(self2): # noqa: N805 self2.called = 0 - def __call__(self2): + def __call__(self2): # noqa: N805 self2.called += 1 if self2.called == 2: self2.process() - def process(self2): + def process(self2): # noqa: N805 # create empty white image - needed for line im = wx.Image(width, height) im.Replace(0, 0, 0, 255, 255, 255) diff --git a/gui/wxpython/modules/colorrules.py b/gui/wxpython/modules/colorrules.py index cad74f38c4b..6f48006db44 100644 --- a/gui/wxpython/modules/colorrules.py +++ b/gui/wxpython/modules/colorrules.py @@ -318,12 +318,14 @@ def LoadRules(self): int, self.ruleslines[item][self.attributeType].split(":") ) except ValueError as e: - message = _("Bad color format. Use color format '0:0:0'") + message = ( + _("Bad color format '%s'. Use color format '0:0:0'") % e + ) self.mainPanel.FindWindowById(item + 2000).SetValue((r, g, b)) else: value = float(self.ruleslines[item][self.attributeType]) self.mainPanel.FindWindowById(item + 2000).SetValue(value) - except: + except Exception: continue if message: @@ -405,12 +407,11 @@ def _initLayer(self): layer = sel else: layer = self.layerTree.FindItemByData(key="type", value=self.mapType) - except: + except (AttributeError, TypeError): layer = None if layer: mapLayer = self.layerTree.GetLayerInfo(layer, key="maplayer") name = mapLayer.GetName() - type = mapLayer.GetType() self.selectionInput.SetValue(name) self.inmap = name @@ -1412,7 +1413,7 @@ def AddTemporaryColumn(self, type): modul = "v.db.addcolumn" else: modul = "v.db.addcol" - ret = RunCommand( + RunCommand( modul, parent=self, map=self.inmap, @@ -1430,7 +1431,7 @@ def DeleteTemporaryColumn(self): modul = "v.db.dropcolumn" else: modul = "v.db.dropcol" - ret = RunCommand( + RunCommand( modul, map=self.inmap, layer=self.properties["layer"], @@ -1505,7 +1506,7 @@ def OnAddColumn(self, event): modul = "v.db.addcolumn" else: modul = "v.db.addcol" - ret = RunCommand( + RunCommand( modul, map=self.inmap, layer=self.properties["layer"], diff --git a/gui/wxpython/modules/histogram.py b/gui/wxpython/modules/histogram.py index 92510406ac7..61d41809858 100644 --- a/gui/wxpython/modules/histogram.py +++ b/gui/wxpython/modules/histogram.py @@ -258,7 +258,7 @@ def UpdateHistDone(self): return try: id = self.imagedict[self.img] - except: + except KeyError: return # paint images to PseudoDC @@ -496,7 +496,6 @@ def SaveToFile(self, event): def PrintMenu(self, event): """Print options and output menu""" - point = wx.GetMousePosition() printmenu = Menu() # Add items to the menu setup = wx.MenuItem(printmenu, id=wx.ID_ANY, text=_("Page setup")) @@ -525,7 +524,7 @@ def OnCloseWindow(self, event): """ try: self.propwin.Close(True) - except: + except Exception: pass self.Map.Clean() self.Destroy() diff --git a/gui/wxpython/modules/import_export.py b/gui/wxpython/modules/import_export.py index f2f70e47831..6810484bfdd 100644 --- a/gui/wxpython/modules/import_export.py +++ b/gui/wxpython/modules/import_export.py @@ -18,9 +18,11 @@ @author Martin Landa @author Anna Kratochvilova (GroupDialog, SymbolDialog) +@author William Welch (commands running queue) """ import os +from collections import deque from pathlib import Path @@ -60,6 +62,8 @@ def __init__( self.commandId = -1 # id of running command + self._commands_running = deque() + wx.Dialog.__init__( self, parent, id, title, style=style, name="MultiImportDialog" ) @@ -122,8 +126,9 @@ def __init__( # buttons # # cancel + self._DEFAULT_CLOSE_BTN_TEXT = _("Close dialog") self.btn_close = CloseButton(parent=self.panel) - self.btn_close.SetToolTip(_("Close dialog")) + self.btn_close.SetToolTip(_(self._DEFAULT_CLOSE_BTN_TEXT)) self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose) # run self.btn_run = Button(parent=self.panel, id=wx.ID_OK, label=_("&Import")) @@ -131,7 +136,7 @@ def __init__( self.btn_run.SetDefault() self.btn_run.Bind(wx.EVT_BUTTON, self.OnRun) - self.Bind(wx.EVT_CLOSE, lambda evt: self.Destroy()) + self.Bind(wx.EVT_CLOSE, self._handleCloseEvent) self.notebook = GNotebook(parent=self, style=globalvar.FNPageDStyle) @@ -270,6 +275,33 @@ def _validateOutputMapName(self): return False return True + def _handleCloseEvent(self, event: wx.CloseEvent): + if event.CanVeto() and len(self._commands_running) > 0: + # prevent dialog close while async commands are running + event.Veto() + return + self.Destroy() + + def _addToCommandQueue(self): + self._commands_running.append(True) + + # disable the dialog close button + self.btn_close.SetToolTip( + _("Dialog cannot be closed while command is running.") + ) + self.btn_close.Disable() + + def _removeFromCommandQueue(self): + try: + self._commands_running.pop() + except IndexError: + pass + finally: + if len(self._commands_running) == 0: + # enable the dialog close button + self.btn_close.Enable() + self.btn_close.SetToolTip(self._DEFAULT_CLOSE_BTN_TEXT) + def OnClose(self, event=None): """Close dialog""" self.Close() @@ -461,7 +493,6 @@ def OnRun(self, event): dsn = self.dsnInput.GetDsn() if not dsn: return - ext = self.dsnInput.GetFormatExt() for layer, output, listId in data: userData = {} @@ -496,7 +527,7 @@ def OnRun(self, event): if nBandsStr: try: nBands = int(nBandsStr.rstrip("\n")) - except: + except ValueError: pass if nBands < 0: GWarning(_("Unable to determine number of raster bands"), parent=self) @@ -519,6 +550,7 @@ def OnRun(self, event): ): cmd.append("--overwrite") + self._addToCommandQueue() # run in Layer Manager self._giface.RunCmd( cmd, onDone=self.OnCmdDone, userData=userData, addLayer=False @@ -527,9 +559,11 @@ def OnRun(self, event): def OnCmdDone(self, event): """Load layers and close if required""" if not hasattr(self, "AddLayers"): + self._removeFromCommandQueue() return self.AddLayers(event.returncode, event.cmd, event.userData) + self._removeFromCommandQueue() if event.returncode == 0 and self.closeOnFinish.IsChecked(): self.Close() @@ -613,7 +647,7 @@ def OnRun(self, event): return if not data: - GMessage(_("No layers selected. Operation canceled."), parent=self) + GMessage(parent=self, message=_("No layers selected. Operation canceled.")) return if not self._validateOutputMapName(): @@ -644,13 +678,7 @@ def OnRun(self, event): if ext and layer.rfind(ext) > -1: layer = layer.replace("." + ext, "") if "|" in layer: - layer, geometry = layer.split("|", 1) - else: - geometry = None - - # TODO: v.import has no geometry option - # if geometry: - # cmd.append('geometry=%s' % geometry) + layer = layer.split("|", 1)[0] cmd = self.getSettingsPageCmd() cmd.append("input=%s" % dsn) @@ -670,6 +698,7 @@ def OnRun(self, event): ): cmd.append("--overwrite") + self._addToCommandQueue() # run in Layer Manager self._giface.RunCmd( cmd, onDone=self.OnCmdDone, userData=userData, addLayer=False @@ -678,10 +707,13 @@ def OnRun(self, event): def OnCmdDone(self, event): """Load layers and close if required""" if not hasattr(self, "AddLayers"): + self._removeFromCommandQueue() return self.AddLayers(event.returncode, event.cmd, event.userData) + self._removeFromCommandQueue() + if self.popOGR: os.environ.pop("GRASS_VECTOR_OGR") @@ -880,16 +912,20 @@ def OnRun(self, event): ): cmd.append("--overwrite") + self._commands_running.append(True) # run in Layer Manager self._giface.RunCmd(cmd, onDone=self.OnCmdDone, addLayer=False) def OnCmdDone(self, event): """Load layers and close if required""" if not hasattr(self, "AddLayers"): + self._removeFromCommandQueue() return self.AddLayers(event.returncode, event.cmd) + self._removeFromCommandQueue() + if self.closeOnFinish.IsChecked(): self.Close() diff --git a/gui/wxpython/modules/mapsets_picker.py b/gui/wxpython/modules/mapsets_picker.py index e7ea7a3e40a..73df8fde3cc 100755 --- a/gui/wxpython/modules/mapsets_picker.py +++ b/gui/wxpython/modules/mapsets_picker.py @@ -12,7 +12,7 @@ def main(): - app = wx.App() + wx.App() dlg = MapsetAccess(parent=None) dlg.CenterOnScreen() diff --git a/gui/wxpython/modules/mcalc_builder.py b/gui/wxpython/modules/mcalc_builder.py index b0f2f280409..52630e55820 100644 --- a/gui/wxpython/modules/mcalc_builder.py +++ b/gui/wxpython/modules/mcalc_builder.py @@ -608,7 +608,7 @@ def _addSomething(self, what): if newmcalcstr[-1] != " ": newmcalcstr += " " position_offset += 1 - except: + except IndexError: pass newmcalcstr += what @@ -617,7 +617,7 @@ def _addSomething(self, what): try: if newmcalcstr[-1] != " " and mcalcstr[position] != " ": newmcalcstr += " " - except: + except IndexError: newmcalcstr += " " newmcalcstr += mcalcstr[position:] @@ -632,7 +632,7 @@ def _addSomething(self, what): try: if newmcalcstr[position + position_offset] == " ": position_offset += 1 - except: + except IndexError: pass self.text_mcalc.SetInsertionPoint(position + position_offset) diff --git a/gui/wxpython/nviz/mapwindow.py b/gui/wxpython/nviz/mapwindow.py index 8cf972704e2..1eea5b278a9 100644 --- a/gui/wxpython/nviz/mapwindow.py +++ b/gui/wxpython/nviz/mapwindow.py @@ -412,7 +412,7 @@ def OnPaint(self, event): Debug.msg(1, "GLCanvas.OnPaint()") self.render["overlays"] = True - dc = wx.PaintDC(self) + wx.PaintDC(self) self.DoPaint() def DoPaint(self): @@ -1438,7 +1438,7 @@ def UnloadDataLayers(self, force=False): GError(parent=self, message=e.value) if force and self.baseId > 0: # unload base surface when quitting - ret = self._display.UnloadSurface(self.baseId) + self._display.UnloadSurface(self.baseId) self.baseId = -1 if update: self.lmgr.nviz.UpdateSettings() @@ -2134,10 +2134,10 @@ def UpdateVolumeProperties(self, id, data, isosurfId=None): # sliceId = 0 for slice in data["slice"]: - ret = self._display.AddSlice(id, slice_id=sliceId) + self._display.AddSlice(id, slice_id=sliceId) if "update" in slice["position"]: pos = slice["position"] - ret = self._display.SetSlicePosition( + self._display.SetSlicePosition( id, sliceId, pos["x1"], diff --git a/gui/wxpython/nviz/tools.py b/gui/wxpython/nviz/tools.py index 20b1df1ffbd..7ced7d157e1 100644 --- a/gui/wxpython/nviz/tools.py +++ b/gui/wxpython/nviz/tools.py @@ -667,7 +667,6 @@ def _createAnimationPage(self): parent=panel, id=wx.ID_ANY, label=" %s " % (_("Save image sequence")) ) boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL) - vSizer = wx.BoxSizer(wx.VERTICAL) gridSizer = wx.GridBagSizer(vgap=5, hgap=10) pwd = str(Path.cwd()) @@ -1197,33 +1196,6 @@ def _createSurfacePage(self, parent): flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=3, ) - # - # mask - # - # box = wx.StaticBox (parent = panel, id = wx.ID_ANY, - # label = " %s " % (_("Mask"))) - ## boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL) - ## gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5) - ## - # gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, - # label = _("Mask zeros:")), - # pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL) - ## - # elev = wx.CheckBox(parent = panel, id = wx.ID_ANY, - # label = _("by elevation")) - # elev.Enable(False) # TODO: not implemented yet - ## gridSizer.Add(item = elev, pos = (0, 1)) - ## - # color = wx.CheckBox(parent = panel, id = wx.ID_ANY, - # label = _("by color")) - # color.Enable(False) # TODO: not implemented yet - ## gridSizer.Add(item = color, pos = (0, 2)) - ## - # boxSizer.Add(item = gridSizer, proportion = 1, - # flag = wx.ALL | wx.EXPAND, border = 3) - # pageSizer.Add(item = boxSizer, proportion = 0, - ## flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, - # border = 3) panel.SetSizer(pageSizer) @@ -1853,24 +1825,6 @@ def _createVectorPage(self, parent): icolor.Bind(csel.EVT_COLOURSELECT, self.OnVectorPoints) gridSizer.Add(icolor, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT, pos=(0, 4)) - # icon width - seems to do nothing - # gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, - # label = _("width")), - # pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL | - # wx.ALIGN_RIGHT) - ## - # iwidth = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1), - ## initial = 1, - ## min = 1, - # max = 1e6) - # iwidth.SetName('value') - # iwidth.SetValue(100) - ## self.win['vector']['points']['width'] = iwidth.GetId() - ## iwidth.Bind(wx.EVT_SPINCTRL, self.OnVectorPoints) - ## iwidth.Bind(wx.EVT_TEXT, self.OnVectorPoints) - # gridSizer.Add(item = iwidth, pos = (1, 2), - # flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT) - # icon symbol gridSizer.Add( StaticText(parent=panel, id=wx.ID_ANY, label=_("symbol:")), pos=(0, 5), @@ -2817,7 +2771,6 @@ def OnAnimationFinished(self, mode): self.UpdateFrameIndex(index=0) slider = self.FindWindowById(self.win["anim"]["frameIndex"]["slider"]) - text = self.FindWindowById(self.win["anim"]["frameIndex"]["text"]) if mode == "record": count = anim.GetFrameCount() @@ -2922,7 +2875,7 @@ def OnConstantSelection(self, event): layerIdx = self.FindWindowById(self.win["constant"]["surface"]).GetSelection() if layerIdx == wx.NOT_FOUND: return - name = _("constant#") + str(layerIdx + 1) + data = self.mapWindow.constants[layerIdx] for attr, value in data["constant"].items(): if attr == "color": @@ -3086,7 +3039,6 @@ def _createIsosurfacePanel(self, parent): ) value.Bind(wx.EVT_TEXT_ENTER, self.OnVolumeIsosurfMap) value.Bind(wx.EVT_KILL_FOCUS, self.OnVolumeIsosurfMap) - ## value.Bind(wx.EVT_TEXT, self.OnVolumeIsosurfMap) else: size = (65, -1) value = SpinCtrl(parent=panel, id=wx.ID_ANY, size=size, initial=0) @@ -3123,8 +3075,6 @@ def _createIsosurfacePanel(self, parent): def _createSlicePanel(self, parent): panel = wx.Panel(parent=parent, id=wx.ID_ANY) - vSizer = wx.BoxSizer(wx.HORIZONTAL) - box = StaticBox( parent=panel, id=wx.ID_ANY, label=" %s " % (_("Slice attributes")) ) @@ -3412,12 +3362,11 @@ def OnSetSurface(self, event): """Surface selected, currently used for fringes""" name = event.GetString() try: - data = self._getLayerPropertiesByName(name, mapType="raster")["surface"] + self._getLayerPropertiesByName(name, mapType="raster")["surface"] except: self.EnablePage("fringe", False) return - layer = self._getMapLayerByName(name, mapType="raster") self.EnablePage("fringe", True) def OnSetRaster(self, event): @@ -3425,7 +3374,7 @@ def OnSetRaster(self, event): name = event.GetString() try: data = self._getLayerPropertiesByName(name, mapType="raster")["surface"] - except TypeError as e: + except TypeError: self.EnablePage("surface", False) return @@ -4590,10 +4539,6 @@ def OnVolumeSelect(self, event): if not winUp.IsEnabled(): winUp.Enable() - # update dialog - name = self.FindWindowById(self.win["volume"]["map"]).GetValue() - layer = self._getMapLayerByName(name, mapType="raster_3d") - if mode == "isosurf": data = self.GetLayerData("volume")["volume"]["isosurface"][selection] self.UpdateVolumeIsosurfPage(data) @@ -4694,8 +4639,6 @@ def OnVolumeDelete(self, event): if list.GetCount() > 0: list.SetSelection(list.GetCount() - 1) - name = self.FindWindowById(self.win["volume"]["map"]).GetValue() - layer = self._getMapLayerByName(name, mapType="raster_3d") data = self.GetLayerData("volume")["volume"] vid = data["object"]["id"] @@ -4736,8 +4679,6 @@ def OnVolumeMoveUp(self, event): if sel < 1: return # this should not happen - name = self.FindWindowById(self.win["volume"]["map"]).GetValue() - layer = self._getMapLayerByName(name, mapType="raster_3d") data = self.GetLayerData("volume")["volume"] id = data["object"]["id"] @@ -4776,8 +4717,6 @@ def OnVolumeMoveDown(self, event): if sel >= list.GetCount() - 1: return # this should not happen - name = self.FindWindowById(self.win["volume"]["map"]).GetValue() - layer = self._getMapLayerByName(name, mapType="raster_3d") data = self.GetLayerData("volume")["volume"] id = data["object"]["id"] diff --git a/gui/wxpython/nviz/wxnviz.py b/gui/wxpython/nviz/wxnviz.py index 47cf99c079d..5a223747277 100644 --- a/gui/wxpython/nviz/wxnviz.py +++ b/gui/wxpython/nviz/wxnviz.py @@ -22,9 +22,9 @@ @author Anna Kratochvilova (Google SoC 2011) """ -import sys import locale import struct +import sys from math import sqrt try: @@ -42,26 +42,203 @@ import wx try: - from ctypes import * + from ctypes import ( + CFUNCTYPE, + UNCHECKED, + byref, + c_char_p, + c_double, + c_float, + c_int, + c_ubyte, + create_string_buffer, + pointer, + ) except KeyError as e: print("wxnviz.py: {}".format(e), file=sys.stderr) try: - from grass.lib.gis import * - from grass.lib.raster3d import * - from grass.lib.vector import * - from grass.lib.ogsf import * - from grass.lib.nviz import * - from grass.lib.raster import * + from grass.lib.gis import ( + G_find_raster2, + G_find_raster3d, + G_find_vector2, + G_free, + G_fully_qualified_name, + G_gisinit, + G_set_error_routine, + G_set_percent_routine, + G_unset_error_routine, + G_unset_percent_routine, + G_unset_window, + G_warning, + ) + from grass.lib.nviz import ( + ATT_COLOR, + ATT_EMIT, + ATT_MASK, + ATT_SHINE, + ATT_TOPO, + ATT_TRANSP, + CONST_ATT, + MAP_ATT, + MAP_OBJ_SITE, + MAP_OBJ_SURF, + MAP_OBJ_UNDEFINED, + MAP_OBJ_VECT, + MAP_OBJ_VOL, + Colors, + Nviz_change_exag, + Nviz_color_from_str, + Nviz_del_texture, + Nviz_delete_arrow, + Nviz_delete_scalebar, + Nviz_draw_all, + Nviz_draw_arrow, + Nviz_draw_cplane, + Nviz_draw_fringe, + Nviz_draw_image, + Nviz_draw_model, + Nviz_draw_quick, + Nviz_draw_scalebar, + Nviz_flythrough, + Nviz_get_bgcolor, + Nviz_get_cplane_rotation, + Nviz_get_cplane_translation, + Nviz_get_current_cplane, + Nviz_get_exag, + Nviz_get_exag_height, + Nviz_get_focus, + Nviz_get_longdim, + Nviz_get_max_texture, + Nviz_get_modelview, + Nviz_get_viewpoint_height, + Nviz_get_viewpoint_position, + Nviz_get_xyrange, + Nviz_get_zrange, + Nviz_has_focus, + Nviz_init_data, + Nviz_init_rotation, + Nviz_init_view, + Nviz_load_image, + Nviz_look_here, + Nviz_new_map_obj, + Nviz_num_cplanes, + Nviz_off_cplane, + Nviz_on_cplane, + Nviz_resize_window, + Nviz_set_2D, + Nviz_set_arrow, + Nviz_set_attr, + Nviz_set_bgcolor, + Nviz_set_cplane_here, + Nviz_set_cplane_rotation, + Nviz_set_cplane_translation, + Nviz_set_fence_color, + Nviz_set_focus, + Nviz_set_focus_map, + Nviz_set_fringe, + Nviz_set_light_ambient, + Nviz_set_light_bright, + Nviz_set_light_color, + Nviz_set_light_position, + Nviz_set_rotation, + Nviz_set_scalebar, + Nviz_set_surface_attr_default, + Nviz_set_viewpoint_height, + Nviz_set_viewpoint_persp, + Nviz_set_viewpoint_position, + Nviz_set_viewpoint_twist, + Nviz_unset_attr, + Nviz_unset_rotation, + nv_data, + ) + from grass.lib.ogsf import ( + GS_clear, + GS_delete_surface, + GS_get_cat_at_xy, + GS_get_distance_alongsurf, + GS_get_rotation_matrix, + GS_get_selected_point_on_surface, + GS_get_surf_list, + GS_get_trans, + GS_get_val_at_xy, + GS_get_viewdir, + GS_libinit, + GS_num_surfs, + GS_set_att_const, + GS_set_drawmode, + GS_set_drawres, + GS_set_rotation_matrix, + GS_set_trans, + GS_set_viewdir, + GS_set_wire_color, + GS_setall_drawmode, + GS_setall_drawres, + GS_surf_exists, + GS_write_ppm, + GS_write_tif, + GVL_delete_vol, + GVL_get_trans, + GVL_init_region, + GVL_isosurf_add, + GVL_isosurf_del, + GVL_isosurf_move_down, + GVL_isosurf_move_up, + GVL_isosurf_num_isosurfs, + GVL_isosurf_set_att_const, + GVL_isosurf_set_att_map, + GVL_isosurf_set_drawmode, + GVL_isosurf_set_drawres, + GVL_isosurf_set_flags, + GVL_isosurf_unset_att, + GVL_libinit, + GVL_set_draw_wire, + GVL_set_trans, + GVL_slice_add, + GVL_slice_del, + GVL_slice_move_down, + GVL_slice_move_up, + GVL_slice_num_slices, + GVL_slice_set_drawmode, + GVL_slice_set_drawres, + GVL_slice_set_pos, + GVL_slice_set_transp, + GVL_vol_exists, + ) + from grass.lib.raster import Rast__init_window, Rast_unset_window, Vect_read_colors + from grass.lib.raster3d import ( + GP_delete_site, + GP_get_sitename, + GP_select_surf, + GP_set_style, + GP_set_style_thematic, + GP_set_trans, + GP_set_zmode, + GP_site_exists, + GP_unselect_surf, + GP_unset_style_thematic, + ) + from grass.lib.vector import ( + GV_delete_vector, + GV_get_vectname, + GV_select_surf, + GV_set_style, + GV_set_style_thematic, + GV_set_trans, + GV_surf_is_selected, + GV_unselect_surf, + GV_unset_style_thematic, + GV_vect_exists, + ) except (ImportError, OSError, TypeError) as e: print("wxnviz.py: {}".format(e), file=sys.stderr) +import grass.script as gs from core.debug import Debug -from core.utils import autoCropImageFromFile from core.gcmd import DecodeString from core.globalvar import wxPythonPhoenix +from core.utils import autoCropImageFromFile from gui_core.wrap import Rect -import grass.script as gs log = None progress = None diff --git a/gui/wxpython/psmap/dialogs.py b/gui/wxpython/psmap/dialogs.py index 66ac66d9348..c4784525cc9 100644 --- a/gui/wxpython/psmap/dialogs.py +++ b/gui/wxpython/psmap/dialogs.py @@ -42,9 +42,8 @@ import wx import wx.lib.agw.floatspin as fs -from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin - from core import globalvar +from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin if globalvar.wxPythonPhoenix: from wx import Validator @@ -52,24 +51,25 @@ from wx import PyValidator as Validator import grass.script as gs - -from core.utils import PilImageToWxImage +from core.gcmd import GError, GMessage, RunCommand +from core.utils import PilImageToWxImage, cmp from dbmgr.vinfo import VectorDBInfo -from gui_core.gselect import Select -from core.gcmd import RunCommand, GError, GMessage from gui_core.dialogs import SymbolDialog +from gui_core.gselect import Select from gui_core.wrap import ( BitmapButton, BitmapComboBox, BitmapFromImage, Button, CheckBox, + CheckListCtrlMixin, Choice, ClientDC, ColourPickerCtrl, Dialog, DirBrowseButton, EmptyBitmap, + EmptyImage, ExpandoTextCtrl, FileBrowseButton, FloatSpin, @@ -86,11 +86,44 @@ StaticText, TextCtrl, TextEntryDialog, - EmptyImage, - CheckListCtrlMixin, ) -from psmap.utils import * -from psmap.instructions import * + +# Explicit imports from psmap.instructions +from psmap.instructions import ( + Image, + Labels, + Line, + MapFrame, + Mapinfo, + NewId, + NorthArrow, + Point, + Raster, + RasterLegend, + Rectangle, + Scalebar, + Text, + Vector, + VectorLegend, + VProperties, +) + +# Explicit imports from psmap.utils +from psmap.utils import ( + AutoAdjust, + BBoxAfterRotation, + ComputeSetRegion, + PaperMapCoordinates, + PILImage, + Rect2D, + Rect2DPP, + SetResolution, + UnitConversion, + convertRGB, + getRasterType, + havePILImage, + projInfo, +) # grass.set_raise_on_error(True) @@ -3586,7 +3619,7 @@ def _rasterLegend(self, notebook): self.Bind(wx.EVT_CHECKBOX, self.OnIsLegend, self.isRLegend) self.Bind(wx.EVT_RADIOBUTTON, self.OnDiscrete, self.discrete) self.Bind(wx.EVT_RADIOBUTTON, self.OnDiscrete, self.continuous) - ## self.Bind(wx.EVT_CHECKBOX, self.OnDefaultSize, panel.defaultSize) + # self.Bind(wx.EVT_CHECKBOX, self.OnDefaultSize, panel.defaultSize) self.Bind(wx.EVT_CHECKBOX, self.OnRange, self.range) self.rasterSelect.GetTextCtrl().Bind(wx.EVT_TEXT, self.OnRaster) diff --git a/gui/wxpython/psmap/frame.py b/gui/wxpython/psmap/frame.py index b3196d348fa..1d937483123 100644 --- a/gui/wxpython/psmap/frame.py +++ b/gui/wxpython/psmap/frame.py @@ -16,10 +16,9 @@ """ import os -import sys - import queue as Queue -from math import sin, cos, pi, sqrt +import sys +from math import cos, pi, sin, sqrt import wx @@ -29,25 +28,51 @@ import wx.lib.flatnotebook as FN import grass.script as gs - from core import globalvar -from gui_core.menu import Menu -from core.gconsole import CmdThread, EVT_CMD_DONE -from psmap.toolbars import PsMapToolbar -from core.gcmd import RunCommand, GError, GMessage +from core.gcmd import GError, GMessage, RunCommand +from core.gconsole import EVT_CMD_DONE, CmdThread from core.settings import UserSettings from core.utils import PilImageToWxImage -from gui_core.forms import GUI -from gui_core.widgets import GNotebook from gui_core.dialogs import HyperlinkDialog +from gui_core.forms import GUI from gui_core.ghelp import ShowAboutDialog -from gui_core.wrap import ClientDC, PseudoDC, Rect, StockCursor, EmptyBitmap -from psmap.menudata import PsMapMenuData +from gui_core.menu import Menu from gui_core.toolbars import ToolSwitcher - -from psmap.dialogs import * -from psmap.instructions import * -from psmap.utils import * +from gui_core.widgets import GNotebook +from gui_core.wrap import ClientDC, EmptyBitmap, PseudoDC, Rect, StockCursor + +from psmap.dialogs import ( + ImageDialog, + LabelsDialog, + LegendDialog, + MainVectorDialog, + MapDialog, + MapinfoDialog, + NorthArrowDialog, + PageSetupDialog, + PointDialog, + RasterDialog, + RectangleDialog, + ScalebarDialog, + TextDialog, +) +from psmap.instructions import InitMap, Instruction, NewId, SetResolution, PageSetup +from psmap.menudata import PsMapMenuData +from psmap.toolbars import PsMapToolbar +from psmap.utils import ( + AutoAdjust, + ComputeSetRegion, + GetMapBounds, + PaperMapCoordinates, + PILImage, + Rect2D, + Rect2DPP, + Rect2DPS, + UnitConversion, + convertRGB, + havePILImage, + projInfo, +) class PsMapFrame(wx.Frame): @@ -755,8 +780,6 @@ def OnAddRaster(self, event): if not self._checkMapFrameExists(type_id=id): return - ## dlg = RasterDialog(self, id = id, settings = self.instruction) - # dlg.ShowModal() if "mapNotebook" in self.openDialogs: self.openDialogs["mapNotebook"].notebook.ChangeSelection(1) else: @@ -775,8 +798,6 @@ def OnAddVect(self, event): if not self._checkMapFrameExists(type_id=id): return - ## dlg = MainVectorDialog(self, id = id, settings = self.instruction) - # dlg.ShowModal() if "mapNotebook" in self.openDialogs: self.openDialogs["mapNotebook"].notebook.ChangeSelection(2) else: diff --git a/gui/wxpython/psmap/instructions.py b/gui/wxpython/psmap/instructions.py index 9db64679422..845e99b2e6a 100644 --- a/gui/wxpython/psmap/instructions.py +++ b/gui/wxpython/psmap/instructions.py @@ -35,17 +35,27 @@ import os import string from math import ceil -from time import strftime, localtime +from time import localtime, strftime -import wx import grass.script as gs -from grass.script.task import cmdlist_to_tuple - +import wx from core.gcmd import GError, GMessage, GWarning from core.utils import GetCmdString from dbmgr.vinfo import VectorDBInfo +from grass.script.task import cmdlist_to_tuple from gui_core.wrap import NewId as wxNewId -from psmap.utils import * + +from psmap.utils import ( # Add any additional required names from psmap.utils here + BBoxAfterRotation, + GetMapBounds, + PaperMapCoordinates, + Rect2D, + Rect2DPP, + SetResolution, + UnitConversion, + getRasterType, + projInfo, +) def NewId(): diff --git a/gui/wxpython/vnet/dialogs.py b/gui/wxpython/vnet/dialogs.py index 39cf2e4ae84..ee553a0b741 100644 --- a/gui/wxpython/vnet/dialogs.py +++ b/gui/wxpython/vnet/dialogs.py @@ -1015,10 +1015,7 @@ def OnAnalysisChanged(self, event): attrCols = an_props["cmdParams"]["cols"] for col in attrCols.keys(): - if "inputField" in attrCols[col]: - colInptF = attrCols[col]["inputField"] - else: - colInptF = col + colInptF = attrCols[col].get("inputField", col) if col in skip: continue @@ -1245,8 +1242,6 @@ def __init__( wx.Dialog.__init__(self, parent, id, title, pos, size, style) self.vnet_mgr = vnet_mgr - - maxValue = 1e8 self.parent = parent self.settings = {} diff --git a/gui/wxpython/vnet/toolbars.py b/gui/wxpython/vnet/toolbars.py index 5f80e50cd9d..d31313091fe 100644 --- a/gui/wxpython/vnet/toolbars.py +++ b/gui/wxpython/vnet/toolbars.py @@ -235,6 +235,5 @@ def __init__(self, parent, vnet_mgr): self.Realize() def _toolbarData(self): - icons = {} return self._getToolbarData(()) diff --git a/gui/wxpython/vnet/vnet_core.py b/gui/wxpython/vnet/vnet_core.py index 8dda73b6770..f55550e65e2 100644 --- a/gui/wxpython/vnet/vnet_core.py +++ b/gui/wxpython/vnet/vnet_core.py @@ -459,11 +459,6 @@ def _vnetPathRunAn(self, analysis, output, params, flags, catPts): return False mapName, mapSet = ParseMapStr(self.tmpTurnAn.GetVectMapName()) - cmdCopy = [ - "g.copy", - "vector=%s,%s" % (params["input"], mapName), - "--overwrite", - ] cmdParams.append("input=" + self.tmpTurnAn.GetVectMapName()) ret, msg, err = RunCommand( @@ -550,11 +545,6 @@ def _runTurnsAn(self, analysis, output, params, flags, catPts): # create and run commands which goes to analysis thread mapName, mapSet = ParseMapStr(self.tmpTurnAn.GetVectMapName()) - cmdCopy = [ - "g.copy", - "vector=%s,%s" % (params["input"], mapName), - "--overwrite", - ] cmdParams.append("input=" + self.tmpTurnAn.GetVectMapName()) ret, msg, err = RunCommand( @@ -580,14 +570,6 @@ def _updateTtbByGlobalCosts(self, vectMapName, tlayer): # TODO get layer number do not use it directly intervals = self.turnsData["global"].GetData() - cmdUpdGlob = [ - "v.db.update", - "map=", - self.inputData["input"].GetValue(), - "layer=%d" % tlayer, - "column=cost", - ] - dbInfo = VectorDBInfo(vectMapName) table = dbInfo.GetTable(tlayer) driver, database = dbInfo.GetDbSettings(tlayer) @@ -762,10 +744,7 @@ def _setInputParams(self, analysis, params, flags): inParams = [] for col, v in self.data.GetAnalysisProperties()["cmdParams"]["cols"].items(): - if "inputField" in v: - colInptF = v["inputField"] - else: - colInptF = col + colInptF = v.get("inputField", col) inParams.append(col + "=" + params[colInptF]) diff --git a/gui/wxpython/vnet/vnet_data.py b/gui/wxpython/vnet/vnet_data.py index 3adc5fa64e9..f2f17b0bfb5 100644 --- a/gui/wxpython/vnet/vnet_data.py +++ b/gui/wxpython/vnet/vnet_data.py @@ -1377,7 +1377,7 @@ def SetValue(self, value, line, col): def SetUTurns(self, value): """Checked if checeBox is checed""" - useUTurns = value + self.useUTurns = value def AppendRow(self, values): self.turn_data.append(values) diff --git a/gui/wxpython/vnet/vnet_utils.py b/gui/wxpython/vnet/vnet_utils.py index f0a354691bc..e3cc11aee17 100644 --- a/gui/wxpython/vnet/vnet_utils.py +++ b/gui/wxpython/vnet/vnet_utils.py @@ -133,7 +133,6 @@ def GetNearestNodeCat(e, n, layer, tresh, vectMap): vectlib.Vect_select_lines_by_box(openedMap, byref(box), vectlib.GV_POINT, List) found = 0 - dcost = 0 Cats = POINTER(vectlib.line_cats) Cats = vectlib.Vect_new_cats_struct() diff --git a/gui/wxpython/vnet/widgets.py b/gui/wxpython/vnet/widgets.py index ed1f2aa514c..71fcd0e0739 100644 --- a/gui/wxpython/vnet/widgets.py +++ b/gui/wxpython/vnet/widgets.py @@ -217,7 +217,6 @@ def GetCellValue(self, key, colName): if colNum < 0: return None - iColEd = self.dataTypes["colEditable"] if self.selIdxs[key][colNum] != -1: return self.selIdxs[key][colNum] @@ -230,7 +229,6 @@ def GetCellSelIdx(self, key, colName): :return: -1 if column does not has values to choose """ colNum = self._getColumnNum(colName) - iColEd = self.dataTypes["colEditable"] return self.selIdxs[key][colNum] def EditCellIndex(self, index, colName, cellData): diff --git a/gui/wxpython/wxgui.py b/gui/wxpython/wxgui.py index 447821c6724..8fac39c4d5a 100644 --- a/gui/wxpython/wxgui.py +++ b/gui/wxpython/wxgui.py @@ -164,7 +164,7 @@ def main(argv=None): app = GMApp(workspaceFile) # suppress wxPython logs - q = wx.LogNull() + wx.LogNull() set_raise_on_error(True) # register GUI PID diff --git a/imagery/i.atcorr/computations.cpp b/imagery/i.atcorr/computations.cpp index 53f8664ee06..6e5a38c5b07 100644 --- a/imagery/i.atcorr/computations.cpp +++ b/imagery/i.atcorr/computations.cpp @@ -102,14 +102,14 @@ double trunca() for (i = 0; i < 83; i++) { if (rmu[i] > 0.8) break; - k = i - 1; + k = i; } int kk = 0; for (i = 0; i < 83; i++) { if (rmu[i] > 0.94) break; - kk = i - 1; + kk = i; } double aa = @@ -118,7 +118,7 @@ double trunca() double x1 = (double)(log10(sixs_trunc.pha[kk])); double x2 = (double)acos(rmu[kk]); - for (i = kk + 1; i < 83; i++) { + for (i = kk; i < 83; i++) { double a; if (fabs(rmu[i] - 1) <= 1e-08) a = x1 - aa * x2; @@ -445,9 +445,14 @@ void os(const double tamoy, const double trmoy, const double pizmoy, /* compute position of the plane layer */ double taup = tap + trp; iplane = -1; - for (int i = 0; i <= ntp; i++) + for (int i = 0; i <= ntp; i++) { if (taup >= h[i]) iplane = i; + } + if (iplane == -1) { + G_fatal_error( + _("Position of the plane layer could not be determined")); + } /* update the layer from the end to the position to update if necessary */ @@ -1006,9 +1011,14 @@ void iso(const double tamoy, const double trmoy, const double pizmoy, /* compute position of the plane layer */ double taup = tap + trp; iplane = -1; - for (int i = 0; i <= ntp; i++) + for (int i = 0; i <= ntp; i++) { if (taup >= h[i]) iplane = i; + } + if (iplane == -1) { + G_fatal_error( + _("Position of the plane layer could not be determined")); + } /* update the layer from the end to the position to update if necessary */ diff --git a/imagery/i.atcorr/create_iwave.py b/imagery/i.atcorr/create_iwave.py index a575d8089cd..21faa21ac2c 100644 --- a/imagery/i.atcorr/create_iwave.py +++ b/imagery/i.atcorr/create_iwave.py @@ -235,7 +235,7 @@ def write_cpp(bands, values, sensor, folder): while c < len(fi) - 1 and fi[c + 1] > rthresh: c += 1 max_wavelength = np.floor(li[0] * 1000 + (2.5 * c)) - print(" %s (%inm - %inm)" % (bands[b], min_wavelength, max_wavelength)) + print(" %s (%inm - %inm)" % (bands[0], min_wavelength, max_wavelength)) else: filter_f = [] diff --git a/imagery/i.atcorr/i.atcorr.html b/imagery/i.atcorr/i.atcorr.html index 7c0b772e089..779ca7c7ef7 100644 --- a/imagery/i.atcorr/i.atcorr.html +++ b/imagery/i.atcorr/i.atcorr.html @@ -4,7 +4,7 @@

DESCRIPTION

map using the 6S algorithm (Second Simulation of Satellite Signal in the Solar Spectrum). A detailed algorithm description is available at the -Land Surface +Land Surface Reflectance Science Computing Facility website.

Important: Current region settings are ignored! The @@ -507,6 +507,16 @@

F. Sensor band

Define your own spectral conditions: +Note that "wlinf" and "wlsup" refer to the limits of the wavelength range +defined by the user for a given simulation. Specifically: + +

+ diff --git a/imagery/i.fft/main.c b/imagery/i.fft/main.c index ad9a87a7890..051428b77ef 100644 --- a/imagery/i.fft/main.c +++ b/imagery/i.fft/main.c @@ -105,8 +105,9 @@ int main(int argc, char *argv[]) inputfd = Rast_open_old(Cellmap_orig, ""); if (Rast_maskfd() >= 0) - G_warning(_("Raster MASK found, consider to remove " - "(see man-page). Will continue...")); + G_warning(_("Raster mask active, consider removing it" + " and running again without it (see documentation for" + " details). This current process will now continue...")); G_get_set_window(&window); /* get the current window for later */ diff --git a/imagery/i.gensig/testsuite/test_i_gensig.py b/imagery/i.gensig/testsuite/test_i_gensig.py index d75e3da2af4..836550fd1e6 100644 --- a/imagery/i.gensig/testsuite/test_i_gensig.py +++ b/imagery/i.gensig/testsuite/test_i_gensig.py @@ -92,9 +92,13 @@ def tearDownClass(cls): """Remove the temporary region and generated data""" cls.del_temp_region() shutil.rmtree(cls.sig_dir1, ignore_errors=True) - cls.runModule("g.remove", flags="f", type="raster", name=cls.b1, quiet=True) - cls.runModule("g.remove", flags="f", type="raster", name=cls.b2, quiet=True) - cls.runModule("g.remove", flags="f", type="raster", name=cls.train, quiet=True) + cls.runModule( + "g.remove", + flags="f", + type="raster", + name=(cls.b1, cls.b2, cls.train), + quiet=True, + ) def test_creation(self): """Test creating a signature""" diff --git a/imagery/i.maxlik/testsuite/test_i_maxlik.py b/imagery/i.maxlik/testsuite/test_i_maxlik.py index e7fbf6c1a79..2245d0d6d58 100644 --- a/imagery/i.maxlik/testsuite/test_i_maxlik.py +++ b/imagery/i.maxlik/testsuite/test_i_maxlik.py @@ -163,13 +163,12 @@ def tearDownClass(cls): cls.del_temp_region() shutil.rmtree(cls.sig_dir1, ignore_errors=True) shutil.rmtree(cls.sig_dir2, ignore_errors=True) - cls.runModule("g.remove", flags="f", type="raster", name=cls.b1, quiet=True) - cls.runModule("g.remove", flags="f", type="raster", name=cls.b2, quiet=True) cls.runModule( - "g.remove", flags="f", type="raster", name=cls.v1_class, quiet=True - ) - cls.runModule( - "g.remove", flags="f", type="raster", name=cls.v2_class, quiet=True + "g.remove", + flags="f", + type="raster", + name=(cls.b1, cls.b2, cls.v1_class, cls.v2_class), + quiet=True, ) cls.runModule("g.remove", flags="f", type="group", name=cls.group, quiet=True) diff --git a/imagery/i.ortho.photo/lib/find_init.c b/imagery/i.ortho.photo/lib/find_init.c index 906203a7f90..f8cea9b9387 100644 --- a/imagery/i.ortho.photo/lib/find_init.c +++ b/imagery/i.ortho.photo/lib/find_init.c @@ -4,15 +4,19 @@ * Find the a camera initial file in the current group (if it exists) **************************************************************/ #include +#include int I_find_initial(char *group) { - char *element; - - element = (char *)G_malloc(80 * sizeof(char)); + char element[GNAME_MAX + 6]; if (group == NULL || *group == 0) return 0; - sprintf(element, "group/%s", group); + + if (snprintf(element, GNAME_MAX, "group/%s", group) >= GNAME_MAX) { + G_warning(_("Group name <%s> is too long"), group); + return 0; + } + return G_find_file(element, "INIT_EXP", G_mapset()) != NULL; } diff --git a/imagery/i.pca/i.pca.html b/imagery/i.pca/i.pca.html index 17b3f5ed163..6fee184c618 100644 --- a/imagery/i.pca/i.pca.html +++ b/imagery/i.pca/i.pca.html @@ -11,7 +11,7 @@

DESCRIPTION

principal component with the highest importance.

-The current geographic region definition and MASK settings are +The current geographic region definition and raster mask settings are respected when reading the input raster map layers. When the rescale option is used, the output files are rescaled to fit the min,max range. diff --git a/imagery/i.segment/i.segment.html b/imagery/i.segment/i.segment.html index 1e2596c1d7e..e30f21d3743 100644 --- a/imagery/i.segment/i.segment.html +++ b/imagery/i.segment/i.segment.html @@ -118,7 +118,7 @@

Mean shift

Boundary Constraints

Boundary constraints limit the adjacency of pixels and segments. Each unique value present in the bounds raster are -considered as a MASK. Thus no segments in the final segmentated map +considered as a mask. Thus, no segments in the final segmented map will cross a boundary, even if their spectral data is very similar.

Minimum Segment Size

diff --git a/imagery/i.segment/iseg.h b/imagery/i.segment/iseg.h index 1e301d2cefa..a8dfb5542c3 100644 --- a/imagery/i.segment/iseg.h +++ b/imagery/i.segment/iseg.h @@ -118,7 +118,7 @@ struct globals { /* processing flags */ FLAG *candidate_flag, - *null_flag; /*TODO, need some way to remember MASK/NULL values. Was + *null_flag; /*TODO, need some way to remember mask/NULL values. Was using -1, 0, 1 in int array. Better to use 2 FLAG structures, better readability? */ diff --git a/imagery/i.vi/testsuite/test_vi.py b/imagery/i.vi/testsuite/test_vi.py index 0c383417257..b2160f924ed 100644 --- a/imagery/i.vi/testsuite/test_vi.py +++ b/imagery/i.vi/testsuite/test_vi.py @@ -26,14 +26,12 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): - cls.runModule("g.remove", flags="f", type="raster", name="ipvi") - cls.runModule("g.remove", flags="f", type="raster", name="ndwi") - cls.runModule("g.remove", flags="f", type="raster", name="dvi") - cls.runModule("g.remove", flags="f", type="raster", name="sr") - cls.runModule("g.remove", flags="f", type="raster", name="evi") - cls.runModule("g.remove", flags="f", type="raster", name="evi2") - cls.runModule("g.remove", flags="f", type="raster", name="gari") - cls.runModule("g.remove", flags="f", type="raster", name="gemi") + cls.runModule( + "g.remove", + flags="f", + type="raster", + name="ipvi,ndwi,dvi,sr,evi,evi2,gari,gemi", + ) cls.del_temp_region() def test_vinameipvi(self): diff --git a/imagery/imageryintro.html b/imagery/imageryintro.html index 4b80951af9e..9018b4871ae 100644 --- a/imagery/imageryintro.html +++ b/imagery/imageryintro.html @@ -2,6 +2,21 @@

Image processing in general

+GRASS GIS provides a powerful suite of tools for the processing and +analysis of geospatial raster data, including satellite imagery and +aerial photography. Its image processing capabilities encompass a broad +range of preprocessing operations, such as data import, georeferencing, +radiometric calibration, and atmospheric correction. It is particularly +suited for handling Earth observation data, enabling the analysis of +multispectral and temporal datasets. GRASS GIS supports advanced +functionalities such as image classification, sensor fusion, and point +cloud statistics. The calculation of vegetation indices further enables +analyses of environmental, agricultural, and land cover dynamics. An +extensive collection of +addons +further enhances its image processing capabilities, particularly in the +context of Earth observation and remote sensing applications. + Digital numbers and physical values (reflection/radiance-at-sensor):

Satellite imagery is commonly stored in Digital Numbers (DN) for diff --git a/include/grass/defs/raster.h b/include/grass/defs/raster.h index c2d26ccdccc..7f358562c72 100644 --- a/include/grass/defs/raster.h +++ b/include/grass/defs/raster.h @@ -392,6 +392,7 @@ int Rast_option_to_interp_type(const struct Option *); /* mask_info.c */ char *Rast_mask_info(void); +bool Rast_mask_status(char *, char *, bool *, char *, char *); int Rast__mask_info(char *, char *); bool Rast_mask_is_present(void); diff --git a/lib/imagery/testsuite/test_imagery_sigfile.py b/lib/imagery/testsuite/test_imagery_sigfile.py index 8d0e288561d..731ef047063 100644 --- a/lib/imagery/testsuite/test_imagery_sigfile.py +++ b/lib/imagery/testsuite/test_imagery_sigfile.py @@ -349,9 +349,12 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): cls.del_temp_region() - cls.runModule("g.remove", flags="f", type="raster", name=cls.map1) - cls.runModule("g.remove", flags="f", type="raster", name=cls.map2) - cls.runModule("g.remove", flags="f", type="raster", name=cls.map3) + cls.runModule( + "g.remove", + flags="f", + type="raster", + name=(cls.map1, cls.map2, cls.map3), + ) def test_symmetric_complete_difference(self): # Prepare imagery group reference struct diff --git a/lib/imagery/testsuite/test_imagery_sigsetfile.py b/lib/imagery/testsuite/test_imagery_sigsetfile.py index d526e920885..80be7c0d3a5 100644 --- a/lib/imagery/testsuite/test_imagery_sigsetfile.py +++ b/lib/imagery/testsuite/test_imagery_sigsetfile.py @@ -242,9 +242,12 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): cls.del_temp_region() - cls.runModule("g.remove", flags="f", type="raster", name=cls.map1) - cls.runModule("g.remove", flags="f", type="raster", name=cls.map2) - cls.runModule("g.remove", flags="f", type="raster", name=cls.map3) + cls.runModule( + "g.remove", + flags="f", + type="raster", + name=(cls.map1, cls.map2, cls.map3), + ) def test_symmetric_complete_difference(self): # Prepare imagery group reference struct diff --git a/lib/init/grass.py b/lib/init/grass.py index f489ad1fe53..505d48dadae 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -125,7 +125,7 @@ def clean_env(): write_gisrc(env_new, gisrc) -def is_debug(): +def is_debug() -> bool: """Returns True if we are in debug mode For debug messages use ``debug()``. @@ -133,13 +133,8 @@ def is_debug(): global _DEBUG if _DEBUG is not None: return _DEBUG - _DEBUG = os.getenv("GRASS_DEBUG") # translate to bool (no or empty variable means false) - if _DEBUG: - _DEBUG = True - else: - _DEBUG = False - return _DEBUG + return bool(os.getenv("GRASS_DEBUG")) def debug(msg): @@ -553,10 +548,7 @@ def write_gisrc(kv, filename, append=False): def add_mapset_to_gisrc(gisrc, grassdb, location, mapset): - if os.access(gisrc, os.R_OK): - kv = read_gisrc(gisrc) - else: - kv = {} + kv = read_gisrc(gisrc) if os.access(gisrc, os.R_OK) else {} kv["GISDBASE"] = grassdb kv["LOCATION_NAME"] = location kv["MAPSET"] = mapset @@ -564,10 +556,7 @@ def add_mapset_to_gisrc(gisrc, grassdb, location, mapset): def add_last_mapset_to_gisrc(gisrc, last_mapset_path): - if os.access(gisrc, os.R_OK): - kv = read_gisrc(gisrc) - else: - kv = {} + kv = read_gisrc(gisrc) if os.access(gisrc, os.R_OK) else {} kv["LAST_MAPSET_PATH"] = last_mapset_path write_gisrc(kv, gisrc) @@ -1333,11 +1322,8 @@ def get_shell(): sh = os.path.basename(sh) else: # If SHELL is not set, see if there is Bash and use it. - if shutil.which("bash"): - sh = "bash" - else: - # Fallback to sh if there is no Bash on path. - sh = "sh" + # Fallback to sh if there is no Bash on path. + sh = "bash" if shutil.which("bash") else "sh" # Ensure the variable is set. os.environ["SHELL"] = sh @@ -1651,11 +1637,8 @@ def sh_like_startup(location, location_name, grass_env_file, sh): else: f.write("test -r ~/.alias && . ~/.alias\n") - if os.getenv("ISISROOT"): - # GRASS GIS and ISIS blend - grass_name = "ISIS-GRASS" - else: - grass_name = "GRASS" + # GRASS GIS and ISIS blend + grass_name = "GRASS" if not os.getenv("ISISROOT") else "ISIS-GRASS" if sh == "zsh": f.write("setopt PROMPT_SUBST\n") @@ -1667,8 +1650,8 @@ def sh_like_startup(location, location_name, grass_env_file, sh): ) ) + mask2d_test = "r.mask.status -t" # TODO: have a function and/or module to test this - mask2d_test = 'test -f "$MAPSET_PATH/cell/MASK"' mask3d_test = 'test -d "$MAPSET_PATH/grid3/RASTER3D_MASK"' specific_addition = "" diff --git a/lib/init/helptext.html b/lib/init/helptext.html index d988b6b0376..4a14dd52df1 100644 --- a/lib/init/helptext.html +++ b/lib/init/helptext.html @@ -62,7 +62,7 @@

GRASS started in the default project, now what?

Creating a New project with the Project Wizard

If you know the CRS of your data or study area, -you can fill EPSG code +you can fill EPSG code or description and Project Wizard finds appropriate CRS from a predefined list of projections. @@ -97,4 +97,6 @@

See also

- List of EPSG codes (Database of worldwide coordinate systems) + List of EPSG codes (Database of worldwide coordinate systems), + CRS Explorer - PROJ codes, and + EPSG Geodetic Parameter Dataset diff --git a/lib/ogsf/gk.c b/lib/ogsf/gk.c index 3390598960a..b4fea635cc4 100644 --- a/lib/ogsf/gk.c +++ b/lib/ogsf/gk.c @@ -172,8 +172,8 @@ void gk_follow_frames(Viewnode *view, int numsteps, Keylist *keys, int step, GS_get_from(tmp); G_debug(3, "gk_follow_frames():"); - G_debug(3, " MASK: %lx", mask); - G_debug(3, " FROM: %f %f %f", tmp[X], tmp[Y], tmp[Z]); + G_debug(3, " mask: %lx", mask); + G_debug(3, " from: %f %f %f", tmp[X], tmp[Y], tmp[Z]); /* ACS 1 line: was GS_get_focus(tmp); with this kanimator works also for flythrough navigation diff --git a/lib/ogsf/gvd.c b/lib/ogsf/gvd.c index 6a6176526a2..84867123c5e 100644 --- a/lib/ogsf/gvd.c +++ b/lib/ogsf/gvd.c @@ -201,7 +201,7 @@ int gvd_vect(geovect *gv, geosurf *gs, int do_fast) } gsd_endline(); } - /* need to handle MASK! */ + /* need to handle mask! */ else if (src == CONST_ATT) { /* for now - but later, do seg intersect maskedge */ if (gs_point_is_masked(gs, bgn) || diff --git a/lib/raster/mask_info.c b/lib/raster/mask_info.c index 1a11da972f8..317bab75b63 100644 --- a/lib/raster/mask_info.c +++ b/lib/raster/mask_info.c @@ -1,30 +1,17 @@ -/* - ************************************************************* - * char * Rast_mask_info () - * - * returns a printable text of mask information - * - ************************************************************ - * Rast__mask_info (name, mapset) - * - * char name[GNAME_MAX], mapset[GMAPSET_MAX]; - * - * function: - * determine the status off the automatic masking - * and the name of the cell file which forms the mask +/** + * \file lib/raster/mask_info.c * - * (the mask file is actually MASK in the current mapset, - * but is usually a reclassed cell file, and the reclass - * name and mapset are returned) + * \brief Raster Library - Get mask information * - * returns: - * -1 no masking (name, mapset undefined) - * name, mapset are undefined + * (C) 1999-2024 by Vaclav Petras and the GRASS Development Team * - * 1 mask file present, masking on - * name, mapset hold mask file name, mapset + * This program is free software under the GNU General Public + * License (>=v2). Read the file COPYING that comes with GRASS + * for details. * - ***************************************************************/ + * \author CERL + * \author Vaclav Petras, NC State University, Center for Geospatial Analytics + */ #include @@ -32,6 +19,15 @@ #include #include +/** + * @brief Get a printable text with information about raster mask + * + * Determines if 2D raster mask is present and returns textual information about + * the mask suitable for end-user display. The resulting text is translated. + * Caller is responsible for freeing the memory of the returned string. + * + * @return New string with textual information + */ char *Rast_mask_info(void) { char text[GNAME_MAX + GMAPSET_MAX + 16]; @@ -53,16 +49,88 @@ char *Rast_mask_info(void) return G_store(text); } +/** + * @brief Get raster mask status information + * + * _is_mask_reclass_ is a pointer to a bool variable which + * will be set to true if mask raster is a reclass and false otherwise. + * + * If you are not interested in the underlying reclassified raster map, + * pass NULL pointers for the three reclass parameters: + * + * ``` + * Rast_mask_status(name, mapset, NULL, NULL, NULL); + * ``` + * + * @param[out] name Name of the raster map used as mask + * @param[out] mapset Name of the mapset the raster is in + * @param[out] is_mask_reclass Will be set to true if mask raster is a reclass + * @param[out] reclass_name Name of the underlying reclassified raster map + * @param[out] reclass_mapset Name of the mapset the reclassified raster is in + * + * @return true if mask is present, false otherwise + */ +bool Rast_mask_status(char *name, char *mapset, bool *is_mask_reclass, + char *reclass_name, char *reclass_mapset) +{ + int present = Rast__mask_info(name, mapset); + + if (is_mask_reclass && reclass_name && reclass_mapset) { + if (present) { + *is_mask_reclass = Rast_is_reclass("MASK", G_mapset(), reclass_name, + reclass_mapset) > 0; + if (*is_mask_reclass) { + // The original mask values were overwritten in the initial + // info call. Put back the original values, so that we can + // report them to the caller. + strcpy(name, "MASK"); + strcpy(mapset, G_mapset()); + } + } + else { + *is_mask_reclass = false; + } + } + + if (present == 1) + return true; + else + return false; +} + +/** + * @brief Get information about the current mask + * + * Determines the status of the automatic masking and the name of the 2D + * raster which forms the mask. Typically, mask is raster called MASK in the + * current mapset, but when used with r.mask, it is usually a reclassed + * raster, and so when a MASK raster is present and it is a reclass raster, + * the name and mapset of the underlying reclassed raster are returned. + * + * The name and mapset is written to the parameter which need to be defined + * with a sufficient size, least as `char name[GNAME_MAX], mapset[GMAPSET_MAX]`. + * + * When the masking is not active, -1 is returned and name and mapset are + * undefined. When the masking is active, 1 is returned and name and mapset + * will hold the name and mapset of the underlying raster. + * + * @param[out] name Name of the raster map used as mask + * @param[out] mapset Name of the map's mapset + * + * @return 1 if mask is present, -1 otherwise + */ int Rast__mask_info(char *name, char *mapset) { char rname[GNAME_MAX], rmapset[GMAPSET_MAX]; - strcpy(name, "MASK"); - strcpy(mapset, G_mapset()); + strcpy(rname, "MASK"); + (void)G_strlcpy(rmapset, G_mapset(), GMAPSET_MAX); - if (!G_find_raster(name, mapset)) + if (!G_find_raster(rname, rmapset)) return -1; + strcpy(name, rname); + strcpy(mapset, rmapset); if (Rast_is_reclass(name, mapset, rname, rmapset) > 0) { strcpy(name, rname); strcpy(mapset, rmapset); diff --git a/lib/vector/Vlib/color_read.c b/lib/vector/Vlib/color_read.c index 0a023a89a7d..4e0b3b04837 100644 --- a/lib/vector/Vlib/color_read.c +++ b/lib/vector/Vlib/color_read.c @@ -49,7 +49,10 @@ int Vect_read_colors(const char *name, const char *mapset, if (colors) Rast_init_colors(colors); - strcpy(xname, name); + if (G_strlcpy(xname, name, sizeof(xname)) >= sizeof(xname)) { + G_warning(_("Vector map name <%s> is too long"), name); + return -1; + } mapset = G_find_vector(xname, mapset); if (!mapset) return -1; @@ -58,12 +61,12 @@ int Vect_read_colors(const char *name, const char *mapset, if (strcmp(mapset, G_mapset()) == 0) { /* look for the regular color table */ - sprintf(buf, "%s/%s", GV_DIRECTORY, name); + (void)snprintf(buf, sizeof(buf), "%s/%s", GV_DIRECTORY, name); ret = Rast__read_colors(buf, GV_COLR_ELEMENT, mapset, colors); } else { /* look for secondary color table in current mapset */ - sprintf(buf, "%s/%s", GV_COLR2_DIRECTORY, mapset); + (void)snprintf(buf, sizeof(buf), "%s/%s", GV_COLR2_DIRECTORY, mapset); ret = Rast__read_colors(buf, name, G_mapset(), colors); } if (ret == -2) diff --git a/lib/vector/Vlib/geos.c b/lib/vector/Vlib/geos.c index 719e0fdd270..2c68a0a6fba 100644 --- a/lib/vector/Vlib/geos.c +++ b/lib/vector/Vlib/geos.c @@ -290,7 +290,7 @@ GEOSCoordSequence *V1_read_line_geos(struct Map_info *Map, long offset, long size; double *x, *y, *z; - GEOSCoordSequence *pseq; + GEOSCoordSequence *pseq = NULL; G_debug(3, "V1_read_line_geos(): offset = %ld", offset); @@ -353,8 +353,6 @@ GEOSCoordSequence *V1_read_line_geos(struct Map_info *Map, long offset, G_debug(3, " n_points = %d dim = %d", n_points, (Map->head.with_z) ? 3 : 2); - pseq = GEOSCoordSeq_create(n_points, (Map->head.with_z) ? 3 : 2); - x = (double *)G_malloc(n_points * sizeof(double)); y = (double *)G_malloc(n_points * sizeof(double)); if (Map->head.with_z) @@ -362,17 +360,22 @@ GEOSCoordSequence *V1_read_line_geos(struct Map_info *Map, long offset, else z = NULL; - if (0 >= dig__fread_port_D(x, n_points, &(Map->dig_fp))) - return NULL; /* end of file */ + if (0 >= dig__fread_port_D(x, n_points, &(Map->dig_fp))) { + goto free_return; /* end of file */ + } - if (0 >= dig__fread_port_D(y, n_points, &(Map->dig_fp))) - return NULL; /* end of file */ + if (0 >= dig__fread_port_D(y, n_points, &(Map->dig_fp))) { + goto free_return; /* end of file */ + } if (Map->head.with_z) { - if (0 >= dig__fread_port_D(z, n_points, &(Map->dig_fp))) - return NULL; /* end of file */ + if (0 >= dig__fread_port_D(z, n_points, &(Map->dig_fp))) { + goto free_return; /* end of file */ + } } + pseq = GEOSCoordSeq_create(n_points, (Map->head.with_z) ? 3 : 2); + for (i = 0; i < n_points; i++) { GEOSCoordSeq_setX(pseq, i, x[i]); GEOSCoordSeq_setY(pseq, i, y[i]); @@ -382,6 +385,7 @@ GEOSCoordSequence *V1_read_line_geos(struct Map_info *Map, long offset, G_debug(3, " off = %ld", (long)dig_ftell(&(Map->dig_fp))); +free_return: G_free((void *)x); G_free((void *)y); if (z) diff --git a/lib/vector/Vlib/open.c b/lib/vector/Vlib/open.c index 501b31eb4cb..0008156c34a 100644 --- a/lib/vector/Vlib/open.c +++ b/lib/vector/Vlib/open.c @@ -572,6 +572,7 @@ int Vect__open_old(struct Map_info *Map, const char *name, const char *mapset, if (access(file_path, F_OK) == 0) /* fidx file exists? */ unlink(file_path); } + Map->support_updated = TRUE; } return level; diff --git a/man/build_html.py b/man/build_html.py index 0ef860d42f3..a8cc4c057a5 100644 --- a/man/build_html.py +++ b/man/build_html.py @@ -464,10 +464,7 @@ def write_html_cmd_overview(f): def write_html_footer(f, index_url, year=None): - if year is None: - cur_year = default_year - else: - cur_year = year + cur_year = default_year if year is None else year f.write( footer_tmpl.substitute( grass_version=grass_version, index_url=index_url, year=cur_year diff --git a/ps/ps.map/do_geogrid.c b/ps/ps.map/do_geogrid.c index 0fac5f80cac..c22ef86379c 100644 --- a/ps/ps.map/do_geogrid.c +++ b/ps/ps.map/do_geogrid.c @@ -300,6 +300,7 @@ void get_ll_bounds(double *w, double *e, double *s, double *n) double ew, ns; int first; + east = west = north = south = 0.0; e1 = PS.w.east; w1 = PS.w.west; n1 = PS.w.north; diff --git a/ps/ps.map/r_instructions.c b/ps/ps.map/r_instructions.c index 0e2c012fd92..87a2e4bbfcf 100644 --- a/ps/ps.map/r_instructions.c +++ b/ps/ps.map/r_instructions.c @@ -29,7 +29,7 @@ static char *help[] = { "read unix-file eps Encapsulated PostScript file", "border [y|n] mapinfo map information", "window region definition region region definition", - "maskcolor MASK color", + "maskcolor mask color", "rectangle east north east north", "scale 1:#|# inches|# panels|1 inch = # miles", "outline map composition outline", diff --git a/pyproject.toml b/pyproject.toml index 6147e2db897..4b7856bab3b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,9 +34,7 @@ select = [ "COM", # flake8-commas (COM) "D", # pydocstyle (D) "DTZ", # flake8-datetimez (DTZ) - "E4", # pycodestyle (E, W) - "E7", # pycodestyle (E, W) - "E9", # pycodestyle (E, W) + "E", # pycodestyle (E, W) "F", # Pyflakes (F) "FA", # flake8-future-annotations (FA) "FBT", # flake8-boolean-trap (FBT) @@ -48,6 +46,7 @@ select = [ "INT", # flake8-gettext (INT) "ISC", # flake8-implicit-str-concat (ISC) "LOG", # flake8-logging (LOG) + "N", # pep8-naming (N) "NPY", # NumPy-specific rules (NPY) "PERF", # Perflint (PERF) "PGH", # pygrep-hooks (PGH) @@ -126,8 +125,8 @@ ignore = [ "DTZ006", # call-datetime-fromtimestamp "DTZ007", # call-datetime-strptime-without-zone "DTZ011", # call-date-today - "E401", # multiple-imports-on-one-line "E402", # module-import-not-at-top-of-file + "E501", # line-too-long "E721", # type-comparison "E722", # bare-except "E731", # lambda-assignment @@ -135,7 +134,6 @@ ignore = [ "F401", # unused-import "F403", # undefined-local-with-import-star "F405", # undefined-local-with-import-star-usage - "F601", # multi-value-repeated-key-literal "F811", # redefined-while-unused "F821", # undefined-name "F822", # undefined-export @@ -145,6 +143,16 @@ ignore = [ "FBT003", # boolean-positional-value-in-call "I001", # unsorted-imports "ISC003", # explicit-string-concatenation + "N801", # invalid-class-name + "N802", # invalid-function-name + "N803", # invalid-argument-name + "N806", # non-lowercase-variable-in-function + "N812", # lowercase-imported-as-non-lowercase + "N814", # camelcase-imported-as-constant + "N815", # mixed-case-variable-in-class-scope + "N816", # mixed-case-variable-in-global-scope + "N818", # error-suffix-on-exception-name + "N999", # invalid-module-name "PERF203", # try-except-in-loop "PERF401", # manual-list-comprehension "PERF402", # manual-list-copy @@ -176,6 +184,7 @@ ignore = [ "PLW1641", # eq-without-hash "PLW2901", # redefined-loop-name "PLW3201", # bad-dunder-method-name + "PT004", # pytest-missing-fixture-name-underscore # deprecated, so doesn't appear with --preview "PTH100", # os-path-abspath "PTH101", # os-chmod "PTH102", # os-mkdir @@ -238,7 +247,6 @@ ignore = [ "SIM116", # if-else-block-instead-of-dict-lookup "SIM118", # in-dict-keys "SIM223", # expr-and-false - "SIM401", # if-else-block-instead-of-dict-get "SLF001", # private-member-access "TRY002", # raise-vanilla-class "TRY003", # raise-vanilla-args @@ -356,7 +364,7 @@ ignore = [ "temporal/t.rast.algebra/testsu*/*_algebra_arithmetic.py" = ["FLY002"] "temporal/t.rast.colors/t.rast.colors.py" = ["SIM115"] "temporal/t.rast.series/t.rast.series.py" = ["SIM115"] -"temporal/t.rast.what/t.rast.what.py" = ["SIM115"] +"temporal/t.rast.what/t.rast.what.py" = ["E265", "E266", "SIM115"] "temporal/t.register/testsu*/*_raster_different_local.py" = ["FLY002"] "temporal/t.register/testsu*/*_raster_mapmetadata.py" = ["FLY002"] "temporal/t.register/testsuite/test_t_register_raster.py" = ["FLY002"] diff --git a/python/grass/app/runtime.py b/python/grass/app/runtime.py index 3f3805c9dfd..27f35760b8f 100644 --- a/python/grass/app/runtime.py +++ b/python/grass/app/runtime.py @@ -79,10 +79,7 @@ def append_left_addon_paths(paths, config_dir, env): # addons (base) addon_base = env.get("GRASS_ADDON_BASE") if not addon_base: - if MACOS: - name = "Addons" - else: - name = "addons" + name = "addons" if not MACOS else "Addons" addon_base = os.path.join(config_dir, name) env["GRASS_ADDON_BASE"] = addon_base @@ -174,10 +171,7 @@ def set_python_path_variable(install_path, env): """Set PYTHONPATH to find GRASS Python package in subprocesses""" path = env.get("PYTHONPATH") etcpy = os.path.join(install_path, "etc", "python") - if path: - path = etcpy + os.pathsep + path - else: - path = etcpy + path = etcpy + os.pathsep + path if path else etcpy env["PYTHONPATH"] = path diff --git a/python/grass/benchmark/app.py b/python/grass/benchmark/app.py index 09cdf3f9481..835fc0d544b 100644 --- a/python/grass/benchmark/app.py +++ b/python/grass/benchmark/app.py @@ -47,11 +47,7 @@ def join_results_cli(args): def select_only(result): return result.label == args.only - if args.only: - select_function = select_only - else: - select_function = None - + select_function = select_only if args.only else None results = join_results_from_files( source_filenames=args.results, prefixes=args.prefixes, diff --git a/python/grass/benchmark/plots.py b/python/grass/benchmark/plots.py index 9483fda9116..24afcbcfbae 100644 --- a/python/grass/benchmark/plots.py +++ b/python/grass/benchmark/plots.py @@ -25,10 +25,7 @@ def get_pyplot(to_file): """ import matplotlib as mpl # pylint: disable=import-outside-toplevel - if to_file: - backend = "agg" - else: - backend = None + backend = "agg" if to_file else None if backend: mpl.use(backend) @@ -124,10 +121,7 @@ def num_cells_plot(results, filename=None, title=None, show_resolution=False): x_ticks = set() for result in results: - if show_resolution: - x = result.resolutions - else: - x = result.cells + x = result.resolutions if show_resolution else result.cells x_ticks.update(x) plt.plot(x, result.times, label=result.label) if hasattr(result, "all_times"): diff --git a/python/grass/exceptions/__init__.py b/python/grass/exceptions/__init__.py index b22d0a45c1d..deec48ffd96 100644 --- a/python/grass/exceptions/__init__.py +++ b/python/grass/exceptions/__init__.py @@ -71,13 +71,9 @@ def __init__(self, module, code, returncode, errors=None): """ # CalledProcessError has undocumented constructor super().__init__(returncode, module) - if not module or module in code: - # No need to include module name if it is directly in code - # of if it is not set. - executed = code - else: - # Make sure module name is there if provided and not in code. - executed = f"{module} {code}" + # No need to include module name if it is directly in code of if it is not set. + # Otherwise, make sure module name is there if provided and not in code. + executed = code if not module or module in code else f"{module} {code}" if errors: # We assume actual errors, e.g., captured stderr. err = _("See the following errors:\n{errors}").format(errors=errors) diff --git a/python/grass/gunittest/case.py b/python/grass/gunittest/case.py index 1277ee542b5..4e0ee3ab24d 100644 --- a/python/grass/gunittest/case.py +++ b/python/grass/gunittest/case.py @@ -691,10 +691,7 @@ def assertFileMd5(self, filename, md5, text=False, msg=None): at the end of file (as for example, Git or PEP8 requires). """ self.assertFileExists(filename, msg=msg) - if text: - actual = text_file_md5(filename) - else: - actual = file_md5(filename) + actual = text_file_md5(filename) if text else file_md5(filename) if actual != md5: standardMsg = ( "File <{name}> does not have the right MD5 sum.\n" @@ -1339,12 +1336,9 @@ def runModule(cls, module, expecting_stdout=False, **kwargs): errors = " The errors are:\n" + module.outputs.stderr else: errors = " There were no error messages." - if module.outputs.stdout: - # this is not appropriate for translation but we don't want - # and don't need testing to be translated - got = "only whitespace." - else: - got = "nothing." + # This is not appropriate for translation but we don't want + # and don't need testing to be translated + got = "only whitespace." if module.outputs.stdout else "nothing." raise RuntimeError( "Module call " + module.get_python() diff --git a/python/grass/gunittest/invoker.py b/python/grass/gunittest/invoker.py index f07e8dd5a84..29b62830e25 100644 --- a/python/grass/gunittest/invoker.py +++ b/python/grass/gunittest/invoker.py @@ -54,10 +54,7 @@ def update_keyval_file(filename, module, returncode): keyval["name"] = module.name keyval["tested_dir"] = module.tested_dir if "status" not in keyval.keys(): - if returncode is None or returncode: - status = "failed" - else: - status = "passed" + status = "failed" if returncode is None or returncode else "passed" keyval["status"] = status keyval["returncode"] = returncode keyval["test_file_authors"] = test_file_authors @@ -244,7 +241,7 @@ def try_decode(data, encodings): Path(stdout_path).write_text(stdout) with open(stderr_path, "w") as stderr_file: - if type(stderr) == "bytes": + if isinstance(stderr, bytes): stderr_file.write(decode(stderr)) elif isinstance(stderr, str): stderr_file.write(stderr) diff --git a/python/grass/gunittest/loader.py b/python/grass/gunittest/loader.py index f01467ef1ba..50fa368f62a 100644 --- a/python/grass/gunittest/loader.py +++ b/python/grass/gunittest/loader.py @@ -235,4 +235,18 @@ def discover(self, start_dir, pattern="test*.py", top_level_dir=None): if __name__ == "__main__": - GrassTestLoader().discover() + for expression in [r".*\.py$", r".*\.sh$"]: + modules = discover_modules( + start_dir=".", + grass_location="all", + file_regexp=expression, + skip_dirs=GrassTestLoader.skip_dirs, + testsuite_dir=GrassTestLoader.testsuite_dir, + all_locations_value=GrassTestLoader.all_tests_value, + universal_location_value=GrassTestLoader.universal_tests_value, + import_modules=False, + exclude=None, + ) + print("Expression:", expression) + print(len(modules)) + print([module.file_path for module in modules]) diff --git a/python/grass/gunittest/multireport.py b/python/grass/gunittest/multireport.py index c5f36a6e0db..5a0350f1786 100644 --- a/python/grass/gunittest/multireport.py +++ b/python/grass/gunittest/multireport.py @@ -119,10 +119,7 @@ def median(values): smedian = median(successes) smax = max(successes) - if successes[-1] < smedian: - color = "r" - else: - color = "g" + color = "r" if successes[-1] < smedian else "g" # another possibility is to color according to the gradient, ideally # on the whole curve but that's much more complicated diff --git a/python/grass/gunittest/reporters.py b/python/grass/gunittest/reporters.py index 09f371dfffc..57b0a8fc643 100644 --- a/python/grass/gunittest/reporters.py +++ b/python/grass/gunittest/reporters.py @@ -234,11 +234,8 @@ def get_svn_path_authors(path, from_date=None): :returns: a set of authors """ - if from_date is None: - # this is the SVN default for local copies - revision_range = "BASE:1" - else: - revision_range = "BASE:{%s}" % from_date + # "BASE:1" is the SVN default for local copies + revision_range = "BASE:1" if from_date is None else "BASE:{%s}" % from_date try: # TODO: allow also usage of --limit p = subprocess.Popen( @@ -487,10 +484,7 @@ def tail(filename, n): def returncode_to_html_text(returncode, timed_out=None): if returncode: - if timed_out is not None: - extra = f" (timeout >{timed_out}s)" - else: - extra = "" + extra = f" (timeout >{timed_out}s)" if timed_out is not None else "" return f'FAILED{extra}' # alternatives: SUCCEEDED, passed, OK return 'succeeded' @@ -857,10 +851,7 @@ def finish(self): # this shoul be moved to some additional meta passed in constructor svn_info = get_svn_info() - if not svn_info: - svn_revision = "" - else: - svn_revision = svn_info["revision"] + svn_revision = "" if not svn_info else svn_info["revision"] summary = {} summary["files_total"] = self.test_files @@ -1025,10 +1016,7 @@ def end_file_test( num_failed = test_summary.get("failures", 0) num_failed += test_summary.get("errors", 0) if num_failed: - if num_failed > 1: - text = " ({f} tests failed)" - else: - text = " ({f} test failed)" + text = " ({f} tests failed)" if num_failed > 1 else " ({f} test failed)" self._stream.write(text.format(f=num_failed)) self._stream.write("\n") # TODO: here we lost the possibility to include also file name diff --git a/python/grass/imaging/images2ims.py b/python/grass/imaging/images2ims.py index a5cee9b49de..d86a2247952 100644 --- a/python/grass/imaging/images2ims.py +++ b/python/grass/imaging/images2ims.py @@ -32,6 +32,7 @@ import os from operator import itemgetter +from string import digits try: import numpy as np @@ -117,7 +118,7 @@ def _getSequenceNumber(filename, part1, part2): # Get all numeric chars seq2 = "" for c in seq: - if c in "0123456789": + if c in digits: seq2 += c else: break diff --git a/python/grass/jupyter/map3d.py b/python/grass/jupyter/map3d.py index dd373f315a7..99253251406 100644 --- a/python/grass/jupyter/map3d.py +++ b/python/grass/jupyter/map3d.py @@ -210,10 +210,7 @@ def render(self, **kwargs): with Display( size=(self._width, self._height), **additional_kwargs ) as display: - if has_env_copy: - env = display.env() - else: - env = os.environ.copy() + env = display.env() if has_env_copy else os.environ.copy() self._region_manager.set_region_from_command(env=env, **kwargs) self.overlay.region_manager.set_region_from_env(env) gs.run_command(module, env=env, **kwargs) diff --git a/python/grass/pydispatch/signal.py b/python/grass/pydispatch/signal.py index 1e968e99dca..51808e01ee3 100644 --- a/python/grass/pydispatch/signal.py +++ b/python/grass/pydispatch/signal.py @@ -7,7 +7,7 @@ from grass.pydispatch import dispatcher -def _islambda(function): +def _islambda(function) -> bool: """ Tests if object is a lambda function. @@ -146,10 +146,7 @@ class connects to the signal:: will print """ if weak is None: - if _islambda(handler): - weak = False - else: - weak = True + weak = not _islambda(handler) dispatcher.connect(receiver=handler, signal=self, weak=weak) def disconnect(self, handler, weak=True): diff --git a/python/grass/pygrass/modules/interface/parameter.py b/python/grass/pygrass/modules/interface/parameter.py index 3b58ea2b372..1ec5466bc74 100644 --- a/python/grass/pygrass/modules/interface/parameter.py +++ b/python/grass/pygrass/modules/interface/parameter.py @@ -337,10 +337,7 @@ def __doc__(self): .. """ if hasattr(self, "values"): - if self.isrange: - vals = self.isrange - else: - vals = ", ".join([repr(val) for val in self.values]) + vals = self.isrange or ", ".join([repr(val) for val in self.values]) else: vals = False if self.keydescvalues: diff --git a/python/grass/pygrass/modules/interface/testsuite/test_modules.py b/python/grass/pygrass/modules/interface/testsuite/test_modules.py index 3318154f87d..ee005498692 100644 --- a/python/grass/pygrass/modules/interface/testsuite/test_modules.py +++ b/python/grass/pygrass/modules/interface/testsuite/test_modules.py @@ -21,7 +21,7 @@ class ModulesMeta(type): - def __new__(mcs, name, bases, dict): + def __new__(cls, name, bases, dict): def gen_test(cmd): def test(self): Module(cmd) @@ -36,7 +36,7 @@ def test(self): for cmd in cmds: test_name = "test__%s" % cmd.replace(".", "_") dict[test_name] = gen_test(cmd) - return type.__new__(mcs, name, bases, dict) + return type.__new__(cls, name, bases, dict) class TestModules(TestCase, metaclass=ModulesMeta): diff --git a/python/grass/pygrass/vector/__init__.py b/python/grass/pygrass/vector/__init__.py index 461050807b0..6723b2e44b3 100644 --- a/python/grass/pygrass/vector/__init__.py +++ b/python/grass/pygrass/vector/__init__.py @@ -859,10 +859,7 @@ def features_to_wkb_list(self, bbox=None, feature_type="point", field=1): ok = libvect.Vect_cat_get( ctypes.byref(line_c), field, ctypes.byref(cat) ) - if ok < 1: - pcat = None - else: - pcat = cat.value + pcat = None if ok < 1 else cat.value wkb_list.append((f_id, pcat, ctypes.string_at(barray, size.value))) libgis.G_free(barray) diff --git a/python/grass/pygrass/vector/abstract.py b/python/grass/pygrass/vector/abstract.py index 6700dca8e4c..79307f0d354 100644 --- a/python/grass/pygrass/vector/abstract.py +++ b/python/grass/pygrass/vector/abstract.py @@ -457,14 +457,14 @@ def close(self, build=False): if hasattr(self, "table") and self.table is not None: self.table.conn.close() if self.is_open(): - if libvect.Vect_close(self.c_mapinfo) != 0: - str_err = "Error when trying to close the map with Vect_close" - raise GrassError(str_err) if ( self.c_mapinfo.contents.mode in {libvect.GV_MODE_RW, libvect.GV_MODE_WRITE} ) and build: self.build() + if libvect.Vect_close(self.c_mapinfo) != 0: + str_err = "Error when trying to close the map with Vect_close" + raise GrassError(str_err) def remove(self): """Remove vector map""" @@ -474,16 +474,11 @@ def remove(self): def build(self): """Close the vector map and build vector Topology""" - self.close() - libvect.Vect_set_open_level(1) - if libvect.Vect_open_old2(self.c_mapinfo, self.name, self.mapset, "0") != 1: - str_err = "Error when trying to open the vector map." - raise GrassError(str_err) - # Vect_build returns 1 on success and 0 on error (bool approach) - if libvect.Vect_build(self.c_mapinfo) != 1: - str_err = "Error when trying build topology with Vect_build" - raise GrassError(str_err) - libvect.Vect_close(self.c_mapinfo) + if self.is_open(): + # Vect_build returns 1 on success and 0 on error (bool approach) + if libvect.Vect_build(self.c_mapinfo) != 1: + str_err = "Error when trying build topology with Vect_build" + raise GrassError(str_err) if __name__ == "__main__": diff --git a/python/grass/pygrass/vector/geometry.py b/python/grass/pygrass/vector/geometry.py index 10ecb1d2d13..b734161d1ea 100644 --- a/python/grass/pygrass/vector/geometry.py +++ b/python/grass/pygrass/vector/geometry.py @@ -810,10 +810,7 @@ def extend(self, line, forward=True): """ # set direction - if forward: - direction = libvect.GV_FORWARD - else: - direction = libvect.GV_BACKWARD + direction = libvect.GV_FORWARD if forward else libvect.GV_BACKWARD # check if is a Line object if isinstance(line, Line): c_points = line.c_points diff --git a/python/grass/pygrass/vector/table.py b/python/grass/pygrass/vector/table.py index 9c8d0c3f9a2..5442d2f1e51 100644 --- a/python/grass/pygrass/vector/table.py +++ b/python/grass/pygrass/vector/table.py @@ -1261,10 +1261,7 @@ def create(self, cols, name=None, overwrite=False, cursor=None): """ cur = cursor or self.conn.cursor() coldef = ",\n".join(["%s %s" % col for col in cols]) - if name: - newname = name - else: - newname = self.name + newname = name or self.name try: cur.execute(sql.CREATE_TAB.format(tname=newname, coldef=coldef)) self.conn.commit() diff --git a/python/grass/script/core.py b/python/grass/script/core.py index 4f70f525133..0b51e7fdbe9 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -334,10 +334,7 @@ def get_module_and_code(args, kwargs): args = make_command(*args, **kwargs) # Since we are in error handler, let's be extra cautious # about an empty command. - if args: - module = args[0] - else: - module = None + module = args[0] if args else None code = " ".join(args) return module, code @@ -1699,10 +1696,7 @@ def mapsets(search_path=False, env=None): :return: list of mapsets """ - if search_path: - flags = "p" - else: - flags = "l" + flags = "p" if search_path else "l" mapsets = read_command("g.mapsets", flags=flags, sep="newline", quiet=True, env=env) if not mapsets: fatal(_("Unable to list mapsets")) @@ -2033,10 +2027,7 @@ def create_environment(gisdbase, location, mapset, env=None): f.write("GISDBASE: {g}\n".format(g=gisdbase)) f.write("LOCATION_NAME: {l}\n".format(l=location)) f.write("GUI: text\n") - if env: - env = env.copy() - else: - env = os.environ.copy() + env = env.copy() if env else os.environ.copy() env["GISRC"] = f.name # remove mapset-specific env vars env = sanitize_mapset_environment(env) diff --git a/python/grass/script/raster.py b/python/grass/script/raster.py index bcaab7ef596..d82780d7c5c 100644 --- a/python/grass/script/raster.py +++ b/python/grass/script/raster.py @@ -66,7 +66,7 @@ def raster_history(map, overwrite=False, env=None): "Unable to write history for <%(map)s>. " "Raster map <%(map)s> not found in current mapset." ) - % {"map": map, "map": map} + % {"map": map} ) return False @@ -218,10 +218,7 @@ def raster_what(map, coord, env=None, localized=False): query :param env: """ - if isinstance(map, (bytes, str)): - map_list = [map] - else: - map_list = map + map_list = [map] if isinstance(map, (bytes, str)) else map coord_list = [] if isinstance(coord, tuple): diff --git a/python/grass/script/task.py b/python/grass/script/task.py index cf398c59214..9867b3d8c49 100644 --- a/python/grass/script/task.py +++ b/python/grass/script/task.py @@ -345,14 +345,8 @@ def _process_params(self): for ki in node_key_desc.findall("item"): key_desc.append(ki.text) - if p.get("multiple", "no") == "yes": - multiple = True - else: - multiple = False - if p.get("required", "no") == "yes": - required = True - else: - required = False + multiple = p.get("multiple", "no") == "yes" + required = p.get("required", "no") == "yes" if ( self.task.blackList["enabled"] diff --git a/python/grass/script/tests/test_script_task.py b/python/grass/script/tests/test_script_task.py index 6799885761e..cae8735827e 100644 --- a/python/grass/script/tests/test_script_task.py +++ b/python/grass/script/tests/test_script_task.py @@ -1,11 +1,11 @@ -from grass.script.task import grassTask as gtask +from grass.script.task import grassTask def test_mapcalc_simple_e_name(): - gt = gtask("r.mapcalc.simple") + gt = grassTask("r.mapcalc.simple") assert gt.get_param("e")["name"] == "e" def test_mapcalc_simple_expession_name(): - gt = gtask("r.mapcalc.simple") + gt = grassTask("r.mapcalc.simple") assert gt.get_param("expression")["name"] == "expression" diff --git a/python/grass/script/testsuite/data/script_using_temporary_region.py b/python/grass/script/testsuite/data/script_using_temporary_region.py index 5bb5a4cda26..839bf3d0a72 100755 --- a/python/grass/script/testsuite/data/script_using_temporary_region.py +++ b/python/grass/script/testsuite/data/script_using_temporary_region.py @@ -65,15 +65,9 @@ def main(): argument = sys.argv[1] sizes = argument.split(",", 1) size = sizes[0] - if len(sizes) > 1: - remaining = sizes[1] - else: - remaining = None + remaining = sizes[1] if len(sizes) > 1 else None nesting = int(sys.argv[2]) - if len(sys.argv) == 4: - map_name = sys.argv[3] - else: - map_name = None + map_name = sys.argv[3] if len(sys.argv) == 4 else None call_use_temp_region( script=this_file, size=size, diff --git a/python/grass/script/utils.py b/python/grass/script/utils.py index 448289b57aa..25eb70190c5 100644 --- a/python/grass/script/utils.py +++ b/python/grass/script/utils.py @@ -190,10 +190,7 @@ def decode(bytes_, encoding=None): if isinstance(bytes_, str): return bytes_ if isinstance(bytes_, bytes): - if encoding is None: - enc = _get_encoding() - else: - enc = encoding + enc = _get_encoding() if encoding is None else encoding return bytes_.decode(enc) # only text should be used raise TypeError("can only accept types str and bytes") @@ -221,10 +218,7 @@ def encode(string, encoding=None): if isinstance(string, bytes): return string if isinstance(string, str): - if encoding is None: - enc = _get_encoding() - else: - enc = encoding + enc = _get_encoding() if encoding is None else encoding return string.encode(enc) # if something else than text raise TypeError("can only accept types str and bytes") @@ -276,10 +270,7 @@ def parse_key_val(s, sep="=", dflt=None, val_type=None, vsep=None): for line in lines: kv = line.split(sep, 1) k = decode(kv[0].strip()) - if len(kv) > 1: - v = decode(kv[1].strip()) - else: - v = dflt + v = decode(kv[1].strip()) if len(kv) > 1 else dflt if val_type: result[k] = val_type(v) @@ -353,10 +344,7 @@ def convert(text): return int(text) if text.isdigit() else text.lower() def alphanum_key(actual_key): - if key: - sort_key = key(actual_key) - else: - sort_key = actual_key + sort_key = key(actual_key) if key else actual_key return [convert(c) for c in re.split("([0-9]+)", sort_key)] items.sort(key=alphanum_key) diff --git a/python/grass/script/vector.py b/python/grass/script/vector.py index ca3caf18471..2d484f7d590 100644 --- a/python/grass/script/vector.py +++ b/python/grass/script/vector.py @@ -129,10 +129,7 @@ def vector_columns(map, layer=None, getDict=True, env=None, **kwargs): s = read_command( "v.info", flags="c", map=map, layer=layer, quiet=True, env=env, **kwargs ) - if getDict: - result = {} - else: - result = [] + result = {} if getDict else [] i = 0 for line in s.splitlines(): ctype, cname = line.split("|") @@ -377,10 +374,7 @@ def vector_what( if "LC_ALL" in env: env["LC_ALL"] = "C" - if isinstance(map, (bytes, str)): - map_list = [map] - else: - map_list = map + map_list = [map] if isinstance(map, (bytes, str)) else map if layer: if isinstance(layer, (tuple, list)): diff --git a/python/grass/temporal/abstract_map_dataset.py b/python/grass/temporal/abstract_map_dataset.py index ecbf761d139..f6bec2bfef3 100644 --- a/python/grass/temporal/abstract_map_dataset.py +++ b/python/grass/temporal/abstract_map_dataset.py @@ -820,10 +820,7 @@ def temporal_buffer(self, increment, update=False, dbif=None): else: start, end, unit = self.get_relative_time() new_start = start - increment - if end is None: - new_end = start + increment - else: - new_end = end + increment + new_end = start + increment if end is None else end + increment if update: self.update_relative_time(new_start, new_end, unit, dbif=dbif) diff --git a/python/grass/temporal/abstract_space_time_dataset.py b/python/grass/temporal/abstract_space_time_dataset.py index d17baaab6ae..8140a2da261 100644 --- a/python/grass/temporal/abstract_space_time_dataset.py +++ b/python/grass/temporal/abstract_space_time_dataset.py @@ -779,10 +779,7 @@ def sample_by_dataset(self, stds, method=None, spatial=False, dbif=None): # print(relations) tb = SpatioTemporalTopologyBuilder() - if spatial: - spatial = "2D" - else: - spatial = None + spatial = "2D" if spatial else None mapsA = self.get_registered_maps_as_objects(dbif=dbif) mapsB = stds.get_registered_maps_as_objects_with_gaps(dbif=dbif) @@ -1429,10 +1426,7 @@ def get_registered_maps_as_objects_with_gaps( start1, end1 = maps[i].get_temporal_extent_as_tuple() start2, end2 = maps[i + 1].get_temporal_extent_as_tuple() end = start2 - if end1 is not None: - start = end1 - else: - start = start1 + start = end1 if end1 is not None else start1 map = self.get_new_map_instance(None) diff --git a/python/grass/temporal/c_libraries_interface.py b/python/grass/temporal/c_libraries_interface.py index 85a19a3ec6e..0d24fc0f732 100644 --- a/python/grass/temporal/c_libraries_interface.py +++ b/python/grass/temporal/c_libraries_interface.py @@ -256,11 +256,7 @@ def _get_driver_name(lock, conn, data): :returns: Name of the driver or None if no temporal database present """ mapset = data[1] - if not mapset: - mapset = libgis.G_mapset() - else: - mapset = encode(mapset) - + mapset = libgis.G_mapset() if not mapset else encode(mapset) drstring = libtgis.tgis_get_mapset_driver_name(mapset) conn.send(decode(drstring.data)) @@ -280,10 +276,7 @@ def _get_database_name(lock, conn, data): dbstring = None try: mapset = data[1] - if not mapset: - mapset = libgis.G_mapset() - else: - mapset = encode(mapset) + mapset = libgis.G_mapset() if not mapset else encode(mapset) dbstring = libtgis.tgis_get_mapset_database_name(mapset) dbstring = dbstring.data diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index 1031582fe23..b986637162a 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -694,10 +694,7 @@ def compute_datetime_delta(start, end): else: d += 24 * 60 * day_diff elif d == 0: - if comp["hour"]: - d = 60 * comp["hour"] - else: - d = 24 * 60 * day_diff + d = 60 * comp["hour"] if comp["hour"] else 24 * 60 * day_diff comp["minute"] = d @@ -914,10 +911,7 @@ def datetime_to_grass_datetime_string(dt): # Check for time zone info in the datetime object if dt.tzinfo is not None: tz = dt.tzinfo.utcoffset(0) - if tz.seconds > 86400 / 2: - tz = (tz.seconds - 86400) / 60 - else: - tz = tz.seconds / 60 + tz = (tz.seconds - 86400) / 60 if tz.seconds > 86400 / 2 else tz.seconds / 60 string = "%.2i %s %.2i %.2i:%.2i:%.2i %+.4i" % ( dt.day, @@ -999,10 +993,7 @@ def create_numeric_suffix(base, count, zeros): if len(spli) == 2: suff = spli[1] if suff.isdigit(): - if int(suff[0]) == 0: - zero = suff - else: - zero = "0{nu}".format(nu=suff) + zero = suff if int(suff[0]) == 0 else "0{nu}".format(nu=suff) else: zero = "05" else: diff --git a/python/grass/temporal/gui_support.py b/python/grass/temporal/gui_support.py index 41f22edc6b7..d2cbca13d68 100644 --- a/python/grass/temporal/gui_support.py +++ b/python/grass/temporal/gui_support.py @@ -37,16 +37,14 @@ def tlist_grouped(type, group_type=False, dbif=None): :return: directory of mapsets/elements """ result = {} + _type = type dbif, connection_state_changed = init_dbif(dbif) mapset = None - if type == "stds": - types = ["strds", "str3ds", "stvds"] - else: - types = [type] - for type in types: + types = ["strds", "str3ds", "stvds"] if _type == "stds" else [_type] + for _type in types: try: - tlist_result = tlist(type=type, dbif=dbif) + tlist_result = tlist(type=_type, dbif=dbif) except gs.ScriptError as e: gs.warning(e) continue @@ -65,10 +63,10 @@ def tlist_grouped(type, group_type=False, dbif=None): result[mapset] = [] if group_type: - if type in result[mapset]: - result[mapset][type].append(name) + if _type in result[mapset]: + result[mapset][_type].append(name) else: - result[mapset][type] = [ + result[mapset][_type] = [ name, ] else: @@ -90,19 +88,20 @@ def tlist(type, dbif=None): :return: a list of space time dataset ids """ + _type = type id = None - sp = dataset_factory(type, id) + sp = dataset_factory(_type, id) dbif, connection_state_changed = init_dbif(dbif) mapsets = get_available_temporal_mapsets() output = [] temporal_type = ["absolute", "relative"] - for type in temporal_type: + for _type in temporal_type: # For each available mapset for mapset in mapsets.keys(): # Table name - if type == "absolute": + if _type == "absolute": table = sp.get_type() + "_view_abs_time" else: table = sp.get_type() + "_view_rel_time" diff --git a/python/grass/temporal/list_stds.py b/python/grass/temporal/list_stds.py index 9f6b6867766..22a2708123d 100644 --- a/python/grass/temporal/list_stds.py +++ b/python/grass/temporal/list_stds.py @@ -325,10 +325,7 @@ def _get_get_registered_maps_as_objects_delta_gran( msgr.fatal(_("Empty entry in map list, this should not happen")) start, end = map_object.get_temporal_extent_as_tuple() - if end: - delta = end - start - else: - delta = None + delta = end - start if end else None delta_first = start - first_time if map_object.is_time_absolute(): diff --git a/python/grass/temporal/register.py b/python/grass/temporal/register.py index 8b7852ab557..d8705765d02 100644 --- a/python/grass/temporal/register.py +++ b/python/grass/temporal/register.py @@ -158,10 +158,7 @@ def register_maps_in_space_time_dataset( # Read the map list from file if file: - if hasattr(file, "readline"): - fd = file - else: - fd = open(file) + fd = file if hasattr(file, "readline") else open(file) line = True while True: @@ -264,10 +261,7 @@ def register_maps_in_space_time_dataset( end = row["end"] # Use the semantic label from file - if "semantic_label" in row: - semantic_label = row["semantic_label"] - else: - semantic_label = None + semantic_label = row.get("semantic_label", None) is_in_db = map_object.is_in_db(dbif, mapset) @@ -642,10 +636,7 @@ def register_map_object_list( string = f"{id}|{start}|{end}\n" register_file.write(string) - if output_stds: - output_stds_id = output_stds.get_id() - else: - output_stds_id = None + output_stds_id = output_stds.get_id() if output_stds else None register_maps_in_space_time_dataset( type, output_stds_id, unit=unit, file=filename, dbif=dbif diff --git a/python/grass/temporal/sampling.py b/python/grass/temporal/sampling.py index 425ca484da5..5209ce6a80c 100644 --- a/python/grass/temporal/sampling.py +++ b/python/grass/temporal/sampling.py @@ -82,19 +82,11 @@ def sample_stds_by_stds_topology( sts = [] for input in inputs: - if input.find("@") >= 0: - id = input - else: - id = input + "@" + mapset - + id = input if input.find("@") >= 0 else input + "@" + mapset st = dataset_factory(intype, id) sts.append(st) - if sampler.find("@") >= 0: - sid = sampler - else: - sid = sampler + "@" + mapset - + sid = sampler if sampler.find("@") >= 0 else sampler + "@" + mapset sst = dataset_factory(sampletype, sid) dbif = SQLDatabaseInterfaceConnection() @@ -156,10 +148,7 @@ def sample_stds_by_stds_topology( map = entry["granule"] start, end = map.get_temporal_extent_as_tuple() - if end: - delta = end - start - else: - delta = None + delta = end - start if end else None delta_first = start - first_time if map.is_time_absolute(): diff --git a/python/grass/temporal/temporal_algebra.py b/python/grass/temporal/temporal_algebra.py index e4717eb3326..2429b61e35e 100644 --- a/python/grass/temporal/temporal_algebra.py +++ b/python/grass/temporal/temporal_algebra.py @@ -992,10 +992,7 @@ def generate_map_name(self): same object for map name generation in multiple threads. """ self.count += 1 - if self.pid is not None: - pid = self.pid - else: - pid = os.getpid() + pid = self.pid if self.pid is not None else os.getpid() name = "tmp_map_name_%i_%i" % (pid, self.count) self.names[name] = name return name @@ -1234,10 +1231,7 @@ def check_stds(self, input, clear=False, stds_type=None, check_type=True): """ if isinstance(input, str): # Check for mapset in given stds input. - if input.find("@") >= 0: - id_input = input - else: - id_input = input + "@" + self.mapset + id_input = input if input.find("@") >= 0 else input + "@" + self.mapset # Create empty spacetime dataset. if stds_type: stds = dataset_factory(stds_type, id_input) @@ -1634,7 +1628,7 @@ def build_spatio_temporal_topology_list( def assign_bool_value( self, map_i, temporal_topo_list=["EQUAL"], spatial_topo_list=[] - ): + ) -> bool: """Function to assign boolean map value based on the map_values from the compared map list by topological relationships. @@ -1668,10 +1662,7 @@ def assign_bool_value( str(relationmap.get_temporal_extent_as_tuple()) + str(boolean), ) - if all(condition_value_list): - resultbool = True - else: - resultbool = False + resultbool = bool(all(condition_value_list)) map_i.condition_value = [resultbool] return resultbool @@ -2296,20 +2287,14 @@ def recurse_compare(conditionlist): ele_index = conditionlist.index(ele) right = conditionlist.pop(ele_index) left = conditionlist.pop(ele_index - 2) - if any([left, right]): - result = True - else: - result = False + result = bool(any([left, right])) conditionlist[ele_index - 2] = result recurse_compare(conditionlist) if ele == "&&": ele_index = conditionlist.index(ele) right = conditionlist.pop(ele_index) left = conditionlist.pop(ele_index - 2) - if all([left, right]): - result = True - else: - result = False + result = bool(all([left, right])) conditionlist[ele_index - 2] = result recurse_compare(conditionlist) @@ -2643,10 +2628,7 @@ def p_expr_tmap_function(self, t): input = t[3] if not isinstance(input, list): # Check for mapset in given stds input. - if input.find("@") >= 0: - id_input = input - else: - id_input = input + "@" + self.mapset + id_input = input if input.find("@") >= 0 else input + "@" + self.mapset # Create empty map dataset. map_i = dataset_factory(self.maptype, id_input) # Check for occurrence of space time dataset. @@ -3079,10 +3061,7 @@ def p_expr_t_select_operator(self, t): # Evaluate temporal operator. operators = self.eval_toperator(t[2], optype="select") # Check for negative selection. - if operators[2] == "!:": - negation = True - else: - negation = False + negation = operators[2] == "!:" # Perform selection. selectlist = self.perform_temporal_selection( maplistA, maplistB, topolist=operators[0], inverse=negation diff --git a/python/grass/temporal/temporal_granularity.py b/python/grass/temporal/temporal_granularity.py index b4043054520..173e4e715ea 100644 --- a/python/grass/temporal/temporal_granularity.py +++ b/python/grass/temporal/temporal_granularity.py @@ -494,7 +494,7 @@ def compute_absolute_time_granularity(maps): # Keep the temporal extent to compare to the following/next map previous_start, previous_end = start, end - # Create a list with a single time unit only + # Create a set with a single time unit only dlist = set() assigned_time_unit = None time_unit_multipliers = { @@ -529,11 +529,8 @@ def compute_absolute_time_granularity(maps): if not dlist: return None - if len(dlist) > 1: - # Find greatest common divisor - granularity = gcd_list(dlist) - else: - granularity = dlist.pop() + # Find greatest common divisor to get a single time unit + granularity = gcd_list(dlist) if len(dlist) > 1 else dlist.pop() if granularity is None: return None diff --git a/python/grass/temporal/temporal_raster_base_algebra.py b/python/grass/temporal/temporal_raster_base_algebra.py index 8ff156cbed5..cb2122f4a37 100644 --- a/python/grass/temporal/temporal_raster_base_algebra.py +++ b/python/grass/temporal/temporal_raster_base_algebra.py @@ -960,10 +960,7 @@ def p_expr_spmap_function(self, t): input = t[3] if not isinstance(input, list): # Check for mapset in given stds input. - if input.find("@") >= 0: - id_input = input - else: - id_input = input + "@" + self.mapset + id_input = input if input.find("@") >= 0 else input + "@" + self.mapset # Create empty map dataset. map_i = dataset_factory(self.maptype, id_input) # Check for occurrence of space time dataset. diff --git a/python/grass/temporal/univar_statistics.py b/python/grass/temporal/univar_statistics.py index 96a9f65474d..b6a9c1ac508 100755 --- a/python/grass/temporal/univar_statistics.py +++ b/python/grass/temporal/univar_statistics.py @@ -103,7 +103,7 @@ def compute_univar_stats(registered_map_info, stats_module, fs, rast_region=Fals for perc in stats_module.inputs.percentile: perc_value = stats[ "percentile_" - f"{str(perc).rstrip('0').rstrip('.').replace('.','_')}" + f"{str(perc).rstrip('0').rstrip('.').replace('.', '_')}" ] string += f"{fs}{perc_value}" string += eol @@ -220,7 +220,7 @@ def print_gridded_dataset_univar_statistics( cols.extend( [ "percentile_" - f"{str(perc).rstrip('0').rstrip('.').replace('.','_')}" + f"{str(perc).rstrip('0').rstrip('.').replace('.', '_')}" for perc in percentile ] ) @@ -304,10 +304,7 @@ def print_vector_dataset_univar_statistics( mapset = get_current_mapset() - if input.find("@") >= 0: - id = input - else: - id = input + "@" + mapset + id = input if input.find("@") >= 0 else input + "@" + mapset sp = dataset_factory("stvds", id) diff --git a/raster/Makefile b/raster/Makefile index bcd07660238..91ff54d0863 100644 --- a/raster/Makefile +++ b/raster/Makefile @@ -45,6 +45,7 @@ SUBDIRS = \ r.lake \ r.li \ r.mapcalc \ + r.mask.status \ r.mfilter \ r.mode \ r.neighbors \ diff --git a/raster/r.basins.fill/testsuite/testrbf.py b/raster/r.basins.fill/testsuite/testrbf.py index b9937b05689..4077a2113a4 100644 --- a/raster/r.basins.fill/testsuite/testrbf.py +++ b/raster/r.basins.fill/testsuite/testrbf.py @@ -5,8 +5,8 @@ Author: Sunveer Singh Copyright: (C) 2017 by Sunveer Singh and the GRASS Development Team Licence: This program is free software under the GNU General Public - License (>=v2). Read the file COPYING that comes with GRASS - for details. + License (>=v2). Read the file COPYING that comes with GRASS + for details. """ import unittest @@ -47,8 +47,8 @@ def setUpClass(cls): def tearDownClass(cls): cls.del_temp_region() - def tearDown(cls): - cls.runModule("g.remove", flags="f", type="raster", name=cls.output) + def tearDown(self): + self.runModule("g.remove", flags="f", type="raster", name=self.output) def test_no1(self): lakes = "lakes" diff --git a/raster/r.carve/enforce_ds.c b/raster/r.carve/enforce_ds.c index ea4d7a1ae7a..7a71dc97def 100644 --- a/raster/r.carve/enforce_ds.c +++ b/raster/r.carve/enforce_ds.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "enforce.h" #ifndef MAX @@ -487,4 +488,6 @@ static void process_line_segment(const int npts, void *rbuf, Point2 *pgxypts, prevrow = row; prevcol = col; } + Vect_destroy_line_struct(points); + Vect_destroy_cats_struct(cats); } diff --git a/raster/r.circle/main.c b/raster/r.circle/main.c index 4b41a78c7c6..bd99a292f71 100644 --- a/raster/r.circle/main.c +++ b/raster/r.circle/main.c @@ -79,7 +79,9 @@ int main(int argc, char *argv[]) flag = G_define_flag(); flag->key = 'b'; - flag->description = _("Generate binary raster map"); + flag->label = _("Generate binary raster map"); + flag->description = + _("Generate binary pattern only (useful for creating mask)"); if (G_parser(argc, argv)) exit(EXIT_FAILURE); @@ -114,7 +116,7 @@ int main(int argc, char *argv[]) "using the binary flag")); if (flag->answer) - binary = 1; /* generate binary pattern only, useful for MASK */ + binary = 1; else binary = 0; diff --git a/raster/r.contour/testsuite/test_r_contour.py b/raster/r.contour/testsuite/test_r_contour.py index c9ee4c9876a..49617ef043d 100644 --- a/raster/r.contour/testsuite/test_r_contour.py +++ b/raster/r.contour/testsuite/test_r_contour.py @@ -32,10 +32,11 @@ def setUpClass(cls): def tearDownClass(cls): cls.del_temp_region() - cls.runModule("g.remove", type="vector", flags="f", name=cls.output) - cls.runModule("g.remove", type="vector", flags="f", name=cls.output + "_cut") cls.runModule( - "g.remove", type="vector", flags="f", name=cls.output + "_cut_flag_t" + "g.remove", + type="vector", + flags="f", + name=(cls.output, cls.output + "_cut", cls.output + "_cut_flag_t"), ) if os.path.isfile("testReport"): diff --git a/raster/r.external/r.external.html b/raster/r.external/r.external.html index 797cc5ea605..f20c3e059d6 100644 --- a/raster/r.external/r.external.html +++ b/raster/r.external/r.external.html @@ -17,10 +17,12 @@

NULL data handling

NULL cells are those whose value matches the value reported by the GDALGetRasterNoDataValue() function. -To apply the GDAL-linked the user need to either create a MASK (e.g. -with r.mask) and then "apply" it using e.g. r.resample, +

+To introduce additional NULL values to a computation based on a GDAL-linked +raster, the user needs to either create a mask with with r.mask and +then "apply" it using e.g. r.resample or r.mapcalc, or use r.mapcalc to create a copy with the appropriate categories -changed to NULL (if() condition). +changed to NULL (if() condition).

EXAMPLES

diff --git a/raster/r.fill.dir/r.fill.dir.html b/raster/r.fill.dir/r.fill.dir.html index 6ad8eb8019c..17173ea3dd3 100644 --- a/raster/r.fill.dir/r.fill.dir.html +++ b/raster/r.fill.dir/r.fill.dir.html @@ -69,11 +69,11 @@

DESCRIPTION

attributes required by other hydrological models.

-As any GRASS GIS module, r.fill.dir is sensitive to the -computational region settings. Thus +As any GRASS GIS module, r.fill.dir respects the +computational region settings. Thus, the module can be used to generate a flow direction map for any -sub-area within the full map layer. Also, r.fill.dir is -sensitive to any raster MASK in effect. +sub-area within the full raster map layer. Also, r.fill.dir +will take into account an active raster mask.

NOTES

diff --git a/raster/r.in.ascii/testsuite/test_r_in_ascii.py b/raster/r.in.ascii/testsuite/test_r_in_ascii.py index cff31b7b3e2..baa892d83a8 100644 --- a/raster/r.in.ascii/testsuite/test_r_in_ascii.py +++ b/raster/r.in.ascii/testsuite/test_r_in_ascii.py @@ -5,8 +5,8 @@ Author: Sunveer Singh, Google Code-in 2017 Copyright: (C) 2017 by Sunveer Singh and the GRASS Development Team Licence: This program is free software under the GNU General Public - License (>=v2). Read the file COPYING that comes with GRASS - for details. + License (>=v2). Read the file COPYING that comes with GRASS + for details. """ from grass.gunittest.case import TestCase diff --git a/raster/r.in.gridatb/file_io.c b/raster/r.in.gridatb/file_io.c index 256f9869473..52ad174df4d 100644 --- a/raster/r.in.gridatb/file_io.c +++ b/raster/r.in.gridatb/file_io.c @@ -12,6 +12,9 @@ void rdwr_gridatb(void) float idx; fp = fopen(file, "r"); + if (!fp) { + G_fatal_error(_("Unable to open file: %s"), file); + } buf[0] = 0; if (fscanf(fp, "%[^\n]", buf) != 1) diff --git a/raster/r.in.lidar/main.c b/raster/r.in.lidar/main.c index 68d3a0fcd1b..96f562167b6 100644 --- a/raster/r.in.lidar/main.c +++ b/raster/r.in.lidar/main.c @@ -51,8 +51,8 @@ int main(int argc, char *argv[]) struct PointBinning point_binning; void *base_array; void *raster_row; - struct Cell_head region; - struct Cell_head input_region; + struct Cell_head region = {0}; + struct Cell_head input_region = {0}; int rows, last_rows, row0, cols; /* scan box size */ int row; /* counters */ @@ -100,7 +100,7 @@ int main(int argc, char *argv[]) int return_filter; const char *projstr; - struct Cell_head cellhd, loc_wind; + struct Cell_head cellhd = {0}, loc_wind = {0}; unsigned int n_filtered; diff --git a/raster/r.in.pdal/main.cpp b/raster/r.in.pdal/main.cpp index da32dcb5484..36d09558eea 100644 --- a/raster/r.in.pdal/main.cpp +++ b/raster/r.in.pdal/main.cpp @@ -69,8 +69,8 @@ int main(int argc, char *argv[]) SEGMENT base_segment; struct PointBinning point_binning; void *raster_row; - struct Cell_head region; - struct Cell_head input_region; + struct Cell_head region = {}; + struct Cell_head input_region = {}; int rows, cols; /* scan box size */ char buff[BUFFSIZE]; @@ -81,7 +81,7 @@ int main(int argc, char *argv[]) bin_index_nodes.max_nodes = 0; bin_index_nodes.nodes = NULL; - struct Cell_head loc_wind; + struct Cell_head loc_wind = {}; G_gisinit(argv[0]); diff --git a/raster/r.in.pdal/testsuite/test_r_in_pdal_binning.py b/raster/r.in.pdal/testsuite/test_r_in_pdal_binning.py index a8a793f5bc7..c256224590d 100644 --- a/raster/r.in.pdal/testsuite/test_r_in_pdal_binning.py +++ b/raster/r.in.pdal/testsuite/test_r_in_pdal_binning.py @@ -68,8 +68,12 @@ def tearDown(self): This is executed after each test run. """ - self.runModule("g.remove", flags="f", type="raster", name=self.bin_raster) - self.runModule("g.remove", flags="f", type="raster", name=self.ref_raster) + self.runModule( + "g.remove", + flags="f", + type="raster", + name=(self.bin_raster, self.ref_raster), + ) @unittest.skipIf(shutil.which("r.in.pdal") is None, "Cannot find r.in.pdal") def test_method_n(self): diff --git a/raster/r.in.pdal/testsuite/test_r_in_pdal_selection.py b/raster/r.in.pdal/testsuite/test_r_in_pdal_selection.py index b7d5acd6b28..4796702d2f6 100644 --- a/raster/r.in.pdal/testsuite/test_r_in_pdal_selection.py +++ b/raster/r.in.pdal/testsuite/test_r_in_pdal_selection.py @@ -67,8 +67,12 @@ def tearDown(self): This is executed after each test run. """ - self.runModule("g.remove", flags="f", type="raster", name=self.imp_raster) - self.runModule("g.remove", flags="f", type="raster", name=self.ref_raster) + self.runModule( + "g.remove", + flags="f", + type="raster", + name=(self.imp_raster, self.ref_raster), + ) try: self.runModule("g.remove", flags="f", type="raster", name=self.base_raster) except AttributeError: diff --git a/raster/r.in.xyz/main.c b/raster/r.in.xyz/main.c index a4c7dfde6ed..b3c3fb380cf 100644 --- a/raster/r.in.xyz/main.c +++ b/raster/r.in.xyz/main.c @@ -112,7 +112,7 @@ int main(int argc, char *argv[]) char *n_array, *min_array, *max_array, *sum_array, *sumsq_array, *index_array; void *raster_row, *ptr; - struct Cell_head region; + struct Cell_head region = {0}; int rows, last_rows, row0, cols; /* scan box size */ int row, col; /* counters */ diff --git a/raster/r.kappa/testsuite/test_r_kappa.py b/raster/r.kappa/testsuite/test_r_kappa.py index 620a4f4979b..ff11f477925 100644 --- a/raster/r.kappa/testsuite/test_r_kappa.py +++ b/raster/r.kappa/testsuite/test_r_kappa.py @@ -51,8 +51,9 @@ def setUpClass(cls): def tearDownClass(cls): """Remove temporary data""" cls.del_temp_region() - cls.runModule("g.remove", flags="f", type="raster", name=cls.ref_1) - cls.runModule("g.remove", flags="f", type="raster", name=cls.class_1) + cls.runModule( + "g.remove", flags="f", type="raster", name=(cls.ref_1, cls.class_1) + ) def test_m(self): """Test printing matrix only @@ -119,8 +120,9 @@ def setUpClass(cls): def tearDownClass(cls): """Remove temporary data""" cls.del_temp_region() - cls.runModule("g.remove", flags="f", type="raster", name=cls.ref_1) - cls.runModule("g.remove", flags="f", type="raster", name=cls.class_1) + cls.runModule( + "g.remove", flags="f", type="raster", name=(cls.ref_1, cls.class_1) + ) def match(self, pat, ref): if pat == "NA" or ref == "NA": @@ -233,8 +235,9 @@ def setUpClass(cls): def tearDownClass(cls): """Remove temporary data""" cls.del_temp_region() - cls.runModule("g.remove", flags="f", type="raster", name=cls.ref_1) - cls.runModule("g.remove", flags="f", type="raster", name=cls.class_1) + cls.runModule( + "g.remove", flags="f", type="raster", name=(cls.ref_1, cls.class_1) + ) def match(self, pat, ref): if pat == "NA" or ref == "NA": diff --git a/raster/r.li/TODO b/raster/r.li/TODO index 61ddd6b0941..382195402a6 100644 --- a/raster/r.li/TODO +++ b/raster/r.li/TODO @@ -24,12 +24,13 @@ d.vect forests type=boundary ######## TODO: CHECK THIS: -# MASK test -g.copy rast=fields,MASK +# Test with raster mask +r.mask raster=fields r.li.patchdensity forests conf=movwindow7 output=forests_p_dens7mask --o d.erase d.rast.leg forests_p_dens7mask -# -> no negative values! but MASK is respected +r.mask -r +# -> no negative values! but mask is respected # zero data test r.mapcalc "forests = 0" diff --git a/raster/r.mask.status/Makefile b/raster/r.mask.status/Makefile new file mode 100644 index 00000000000..62c968d044e --- /dev/null +++ b/raster/r.mask.status/Makefile @@ -0,0 +1,10 @@ +MODULE_TOPDIR = ../.. + +PGM = r.mask.status + +LIBES = $(MANAGELIB) $(RASTERLIB) $(GISLIB) $(PARSONLIB) +DEPENDENCIES = $(MANAGEDEP) $(RASTERDEP) $(GISDEP) + +include $(MODULE_TOPDIR)/include/Make/Module.make + +default: cmd diff --git a/raster/r.mask.status/main.c b/raster/r.mask.status/main.c new file mode 100644 index 00000000000..16790adf35c --- /dev/null +++ b/raster/r.mask.status/main.c @@ -0,0 +1,186 @@ +/**************************************************************************** + * + * MODULE: r.mask.status + * AUTHORS: Vaclav Petras + * PURPOSE: Report status of raster mask + * COPYRIGHT: (C) 2024 by Vaclav Petras and the GRASS Development Team + * + * This program is free software under the GNU General Public + * License (>=v2). Read the file COPYING that comes with GRASS + * for details. + * + *****************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +struct Parameters { + struct Option *format; + struct Flag *like_test; +}; + +void parse_parameters(struct Parameters *params, int argc, char **argv) +{ + struct GModule *module; + + module = G_define_module(); + G_add_keyword(_("raster")); + G_add_keyword(_("mask")); + G_add_keyword(_("reclassification")); + module->label = _("Reports presence or absence of a raster mask"); + module->description = + _("Provides information about the presence of a 2D raster mask" + " as text output or return code"); + + params->format = G_define_option(); + params->format->key = "format"; + params->format->type = TYPE_STRING; + params->format->required = NO; + params->format->answer = "plain"; + params->format->options = "plain,json,shell,yaml"; + params->format->descriptions = + "plain;Plain text output;" + "json;JSON (JavaScript Object Notation);" + "shell;Shell script style output;" + "yaml;YAML (human-friendly data serialization language)"; + params->format->description = _("Format for reporting"); + + params->like_test = G_define_flag(); + params->like_test->key = 't'; + params->like_test->label = + _("Return code 0 when mask present, 1 otherwise"); + params->like_test->description = + _("Behave like the test utility, 0 for true, 1 for false, no output"); + // suppress_required is not required given the default value for format. + // Both no parameters and only -t work as expected. + + if (G_parser(argc, argv)) + exit(EXIT_FAILURE); +} + +int report_status(struct Parameters *params) +{ + + char name[GNAME_MAX]; + char mapset[GMAPSET_MAX]; + char reclass_name[GNAME_MAX]; + char reclass_mapset[GMAPSET_MAX]; + + bool is_mask_reclass; + bool present = Rast_mask_status(name, mapset, &is_mask_reclass, + reclass_name, reclass_mapset); + + // This does not have to be exclusive with the printing, but leaving this + // to a different boolean flag which could do the return code and printing. + // The current implementation really behaves like the test utility which + // facilitates the primary usage of this which is prompt building + // (and there any output would be noise). + if (params->like_test->answer) { + if (present) + return 0; + return 1; + } + + // Mask raster + char *full_mask = G_fully_qualified_name(name, mapset); + // Underlying raster if applicable + char *full_underlying = NULL; + if (is_mask_reclass) + full_underlying = G_fully_qualified_name(reclass_name, reclass_mapset); + + if (strcmp(params->format->answer, "json") == 0) { + JSON_Value *root_value = json_value_init_object(); + JSON_Object *root_object = json_object(root_value); + json_object_set_boolean(root_object, "present", present); + if (present) + json_object_set_string(root_object, "full_name", full_mask); + else + json_object_set_null(root_object, "full_name"); + if (is_mask_reclass) + json_object_set_string(root_object, "is_reclass_of", + full_underlying); + else + json_object_set_null(root_object, "is_reclass_of"); + char *serialized_string = json_serialize_to_string_pretty(root_value); + if (!serialized_string) + G_fatal_error(_("Failed to initialize pretty JSON string.")); + puts(serialized_string); + json_free_serialized_string(serialized_string); + json_value_free(root_value); + } + else if (strcmp(params->format->answer, "shell") == 0) { + printf("present="); + if (present) + printf("1"); + else + printf("0"); + printf("\nfull_name="); + if (present) + printf("%s", full_mask); + printf("\nis_reclass_of="); + if (is_mask_reclass) + printf("%s", full_underlying); + printf("\n"); + } + else if (strcmp(params->format->answer, "yaml") == 0) { + printf("present: "); + if (present) + printf("true"); + else + printf("false"); + printf("\nfull_name: "); + if (present) + printf("|-\n %s", full_mask); + else + printf("null"); + // Null values in YAML can be an empty (no) value (rather than null), + // so we could use that, but using the explicit null as a reasonable + // starting point. + printf("\nis_reclass_of: "); + // Using block scalar with |- to avoid need for escaping. + // Alternatively, we could check mapset naming limits against YAML + // escaping needs for different types of strings and do the necessary + // escaping here. + if (is_mask_reclass) + printf("|-\n %s", full_underlying); + else + printf("null"); + printf("\n"); + } + else { + if (present) + printf(_("Mask is active")); + else + printf(_("Mask is not present")); + if (present) { + printf("\n"); + printf(_("Mask name: %s"), full_mask); + } + if (is_mask_reclass) { + printf("\n"); + printf(_("Mask is a raster reclassified from: %s"), + full_underlying); + } + printf("\n"); + } + + G_free(full_mask); + G_free(full_underlying); + return EXIT_SUCCESS; +} + +int main(int argc, char **argv) +{ + struct Parameters params; + + G_gisinit(argv[0]); + parse_parameters(¶ms, argc, argv); + return report_status(¶ms); +} diff --git a/raster/r.mask.status/r.mask.status.html b/raster/r.mask.status/r.mask.status.html new file mode 100644 index 00000000000..248ee3ea317 --- /dev/null +++ b/raster/r.mask.status/r.mask.status.html @@ -0,0 +1,65 @@ +

DESCRIPTION

+ +The r.mask.status reports information about the 2D raster mask and its +status. If the mask is present, the tool reports a full name of the raster (name +including the mapset) which represents the mask. It can also report full name of +the underlying raster if the mask is reclassified from another raster. + +

+With the -t flag, no output is printed, instead a return code is used to +indicate presence or absence. The convention is the same same the POSIX +test utility, so r.mask.status returns 0 when the mask is +present and 1 otherwise. + +

EXAMPLES

+ +

Generate JSON output

+ +To generate JSON output in Bash, use the format option: + +
+r.mask.status format=json
+
+ +In Python, use: + +
+import grass.script as gs
+gs.parse_command("r.mask.status", format="json")
+
+ +This returns a dictionary with keys present, +full_name, and is_reclass_of. + +

Use as the test utility

+ +The POSIX test utility uses return code 0 to indicate presence +and 1 to indicate absence of a file, so testing existence of a file with +test -f gives return code 0 when the file exists. +r.mask.status can be used in the same with the the -t flag: + +
+r.mask.status -t
+
+ +In a Bash script: + +
+# Bash
+if r.mask.status -t; then
+    echo "Masking is active"
+else
+    echo "Masking is not active"
+fi
+
+ +

SEE ALSO

+ + +r.mask, +g.region + + +

AUTHORS

+ +Vaclav Petras, NC State University, Center for Geospatial Analytics diff --git a/raster/r.mask.status/tests/conftest.py b/raster/r.mask.status/tests/conftest.py new file mode 100644 index 00000000000..e8e27315845 --- /dev/null +++ b/raster/r.mask.status/tests/conftest.py @@ -0,0 +1,25 @@ +"""Fixtures for simple sessions""" + +import os +import pytest +import grass.script as gs + + +@pytest.fixture +def session_no_data(tmp_path): + """Set up a GRASS session for the tests.""" + project = "test_project" + gs.create_project(tmp_path, project) + with gs.setup.init(tmp_path / project, env=os.environ.copy()) as session: + yield session + + +@pytest.fixture +def session_with_data(tmp_path): + """Set up a GRASS session for the tests.""" + project = tmp_path / "test_project" + gs.create_project(project) + with gs.setup.init(project, env=os.environ.copy()) as session: + gs.run_command("g.region", rows=2, cols=2, env=session.env) + gs.mapcalc("a = 1", env=session.env) + yield session diff --git a/raster/r.mask.status/tests/r_mask_status_test.py b/raster/r.mask.status/tests/r_mask_status_test.py new file mode 100644 index 00000000000..deafdfb145b --- /dev/null +++ b/raster/r.mask.status/tests/r_mask_status_test.py @@ -0,0 +1,126 @@ +"""Tests of r.mask.status""" + +import pytest + +try: + import yaml +except ImportError: + yaml = None + +import grass.script as gs + + +def test_json_no_mask(session_no_data): + """Check JSON format for no mask""" + session = session_no_data + data = gs.parse_command("r.mask.status", format="json", env=session.env) + assert "present" in data + assert "full_name" in data + assert "is_reclass_of" in data + assert data["present"] is False + assert not data["full_name"] + assert not data["is_reclass_of"] + + +def test_json_with_r_mask(session_with_data): + """Check JSON format for the r.mask case""" + session = session_with_data + gs.run_command("r.mask", raster="a", env=session.env) + data = gs.parse_command("r.mask.status", format="json", env=session.env) + assert data["present"] is True + assert data["full_name"] == "MASK@PERMANENT" + assert data["is_reclass_of"] == "a@PERMANENT" + # Now remove the mask. + gs.run_command("r.mask", flags="r", env=session.env) + data = gs.parse_command("r.mask.status", format="json", env=session.env) + assert data["present"] is False + assert not data["full_name"] + assert not data["is_reclass_of"] + + +def test_json_with_g_copy(session_with_data): + """Check JSON format for the low-level g.copy case""" + session = session_with_data + gs.run_command("g.copy", raster="a,MASK", env=session.env) + data = gs.parse_command("r.mask.status", format="json", env=session.env) + assert data["present"] is True + assert data["full_name"] == "MASK@PERMANENT" + assert not data["is_reclass_of"] + # Now remove the mask. + gs.run_command("g.remove", type="raster", name="MASK", flags="f", env=session.env) + data = gs.parse_command("r.mask.status", format="json", env=session.env) + assert data["present"] is False + assert not data["full_name"] + assert not data["is_reclass_of"] + + +def test_shell(session_with_data): + """Check shell format for the r.mask case""" + session = session_with_data + gs.run_command("r.mask", raster="a", env=session.env) + data = gs.parse_command("r.mask.status", format="shell", env=session.env) + assert int(data["present"]) + assert data["full_name"] == "MASK@PERMANENT" + assert data["is_reclass_of"] == "a@PERMANENT" + # Now remove the mask. + gs.run_command("r.mask", flags="r", env=session.env) + data = gs.parse_command("r.mask.status", format="shell", env=session.env) + assert not int(data["present"]) + assert not data["full_name"] + assert not data["is_reclass_of"] + + +@pytest.mark.skipif(yaml is None, reason="PyYAML package not available") +def test_yaml(session_with_data): + """Check YAML format for the r.mask case""" + session = session_with_data + gs.run_command("r.mask", raster="a", env=session.env) + text = gs.read_command("r.mask.status", format="yaml", env=session.env) + data = yaml.safe_load(text) + assert data["present"] is True + assert data["full_name"] == "MASK@PERMANENT" + assert data["is_reclass_of"] == "a@PERMANENT" + # Now remove the mask. + gs.run_command("r.mask", flags="r", env=session.env) + text = gs.read_command("r.mask.status", format="yaml", env=session.env) + data = yaml.safe_load(text) + assert data["present"] is False + assert not data["full_name"] + assert not data["is_reclass_of"] + + +def test_plain(session_with_data): + """Check plain text format for the r.mask case""" + session = session_with_data + gs.run_command("r.mask", raster="a", env=session.env) + text = gs.read_command("r.mask.status", format="plain", env=session.env) + assert text + assert "MASK@PERMANENT" in text + assert "a@PERMANENT" in text + # Now remove the mask. + gs.run_command("r.mask", flags="r", env=session.env) + text = gs.read_command("r.mask.status", format="plain", env=session.env) + assert text + + +def test_without_parameters(session_no_data): + """Check output is generated with no parameters""" + session = session_no_data + text = gs.read_command("r.mask.status", env=session.env) + assert text + + +def test_behavior_mimicking_test_program(session_with_data): + """Check test program like behavior for the r.mask case""" + session = session_with_data + gs.run_command("r.mask", raster="a", env=session.env) + returncode = gs.run_command( + "r.mask.status", flags="t", env=session.env, errors="status" + ) + assert returncode == 0 + # Now remove the mask. + gs.run_command("r.mask", flags="r", env=session.env) + returncode = gs.run_command( + "r.mask.status", flags="t", env=session.env, errors="status" + ) + assert returncode == 1 diff --git a/raster/r.mfilter/benchmark/benchmark_r_mfilter_nprocs.py b/raster/r.mfilter/benchmark/benchmark_r_mfilter_nprocs.py index 2dc85b411d8..9a0591f7cfc 100644 --- a/raster/r.mfilter/benchmark/benchmark_r_mfilter_nprocs.py +++ b/raster/r.mfilter/benchmark/benchmark_r_mfilter_nprocs.py @@ -55,8 +55,7 @@ def benchmark(size, label, results): overwrite=True, ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=16, repeat=3)) - Module("g.remove", quiet=True, flags="f", type="raster", name=reference) - Module("g.remove", quiet=True, flags="f", type="raster", name=output) + Module("g.remove", quiet=True, flags="f", type="raster", name=(reference, output)) def generate_map(rows, cols, fname): diff --git a/raster/r.neighbors/benchmark/benchmark_r_neighbors_nprocs.py b/raster/r.neighbors/benchmark/benchmark_r_neighbors_nprocs.py index d355792a734..e7b3196c246 100644 --- a/raster/r.neighbors/benchmark/benchmark_r_neighbors_nprocs.py +++ b/raster/r.neighbors/benchmark/benchmark_r_neighbors_nprocs.py @@ -38,8 +38,7 @@ def benchmark(size, label, results): overwrite=True, ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=16, repeat=3)) - Module("g.remove", quiet=True, flags="f", type="raster", name=reference) - Module("g.remove", quiet=True, flags="f", type="raster", name=output) + Module("g.remove", quiet=True, flags="f", type="raster", name=(reference, output)) def generate_map(rows, cols, fname): diff --git a/raster/r.null/r.null.html b/raster/r.null/r.null.html index 0a98907af5f..4d92b027d65 100644 --- a/raster/r.null/r.null.html +++ b/raster/r.null/r.null.html @@ -49,10 +49,12 @@

External maps

From the r.external documentation: GDAL-linked (r.external) maps do not have or use a NULL bitmap, hence r.null cannot manipulate them directly. Here NULL cells are those whose value matches -the value reported by the GDALGetRasterNoDataValue() function. To apply the -GDAL-linked the user need to either create a MASK (e.g. with r.mask) and -then "apply" it using e.g. r.resample, or use r.mapcalc to create a copy -with the appropriate categories changed to NULL (if() condition). +the value reported by the GDALGetRasterNoDataValue() function. +To introduce additional NULL values to a computation based on a GDAL-linked +raster, the user needs to either create a mask with with r.mask and +then "apply" it using e.g. r.resample or r.mapcalc, +or use r.mapcalc to create a copy with the appropriate categories +changed to NULL (if() condition).

EXAMPLES

diff --git a/raster/r.proj/testsuite/test_rproj.py b/raster/r.proj/testsuite/test_rproj.py index 5f3834be881..678e8b7cd06 100644 --- a/raster/r.proj/testsuite/test_rproj.py +++ b/raster/r.proj/testsuite/test_rproj.py @@ -57,7 +57,7 @@ def run_rproj_test(self, method, statics): The expected statics of the output raster """ output = method - ## Get the boundary and set up region for the projected map + # Get the boundary and set up region for the projected map stdout = call_module( "r.proj", project=src_project, @@ -80,7 +80,7 @@ def run_rproj_test(self, method, statics): res=1, ) - ## Project the map + # Project the map self.assertModule( "r.proj", project=src_project, @@ -91,13 +91,13 @@ def run_rproj_test(self, method, statics): quiet=True, ) - ## Validate the output + # Validate the output self.assertRasterFitsUnivar(output, reference=statics, precision=1e-7) self.assertRasterFitsInfo(output, reference=raster_info, precision=1e-7) def test_nearest(self): """Testing method nearest""" - ## Set up variables and validation values + # Set up variables and validation values method = "nearest" statics = """n=40930 min=55.5787925720215 @@ -109,7 +109,7 @@ def test_nearest(self): def test_bilinear(self): """Testing method bilinear""" - ## Set up variables and validation values + # Set up variables and validation values method = "bilinear" statics = """n=40845 min=56.3932914733887 @@ -121,7 +121,7 @@ def test_bilinear(self): def test_bicubic(self): """Testing method bicubic""" - ## Set up variables and validation values + # Set up variables and validation values method = "bicubic" statics = """n=40677 min=56.2407836914062 @@ -133,7 +133,7 @@ def test_bicubic(self): def test_lanczos(self): """Testing method lanczos""" - ## Set up variables and validation values + # Set up variables and validation values method = "lanczos" statics = """n=40585 min=56.2350921630859 @@ -145,7 +145,7 @@ def test_lanczos(self): def test_bilinear_f(self): """Testing method bilinear_f""" - ## Set up variables and validation values + # Set up variables and validation values method = "bilinear_f" statics = """n=40930 min=55.5787925720215 @@ -157,7 +157,7 @@ def test_bilinear_f(self): def test_bicubic_f(self): """Testing method bicubic_f""" - ## Set up variables and validation values + # Set up variables and validation values method = "bicubic_f" statics = """n=40930 min=55.5787925720215 @@ -169,7 +169,7 @@ def test_bicubic_f(self): def test_lanczos_f(self): """Testing method lanczos_f""" - ## Set up variables and validation values + # Set up variables and validation values method = "lanczos_f" statics = """n=40930 min=55.5787925720215 diff --git a/raster/r.random/testsuite/test_r_random.py b/raster/r.random/testsuite/test_r_random.py index ce12c719c8e..16aae357ebf 100644 --- a/raster/r.random/testsuite/test_r_random.py +++ b/raster/r.random/testsuite/test_r_random.py @@ -28,30 +28,30 @@ def setUpClass(cls): def tearDownClass(cls): cls.del_temp_region() - cls.runModule("g.remove", type="raster", flags="f", name=cls.raster) - cls.runModule("g.remove", type="raster", flags="f", name=cls.raster + "_null") - cls.runModule( - "g.remove", type="raster", flags="f", name=cls.raster + "_without_topology" - ) - cls.runModule("g.remove", type="raster", flags="f", name=cls.raster + "_3D") cls.runModule( "g.remove", type="raster", flags="f", - name=cls.raster + "_cover_landcover_1m", + name=( + cls.raster, + cls.raster + "_null", + cls.raster + "_without_topology", + cls.raster + "_3D", + cls.raster + "_cover_landcover_1m", + ), ) - cls.runModule("g.remove", type="vector", flags="f", name=cls.vector) - cls.runModule("g.remove", type="vector", flags="f", name=cls.vector + "_null") - cls.runModule( - "g.remove", type="vector", flags="f", name=cls.vector + "_without_topology" - ) - cls.runModule("g.remove", type="vector", flags="f", name=cls.vector + "_3D") cls.runModule( "g.remove", type="vector", flags="f", - name=cls.vector + "_cover_landcover_1m", + name=( + cls.vector, + cls.vector + "_null", + cls.vector + "_without_topology", + cls.vector + "_3D", + cls.vector + "_cover_landcover_1m", + ), ) def test_random_raster(self): diff --git a/raster/r.reclass/testsuite/test_r_reclass.py b/raster/r.reclass/testsuite/test_r_reclass.py index 3f4ecfdf6a3..8b1eb2e523a 100644 --- a/raster/r.reclass/testsuite/test_r_reclass.py +++ b/raster/r.reclass/testsuite/test_r_reclass.py @@ -5,8 +5,8 @@ Author: Sunveer Singh, Google Code-in 2017 Copyright: (C) 2017 by Sunveer Singh and the GRASS Development Team Licence: This program is free software under the GNU General Public - License (>=v2). Read the file COPYING that comes with GRASS - for details. + License (>=v2). Read the file COPYING that comes with GRASS + for details. """ from grass.gunittest.case import TestCase diff --git a/raster/r.resamp.filter/benchmark/benchmark_r_resamp_filter.py b/raster/r.resamp.filter/benchmark/benchmark_r_resamp_filter.py index 8c7799e74fc..9ec46132c81 100644 --- a/raster/r.resamp.filter/benchmark/benchmark_r_resamp_filter.py +++ b/raster/r.resamp.filter/benchmark/benchmark_r_resamp_filter.py @@ -40,8 +40,7 @@ def benchmark(size, label, results): overwrite=True, ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=8, repeat=3)) - Module("g.remove", quiet=True, flags="f", type="raster", name=reference) - Module("g.remove", quiet=True, flags="f", type="raster", name=output) + Module("g.remove", quiet=True, flags="f", type="raster", name=(reference, output)) def generate_map(rows, cols, fname): diff --git a/raster/r.resamp.interp/benchmark/benchmark_r_resamp_interp.py b/raster/r.resamp.interp/benchmark/benchmark_r_resamp_interp.py index 20196bc44bc..616dc045aa3 100644 --- a/raster/r.resamp.interp/benchmark/benchmark_r_resamp_interp.py +++ b/raster/r.resamp.interp/benchmark/benchmark_r_resamp_interp.py @@ -38,8 +38,7 @@ def benchmark(size, label, results): overwrite=True, ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=16, repeat=3)) - Module("g.remove", quiet=True, flags="f", type="raster", name=reference) - Module("g.remove", quiet=True, flags="f", type="raster", name=output) + Module("g.remove", quiet=True, flags="f", type="raster", name=(reference, output)) def generate_map(rows, cols, fname): diff --git a/raster/r.resamp.interp/r.resamp.interp.html b/raster/r.resamp.interp/r.resamp.interp.html index e4b77ac0d34..3c3eecc68a9 100644 --- a/raster/r.resamp.interp/r.resamp.interp.html +++ b/raster/r.resamp.interp/r.resamp.interp.html @@ -28,7 +28,7 @@

NOTES

Note that for bilinear, bicubic and lanczos interpolation, cells of the output raster that cannot be bounded by the appropriate number of input cell centers are set to NULL (NULL propagation). This could occur -due to the input cells being outside the current region, being NULL or MASKed. +due to the input cells being outside the current region, being NULL or masked.

For longitude-latitude coordinate reference systems, diff --git a/raster/r.series/benchmark/benchmark_r_series.py b/raster/r.series/benchmark/benchmark_r_series.py index ecf4432f2dd..72c9c3bd23c 100644 --- a/raster/r.series/benchmark/benchmark_r_series.py +++ b/raster/r.series/benchmark/benchmark_r_series.py @@ -37,8 +37,7 @@ def benchmark(size, label, results): overwrite=True, ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=16, repeat=3)) - Module("g.remove", quiet=True, flags="f", type="raster", name=reference) - Module("g.remove", quiet=True, flags="f", type="raster", name=output) + Module("g.remove", quiet=True, flags="f", type="raster", name=(reference, output)) def generate_map(rows, cols, fname): diff --git a/raster/r.series/testsuite/test_r_series.py b/raster/r.series/testsuite/test_r_series.py index 93bfd129242..e067a26e1da 100644 --- a/raster/r.series/testsuite/test_r_series.py +++ b/raster/r.series/testsuite/test_r_series.py @@ -18,13 +18,13 @@ def setUpClass(cls): call_module("r.mapcalc", expression=f"{cls.sum_mapcalc} = {cls.elevation} * 4") @classmethod - def tearDownClass(self): - self.del_temp_region() + def tearDownClass(cls): + cls.del_temp_region() call_module( "g.remove", flags="f", type_="raster", - name=self.sum_mapcalc, + name=cls.sum_mapcalc, ) def tearDown(self): diff --git a/raster/r.sim/r.sim.sediment/main.c b/raster/r.sim/r.sim.sediment/main.c index dd082fe0f81..d0197c2abb1 100644 --- a/raster/r.sim/r.sim.sediment/main.c +++ b/raster/r.sim/r.sim.sediment/main.c @@ -417,6 +417,8 @@ int main(int argc, char *argv[]) G_message(_("Using metric conversion factor %f, step=%f"), wp.conv, wp.step); + wp.observation = parm.observation->answer; + wp.logfile = parm.logfile->answer; init_library_globals(&wp); if ((wp.tc == NULL) && (wp.et == NULL) && (wp.conc == NULL) && diff --git a/raster/r.sim/r.sim.water/main.c b/raster/r.sim/r.sim.water/main.c index 61f128f5504..209be3e33d9 100644 --- a/raster/r.sim/r.sim.water/main.c +++ b/raster/r.sim/r.sim.water/main.c @@ -220,7 +220,7 @@ int main(int argc, char *argv[]) parm.logfile->required = NO; parm.logfile->description = _("Name for sampling points output text file. For each observation " - "vector point the time series of sediment transport is stored."); + "vector point the time series of water discharge is stored."); parm.logfile->guisection = _("Output"); parm.nwalk = G_define_option(); @@ -525,6 +525,8 @@ int main(int argc, char *argv[]) G_message(_("Using metric conversion factor %f, step=%f"), wp.conv, wp.step); + wp.observation = parm.observation->answer; + wp.logfile = parm.logfile->answer; init_library_globals(&wp); if ((wp.depth == NULL) && (wp.disch == NULL) && (wp.err == NULL)) diff --git a/raster/r.sim/r.sim.water/testsuite/data/depth_complex.pack b/raster/r.sim/r.sim.water/testsuite/data/depth_complex.pack new file mode 100644 index 00000000000..60f9de2be64 Binary files /dev/null and b/raster/r.sim/r.sim.water/testsuite/data/depth_complex.pack differ diff --git a/raster/r.sim/r.sim.water/testsuite/data/depth_default.pack b/raster/r.sim/r.sim.water/testsuite/data/depth_default.pack new file mode 100644 index 00000000000..579863ecca9 Binary files /dev/null and b/raster/r.sim/r.sim.water/testsuite/data/depth_default.pack differ diff --git a/raster/r.sim/r.sim.water/testsuite/data/discharge_complex.pack b/raster/r.sim/r.sim.water/testsuite/data/discharge_complex.pack new file mode 100644 index 00000000000..01810de0cb6 Binary files /dev/null and b/raster/r.sim/r.sim.water/testsuite/data/discharge_complex.pack differ diff --git a/raster/r.sim/r.sim.water/testsuite/data/discharge_default.pack b/raster/r.sim/r.sim.water/testsuite/data/discharge_default.pack new file mode 100644 index 00000000000..914eabcda47 Binary files /dev/null and b/raster/r.sim/r.sim.water/testsuite/data/discharge_default.pack differ diff --git a/raster/r.sim/r.sim.water/testsuite/test_r_sim_water.py b/raster/r.sim/r.sim.water/testsuite/test_r_sim_water.py new file mode 100644 index 00000000000..44ea662276a --- /dev/null +++ b/raster/r.sim/r.sim.water/testsuite/test_r_sim_water.py @@ -0,0 +1,201 @@ +import unittest + +from grass.gunittest.case import TestCase + + +class TestRSimWater(TestCase): + """Test r.sim.water""" + + # Set up the necessary raster maps for testing + elevation = "elevation" + dx = "tmp_dx" + dy = "tmp_dy" + depth = "tmp_depth" + discharge = "tmp_discharge" + rain = "tmp_rain" + mannings = "tmp_mannings" + infil = "tmp_infil" + reference_depth_default = "depth_default" + reference_discharge_default = "discharge_default" + reference_depth_complex = "depth_complex" + reference_discharge_complex = "discharge_complex" + + @classmethod + def setUpClass(cls): + """Set up region, create necessary data""" + cls.runModule("g.region", n=224000, s=223000, e=637000, w=636000, res=10) + cls.runModule("r.slope.aspect", elevation=cls.elevation, dx=cls.dx, dy=cls.dy) + cls.runModule( + "r.unpack", + input="data/depth_default.pack", + output=cls.reference_depth_default, + ) + cls.runModule( + "r.unpack", + input="data/discharge_default.pack", + output=cls.reference_discharge_default, + ) + cls.runModule( + "r.random.surface", output=cls.rain, distance=200, seed=1, high=100 + ) + cls.runModule( + "r.mapcalc", + expression=f"{cls.mannings} = if (x() > 636500 && y() > 223500, 0.3, 0.01)", + ) + cls.runModule( + "r.mapcalc", + expression=f"{cls.infil} = if (x() < 636500 && y() > 223500, 0.001, 0)", + ) + cls.runModule( + "r.unpack", + input="data/depth_complex.pack", + output=cls.reference_depth_complex, + ) + cls.runModule( + "r.unpack", + input="data/discharge_complex.pack", + output=cls.reference_discharge_complex, + ) + + @classmethod + def tearDownClass(cls): + """Clean up test environment""" + cls.runModule( + "g.remove", + flags="f", + type="raster", + name=[ + cls.elevation, + cls.dx, + cls.dy, + cls.reference_depth_default, + cls.reference_discharge_default, + cls.rain, + cls.mannings, + cls.infil, + cls.reference_depth_complex, + cls.reference_discharge_complex, + ], + ) + + def tearDown(self): + """Clean up test environment""" + self.runModule( + "g.remove", + flags="f", + type="raster", + pattern=f"{self.depth}*,{self.discharge}*", + ) + + def test_default(self): + """Test r.sim.water execution with defaults""" + # Run the r.sim.water simulation + self.assertModule( + "r.sim.water", + elevation=self.elevation, + dx=self.dx, + dy=self.dy, + depth=self.depth, + discharge=self.discharge, + random_seed=1, + ) + + # Assert that the output rasters exist + self.assertRasterExists(self.depth) + self.assertRasterExists(self.discharge) + # Assert that the output rasters are the same + self.assertRastersEqual( + self.depth, reference=self.reference_depth_default, precision="0.000001" + ) + self.assertRastersEqual( + self.discharge, + reference=self.reference_discharge_default, + precision="0.000001", + ) + + def test_complex(self): + """Test r.sim.water execution with more complex inputs""" + # Run the r.sim.water simulation + self.assertModule( + "r.sim.water", + flags="t", + elevation=self.elevation, + dx=self.dx, + dy=self.dy, + rain=self.rain, + man=self.mannings, + infil=self.infil, + depth=self.depth, + discharge=self.discharge, + niterations=15, + output_step=5, + diffusion_coeff=0.9, + hmax=0.25, + halpha=3.9, + hbeta=0.6, + random_seed=1, + ) + + # Assert that the output rasters exist + self.assertRasterExists(f"{self.depth}.05") + self.assertRasterExists(f"{self.depth}.10") + self.assertRasterExists(f"{self.depth}.15") + # Assert that the output rasters are the same + self.assertRastersEqual( + f"{self.depth}.15", + reference=self.reference_depth_complex, + precision="0.000001", + ) + self.assertRastersEqual( + f"{self.discharge}.15", + reference=self.reference_discharge_complex, + precision="0.000001", + ) + + +@unittest.skip("runs too long") +class TestRSimWaterLarge(TestCase): + """Test r.sim.water with large region""" + + # Set up the necessary raster maps for testing + elevation = "elevation" + dx = "tmp_dx" + dy = "tmp_dy" + depth = "tmp_depth" + + @classmethod + def setUpClass(cls): + """Set up region, create necessary data""" + cls.runModule("g.region", raster=cls.elevation) + cls.runModule("r.slope.aspect", elevation=cls.elevation, dx=cls.dx, dy=cls.dy) + + @classmethod + def tearDownClass(cls): + """Clean up test environment""" + cls.runModule( + "g.remove", + flags="f", + type="raster", + name=[cls.elevation, cls.dx, cls.dy, cls.depth], + ) + + def test_default(self): + """Test r.sim.water execution with defaults""" + # Run the r.sim.water simulation + self.assertModule( + "r.sim.water", + elevation=self.elevation, + dx=self.dx, + dy=self.dy, + depth=self.depth, + random_seed=1, + ) + self.assertRasterFitsUnivar( + self.depth, reference="sum=30364.327529", precision=1e-6 + ) + + +if __name__ == "__main__": + from grass.gunittest.main import test + + test() diff --git a/raster/r.sim/simlib/hydro.c b/raster/r.sim/simlib/hydro.c index 74145ec9bff..eb7649763e9 100644 --- a/raster/r.sim/simlib/hydro.c +++ b/raster/r.sim/simlib/hydro.c @@ -151,9 +151,6 @@ void main_loop(void) maxwa = maxwa / nblock; } - /* Create the observation points */ - create_observation_points(); - G_debug(2, " maxwa, nblock %d %d", maxwa, nblock); for (iblock = 1; iblock <= nblock; iblock++) { diff --git a/raster/r.slope.aspect/benchmark/benchmark_r_slope_aspect.py b/raster/r.slope.aspect/benchmark/benchmark_r_slope_aspect.py index 220b813e323..522a938f26c 100644 --- a/raster/r.slope.aspect/benchmark/benchmark_r_slope_aspect.py +++ b/raster/r.slope.aspect/benchmark/benchmark_r_slope_aspect.py @@ -42,11 +42,13 @@ def benchmark(size, label, results): overwrite=True, ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=16, repeat=3)) - Module("g.remove", quiet=True, flags="f", type="raster", name=reference) - Module("g.remove", quiet=True, flags="f", type="raster", name=slope) - Module("g.remove", quiet=True, flags="f", type="raster", name=aspect) - Module("g.remove", quiet=True, flags="f", type="raster", name=pcurv) - Module("g.remove", quiet=True, flags="f", type="raster", name=tcurv) + Module( + "g.remove", + quiet=True, + flags="f", + type="raster", + name=(reference, slope, aspect, pcurv, tcurv), + ) def generate_map(rows, cols, fname): diff --git a/raster/r.slope.aspect/benchmark/benchmark_r_slope_aspect_memory.py b/raster/r.slope.aspect/benchmark/benchmark_r_slope_aspect_memory.py index c8f5ac09f00..31824233bbc 100644 --- a/raster/r.slope.aspect/benchmark/benchmark_r_slope_aspect_memory.py +++ b/raster/r.slope.aspect/benchmark/benchmark_r_slope_aspect_memory.py @@ -46,10 +46,13 @@ def benchmark(memory, label, results, reference): ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=20, repeat=10)) - Module("g.remove", quiet=True, flags="f", type="raster", name=slope) - Module("g.remove", quiet=True, flags="f", type="raster", name=aspect) - Module("g.remove", quiet=True, flags="f", type="raster", name=pcurv) - Module("g.remove", quiet=True, flags="f", type="raster", name=tcurv) + Module( + "g.remove", + quiet=True, + flags="f", + type="raster", + name=(slope, aspect, pcurv, tcurv), + ) def generate_map(rows, cols, fname): diff --git a/raster/r.stream.extract/r.stream.extract.html b/raster/r.stream.extract/r.stream.extract.html index e1c427599e8..22877936c93 100644 --- a/raster/r.stream.extract/r.stream.extract.html +++ b/raster/r.stream.extract/r.stream.extract.html @@ -126,7 +126,9 @@

Defining a region of interest

The stream extraction procedure can be restricted to a certain region of interest, e.g. a subbasin, by setting the computational region with -g.region and/or creating a MASK. Such region of interest should +g.region and/or creating a mask +with r.mask. +Such region of interest should be a complete catchment area, complete in the sense that the complete area upstream of an outlet point is included and buffered with at least one cell. diff --git a/raster/r.terraflow/fill.cpp b/raster/r.terraflow/fill.cpp index ab1629a3b7f..c7b54cd0a47 100644 --- a/raster/r.terraflow/fill.cpp +++ b/raster/r.terraflow/fill.cpp @@ -518,7 +518,7 @@ void assignFinalDirections(AMI_STREAM *statstr, continue; } } -}; +} /* ********************************************************************** */ class directionElevationMerger { diff --git a/raster/r.terraflow/genericWindow.cpp b/raster/r.terraflow/genericWindow.cpp index 92b0852cd71..9b114773e2a 100644 --- a/raster/r.terraflow/genericWindow.cpp +++ b/raster/r.terraflow/genericWindow.cpp @@ -36,4 +36,4 @@ void fillPit(ElevationWindow &win) if (win.get(4) < min) { win.set(4, min); } -}; +} diff --git a/raster/r.terraflow/sweep.cpp b/raster/r.terraflow/sweep.cpp index 2f7fba8bb13..c98b844b016 100644 --- a/raster/r.terraflow/sweep.cpp +++ b/raster/r.terraflow/sweep.cpp @@ -65,7 +65,7 @@ sweepOutput::sweepOutput() #ifdef OUTPUT_TCI tci = (tci_type)nodataType::ELEVATION_NODATA; #endif -}; +} /* ------------------------------------------------------------ */ /* computes output parameters of cell (i,j) given the flow value, the diff --git a/raster/r.terraflow/testsuite/test_r_terraflow.py b/raster/r.terraflow/testsuite/test_r_terraflow.py index 62ee0eee15d..4085151a337 100644 --- a/raster/r.terraflow/testsuite/test_r_terraflow.py +++ b/raster/r.terraflow/testsuite/test_r_terraflow.py @@ -23,25 +23,25 @@ def tearDownClass(cls): """!Remove the temporary region""" cls.del_temp_region() - def setUp(cls): + def setUp(self): """Create input data for steady state groundwater flow computation""" - if not os.path.exists(cls.testdir): - os.mkdir(cls.testdir) + if not os.path.exists(self.testdir): + os.mkdir(self.testdir) - def test_univar_mfd(cls): + def test_univar_mfd(self): # compute a steady state groundwater flow - cls.assertModule( + self.assertModule( "r.terraflow", overwrite=True, verbose=True, - elevation=cls.elevation, + elevation=self.elevation, filled="terra_flooded", direction="terra_flowdir", swatershed="terra_sink", accumulation="terra_flowaccum", tci="terra_tci", - directory=cls.testdir, - stats=cls.teststats, + directory=self.testdir, + stats=self.teststats, ) # Output of r.univar -g @@ -111,16 +111,16 @@ def test_univar_mfd(cls): sum=8341670.75914752""" # cls.assertRasterFitsUnivar(raster="terra_flooded", reference=terra_flooded_univar, precision=3) - cls.assertRasterFitsUnivar( + self.assertRasterFitsUnivar( raster="terra_flowdir", reference=terra_flowdir_univar, precision=3 ) - cls.assertRasterFitsUnivar( + self.assertRasterFitsUnivar( raster="terra_sink", reference=terra_sink_univar, precision=3 ) - cls.assertRasterFitsUnivar( + self.assertRasterFitsUnivar( raster="terra_flowaccum", reference=terra_flowaccum_univar, precision=3 ) - cls.assertRasterFitsUnivar( + self.assertRasterFitsUnivar( raster="terra_tci", reference=terra_tci_univar, precision=3 ) diff --git a/raster/r.terraflow/weightWindow.cpp b/raster/r.terraflow/weightWindow.cpp index ccf55a82ca9..760dccc6b5c 100644 --- a/raster/r.terraflow/weightWindow.cpp +++ b/raster/r.terraflow/weightWindow.cpp @@ -229,7 +229,7 @@ void weightWindow::compute(const dimension_type i, const dimension_type j, cout << form("%3.2f ", weight.get(l)); cout << "]\n"; #endif -}; +} /* Find the dominant direction. Set corresponding weight to 1, and sets all other weights to 0. Set sumweight and sumcontour.*/ diff --git a/raster/r.tile/testsuite/test_r_tile.py b/raster/r.tile/testsuite/test_r_tile.py index f1dd3afcd3b..c2e62b60dd2 100644 --- a/raster/r.tile/testsuite/test_r_tile.py +++ b/raster/r.tile/testsuite/test_r_tile.py @@ -28,42 +28,20 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): cls.del_temp_region() - cls.runModule( - "g.remove", type="raster", flags="f", name=cls.output_prefix + "-000-000" - ) - cls.runModule( - "g.remove", type="raster", flags="f", name=cls.output_prefix + "-000-001" - ) - cls.runModule( - "g.remove", type="raster", flags="f", name=cls.output_prefix + "-001-000" - ) - cls.runModule( - "g.remove", type="raster", flags="f", name=cls.output_prefix + "-001-001" - ) - - cls.runModule( - "g.remove", - type="raster", - flags="f", - name=cls.output_prefix + "overlap" + "-000-000", - ) - cls.runModule( - "g.remove", - type="raster", - flags="f", - name=cls.output_prefix + "overlap" + "-000-001", - ) - cls.runModule( - "g.remove", - type="raster", - flags="f", - name=cls.output_prefix + "overlap" + "-001-000", - ) cls.runModule( "g.remove", type="raster", flags="f", - name=cls.output_prefix + "overlap" + "-001-001", + name=( + cls.output_prefix + "-000-000", + cls.output_prefix + "-000-001", + cls.output_prefix + "-001-000", + cls.output_prefix + "-001-001", + cls.output_prefix + "overlap" + "-000-000", + cls.output_prefix + "overlap" + "-000-001", + cls.output_prefix + "overlap" + "-001-000", + cls.output_prefix + "overlap" + "-001-001", + ), ) def test_raster_tile(self): diff --git a/raster/r.tile/testsuite/testrt.py b/raster/r.tile/testsuite/testrt.py index 6cb5a91c296..a26ff967d33 100644 --- a/raster/r.tile/testsuite/testrt.py +++ b/raster/r.tile/testsuite/testrt.py @@ -5,8 +5,8 @@ Author: Sunveer Singh, Google Code-in 2018 Copyright: (C) 2018 by Sunveer Singh and the GRASS Development Team Licence: This program is free software under the GNU General Public - License (>=v2). Read the file COPYING that comes with GRASS - for details. + License (>=v2). Read the file COPYING that comes with GRASS + for details. """ from grass.gunittest.case import TestCase diff --git a/raster/r.to.vect/testsuite/test_r_to_vect.py b/raster/r.to.vect/testsuite/test_r_to_vect.py index fbd27af6a33..7826bf44084 100644 --- a/raster/r.to.vect/testsuite/test_r_to_vect.py +++ b/raster/r.to.vect/testsuite/test_r_to_vect.py @@ -28,8 +28,8 @@ def setUpClass(cls): def tearDownClass(cls): cls.del_temp_region() - def tearDown(cls): - cls.runModule("g.remove", type="vector", flags="f", name=cls.output) + def tearDown(self): + self.runModule("g.remove", type="vector", flags="f", name=self.output) def test_flags(self): """Testing flag s""" diff --git a/raster/r.topidx/r.topidx.html b/raster/r.topidx/r.topidx.html index 56dd001f20a..75938e6a231 100644 --- a/raster/r.topidx/r.topidx.html +++ b/raster/r.topidx/r.topidx.html @@ -10,12 +10,14 @@

DESCRIPTION

the local surface topographic slope (delta vertical) / (delta horizontal).
-

Input maps may have NULL values. For example, if you have a MASK for a -watershed (basin map from r.water.outlet), the following command will -create a masked elevation map (belev): +

Input maps may have NULL values. For example, if you have a raster mask set +for a watershed (using basin map from r.water.outlet), the following +command will create a masked elevation map (belev): +

 r.mapcalc "belev = if(isnull(basin), basin, elev)"
 
+

r.stats -Anc prints out averaged statistics for topographic index. diff --git a/raster/r.univar/benchmark/benchmark_r_univar.py b/raster/r.univar/benchmark/benchmark_r_univar.py index 507e6b4b23e..58d607645c7 100644 --- a/raster/r.univar/benchmark/benchmark_r_univar.py +++ b/raster/r.univar/benchmark/benchmark_r_univar.py @@ -35,8 +35,7 @@ def benchmark(size, label, results): overwrite=True, ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=16, repeat=3)) - Module("g.remove", quiet=True, flags="f", type="raster", name=reference) - Module("g.remove", quiet=True, flags="f", type="raster", name=output) + Module("g.remove", quiet=True, flags="f", type="raster", name=(reference, output)) def generate_map(rows, cols, fname): diff --git a/raster/r.univar/r.univar.html b/raster/r.univar/r.univar.html index 5220bc64a44..debde03a1d6 100644 --- a/raster/r.univar/r.univar.html +++ b/raster/r.univar/r.univar.html @@ -53,7 +53,7 @@

PERFORMANCE

r.univar supports parallel processing using OpenMP. The user can specify the number of threads to be used with the nprocs parameter. -However, parallelization is disabled when the MASK is set. +However, parallelization is disabled when the raster mask is set.

Due to the differences in summation order, users may encounter small floating points diff --git a/raster/r.univar/testsuite/test_r_univar.py b/raster/r.univar/testsuite/test_r_univar.py index de28f0753d5..c2ae6669df6 100644 --- a/raster/r.univar/testsuite/test_r_univar.py +++ b/raster/r.univar/testsuite/test_r_univar.py @@ -23,11 +23,12 @@ def tearDownClass(cls): cls.del_temp_region() def tearDown(self): - self.runModule("g.remove", flags="f", type="raster", name="map_a") - self.runModule("g.remove", flags="f", type="raster", name="map_b") - self.runModule("g.remove", flags="f", type="raster", name="map_negative") - self.runModule("g.remove", flags="f", type="raster", name="zone_map") - self.runModule("g.remove", flags="f", type="raster", name="zone_map_with_gap") + self.runModule( + "g.remove", + flags="f", + type="raster", + name="map_a,map_b,map_negative,zone_map,zone_map_with_gap", + ) def setUp(self): """Create input data""" diff --git a/raster/r.viewshed/testsuite/test_r_viewshed.py b/raster/r.viewshed/testsuite/test_r_viewshed.py index b6825c62018..2b136abd551 100644 --- a/raster/r.viewshed/testsuite/test_r_viewshed.py +++ b/raster/r.viewshed/testsuite/test_r_viewshed.py @@ -15,10 +15,10 @@ def setUpClass(cls): def tearDownClass(cls): cls.del_temp_region() - def tearDown(cls): + def tearDown(self): """Remove viewshed map after each test method""" # TODO: eventually, removing maps should be handled through testing framework functions - cls.runModule("g.remove", flags="f", type="raster", name=cls.viewshed) + self.runModule("g.remove", flags="f", type="raster", name=self.viewshed) def test_limits(self): """Test if results is in expected limits""" diff --git a/raster/r.what/testsuite/testrw.py b/raster/r.what/testsuite/testrw.py index 8184f534720..fdaf7eec98c 100644 --- a/raster/r.what/testsuite/testrw.py +++ b/raster/r.what/testsuite/testrw.py @@ -5,8 +5,8 @@ Author: Sunveer Singh, Google Code-in 2018 Copyright: (C) 2018 by Sunveer Singh and the GRASS Development Team Licence: This program is free software under the GNU General Public - License (>=v2). Read the file COPYING that comes with GRASS - for details. + License (>=v2). Read the file COPYING that comes with GRASS + for details. """ from grass.gunittest.case import TestCase diff --git a/raster3d/r3.in.lidar/main.c b/raster3d/r3.in.lidar/main.c index 6170a3f50ba..3cf91189d24 100644 --- a/raster3d/r3.in.lidar/main.c +++ b/raster3d/r3.in.lidar/main.c @@ -361,13 +361,12 @@ int main(int argc, char *argv[]) /* for the CRS info */ const char *projstr; - struct Cell_head current_region; - struct Cell_head file_region; - + struct Cell_head current_region = {0}; + struct Cell_head file_region = {0}; G_get_set_window(¤t_region); /* extent for all data */ - struct Cell_head data_region; + struct Cell_head data_region = {0}; long unsigned header_count = 0; int i; diff --git a/raster3d/r3.in.v5d/binio.c b/raster3d/r3.in.v5d/binio.c index f8582e97313..b47723a2929 100644 --- a/raster3d/r3.in.v5d/binio.c +++ b/raster3d/r3.in.v5d/binio.c @@ -147,7 +147,7 @@ static void if_to_c(long *t, const long *f) { if (*f != 0) { *t = (((*f & 0x8000000000000000) | - ((*f & 0x7f80000000000000) >> 7) + (16258 << 48)) | + ((*f & 0x7f80000000000000) >> 7) + (16258L << 48)) | (((*f & 0x007fffff00000000) >> 8) | (0x0000800000000000))); if ((*f << 1) == 0) *t = 0; @@ -160,7 +160,7 @@ static void if_to_c(long *t, const long *f) #define IF_TO_C(T, F) \ if (F != 0) { \ T = (((F & 0x8000000000000000) | \ - ((F & 0x7f80000000000000) >> 7) + (16258 << 48)) | \ + ((F & 0x7f80000000000000) >> 7) + (16258L << 48)) | \ (((F & 0x007fffff00000000) >> 8) | (0x0000800000000000))); \ if ((F << 1) == 0) \ T = 0; \ diff --git a/raster3d/r3.null/testsuite/test.r3.null.sh b/raster3d/r3.null/testsuite/test.r3.null.sh index 1cf93110b24..d6a6ec35810 100755 --- a/raster3d/r3.null/testsuite/test.r3.null.sh +++ b/raster3d/r3.null/testsuite/test.r3.null.sh @@ -56,14 +56,7 @@ diff data/test_volume_double_null_1.ref test_volume_double_null_1.txt diff data/test_volume_double_null_2.ref test_volume_double_null_2.txt # Cleanup -g.remove -f type=raster_3d name=test_volume_float_1 -g.remove -f type=raster_3d name=test_volume_float_2 -g.remove -f type=raster_3d name=test_volume_float_null_1 -g.remove -f type=raster_3d name=test_volume_float_null_2 -g.remove -f type=raster_3d name=test_volume_double_1 -g.remove -f type=raster_3d name=test_volume_double_2 -g.remove -f type=raster_3d name=test_volume_double_null_1 -g.remove -f type=raster_3d name=test_volume_double_null_2 +g.remove -f type=raster_3d name=test_volume_float_1,test_volume_float_2,test_volume_float_null_1,test_volume_float_null_2,test_volume_double_1,test_volume_double_2,test_volume_double_null_1,test_volume_double_null_2 rm test_volume_float_1.txt rm test_volume_float_2.txt rm test_volume_float_null_1.txt diff --git a/raster3d/r3.showdspf/main_ogl.c b/raster3d/r3.showdspf/main_ogl.c index 55b3097e2cf..da6e90a3721 100644 --- a/raster3d/r3.showdspf/main_ogl.c +++ b/raster3d/r3.showdspf/main_ogl.c @@ -1159,7 +1159,7 @@ void do__draw(file_info *Headp, struct dspec *D_spec) fdraw_polys(D_spec); break; case 2: - case3: + case 3: gdraw_polys(D_spec); break; } diff --git a/raster3d/raster3dintro.html b/raster3d/raster3dintro.html index ac476b974b2..b51748b4dd8 100644 --- a/raster3d/raster3dintro.html +++ b/raster3d/raster3dintro.html @@ -115,7 +115,7 @@

Conversion from 2D raster maps

2D rasters are considered as slices in this case and merged into one 3D raster map (r.to.rast3). -

3D region settings and 3D MASK

+

3D region settings and 3D mask

GRASS GIS 3D raster map processing is always performed in the current 3D region settings (see g.region, -p3 flags), i.e. diff --git a/scripts/d.rast.edit/d.rast.edit.py b/scripts/d.rast.edit/d.rast.edit.py index 6519d10c7a1..102d7e447d8 100755 --- a/scripts/d.rast.edit/d.rast.edit.py +++ b/scripts/d.rast.edit/d.rast.edit.py @@ -77,6 +77,8 @@ import sys import math import atexit +from string import digits + import grass.script as gs from grass.script.setup import set_gui_path @@ -293,10 +295,7 @@ def paint_cell(self, dc, r, c): px, py = -dy, dx r, g, b, a = wx.Colour(fill).Get() - if r + g + b > 384: - line = "black" - else: - line = "white" + line = "black" if r + g + b > 384 else "white" dc.SetPen(wx.Pen(line)) dc.DrawLine(x0, y0, x1, y1) @@ -448,7 +447,7 @@ def OnClose(self, ev): def OnReturn(self, ev): self.app.brush = self.newval.GetValue() - if self.app.brush != "*" and self.app.brush.strip("0123456789") != "": + if self.app.brush != "*" and self.app.brush.strip(digits) != "": self.app.brush = "*" self.brush_update() @@ -645,28 +644,9 @@ def update_status(self, row, col): if self.angles: self.status["aspect"] = self.angles[row][col] - def force_color(self, val): - run("g.region", rows=1, cols=1) - run("r.mapcalc", expression="%s = %d" % (self.tempmap, val)) - run("r.colors", map=self.tempmap, rast=self.inmap) - run("r.out.ppm", input=self.tempmap, out=self.tempfile) - run("g.remove", flags="f", type="raster", name=self.tempmap) - - tempimg = wx.Image(self.tempfile) - gs.try_remove(self.tempfile) - - rgb = tempimg.get(0, 0) - color = "#%02x%02x%02x" % rgb - self.colors[val] = color - tempimg.delete() - def get_color(self, val): if val not in self.colors: - try: - self.force_color(val) - except: - self.colors[val] = "#ffffff" - + self.colors[val] = "#ffffff" return self.colors[val] def refresh_canvas(self): diff --git a/scripts/d.rast.leg/d.rast.leg.py b/scripts/d.rast.leg/d.rast.leg.py index 85cb423f9e9..74576113577 100755 --- a/scripts/d.rast.leg/d.rast.leg.py +++ b/scripts/d.rast.leg/d.rast.leg.py @@ -132,16 +132,10 @@ def main(): if not nlines: nlines = None - if rast: - lmap = rast - else: - lmap = map + lmap = rast or map kv = gs.raster_info(map=lmap) - if kv["datatype"] == "CELL": - leg_at = None - else: - leg_at = "%f,95,5,10" % VSpacing + leg_at = None if kv["datatype"] == "CELL" else "%f,95,5,10" % VSpacing # checking for histogram causes more problems than it solves # histfiledir = grass.find_file(lmap, 'cell_misc')['file'] diff --git a/scripts/db.droptable/db.droptable.py b/scripts/db.droptable/db.droptable.py index 571638748cf..39408221acc 100755 --- a/scripts/db.droptable/db.droptable.py +++ b/scripts/db.droptable/db.droptable.py @@ -56,14 +56,8 @@ def main(): gs.run_command("db.connect", flags="c", quiet=True) kv = gs.db_connection() - if options["database"]: - database = options["database"] - else: - database = kv["database"] - if options["driver"]: - driver = options["driver"] - else: - driver = kv["driver"] + database = options["database"] or kv["database"] + driver = options["driver"] or kv["driver"] # schema needed for PG? if force: diff --git a/scripts/db.out.ogr/db.out.ogr.py b/scripts/db.out.ogr/db.out.ogr.py index 4a840755b20..4fdfed23648 100755 --- a/scripts/db.out.ogr/db.out.ogr.py +++ b/scripts/db.out.ogr/db.out.ogr.py @@ -72,10 +72,7 @@ def main(): if format.lower() == "dbf": format = "ESRI_Shapefile" - if format.lower() == "csv": - olayer = basename(output, "csv") - else: - olayer = None + olayer = basename(output, "csv") if format.lower() == "csv" else None # is there a simpler way of testing for --overwrite? dbffile = input + ".dbf" diff --git a/scripts/db.univar/db.univar.py b/scripts/db.univar/db.univar.py index 97dfbdb7dbe..4654e3011a0 100755 --- a/scripts/db.univar/db.univar.py +++ b/scripts/db.univar/db.univar.py @@ -118,10 +118,7 @@ def main(): perc = [float(p) for p in perc.split(",")] if not output_format: - if shellstyle: - output_format = "shell" - else: - output_format = "plain" + output_format = "shell" if shellstyle else "plain" elif shellstyle: # This can be a message or warning in future versions. # In version 9, -g may be removed. diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index 194f498d49c..f14a742ca06 100644 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -1608,10 +1608,7 @@ def install_extension_win(name): source, url = resolve_source_code(url="{0}/{1}.zip".format(base_url, name)) # to hide non-error messages from subprocesses - if gs.verbosity() <= 2: - outdev = open(os.devnull, "w") - else: - outdev = sys.stdout + outdev = open(os.devnull, "w") if gs.verbosity() <= 2 else sys.stdout # download Addons ZIP file os.chdir(TMPDIR) # this is just to not leave something behind @@ -1961,10 +1958,7 @@ def install_extension_std_platforms(name, source, url, branch): path_to_src_code_message = _("Path to the source code:") # to hide non-error messages from subprocesses - if gs.verbosity() <= 2: - outdev = open(os.devnull, "w") - else: - outdev = sys.stdout + outdev = open(os.devnull, "w") if gs.verbosity() <= 2 else sys.stdout os.chdir(TMPDIR) # this is just to not leave something behind srcdir = os.path.join(TMPDIR, name) @@ -2578,10 +2572,7 @@ def resolve_known_host_service(url, name, branch): ) return None, None if match: - if not actual_start: - actual_start = match["url_start"] - else: - actual_start = "" + actual_start = match["url_start"] if not actual_start else "" if "branch" in match["url_end"]: suffix = match["url_end"].format( name=name, diff --git a/scripts/g.manual/g.manual.py b/scripts/g.manual/g.manual.py index f4ecf961076..345a6d1714a 100755 --- a/scripts/g.manual/g.manual.py +++ b/scripts/g.manual/g.manual.py @@ -132,10 +132,7 @@ def main(): elif flags["t"]: special = "topics" - if flags["m"]: - start = start_man - else: - start = start_browser + start = start_man if flags["m"] else start_browser entry = options["entry"] gisbase = os.environ["GISBASE"] diff --git a/scripts/i.in.spotvgt/i.in.spotvgt.py b/scripts/i.in.spotvgt/i.in.spotvgt.py index cd744d4c252..666f3721761 100755 --- a/scripts/i.in.spotvgt/i.in.spotvgt.py +++ b/scripts/i.in.spotvgt/i.in.spotvgt.py @@ -136,11 +136,7 @@ def main(): spotdir = os.path.dirname(infile) spotname = gs.basename(infile, "hdf") - - if rast: - name = rast - else: - name = spotname + name = rast or spotname if not gs.overwrite() and gs.find_file(name)["file"]: gs.fatal(_("<%s> already exists. Aborting.") % name) diff --git a/scripts/i.oif/i.oif.py b/scripts/i.oif/i.oif.py index 0dabbe7cb38..cb156675927 100755 --- a/scripts/i.oif/i.oif.py +++ b/scripts/i.oif/i.oif.py @@ -90,10 +90,7 @@ def main(): stddev[band] = float(kv["stddev"]) else: # run all bands in parallel - if "WORKERS" in os.environ: - workers = int(os.environ["WORKERS"]) - else: - workers = len(bands) + workers = int(os.environ["WORKERS"]) if "WORKERS" in os.environ else len(bands) proc = {} pout = {} @@ -142,10 +139,7 @@ def main(): _("The Optimum Index Factor analysis result (best combination shown first):") ) - if shell: - fmt = "%s,%s,%s:%.4f\n" - else: - fmt = "%s, %s, %s: %.4f\n" + fmt = "%s,%s,%s:%.4f\n" if shell else "%s, %s, %s: %.4f\n" if not output or output == "-": for v, p in oif: diff --git a/scripts/i.pansharpen/i.pansharpen.py b/scripts/i.pansharpen/i.pansharpen.py index bb271c07deb..e78581c8845 100755 --- a/scripts/i.pansharpen/i.pansharpen.py +++ b/scripts/i.pansharpen/i.pansharpen.py @@ -750,10 +750,7 @@ def matchhist(original, target, matched): ) for n in range(256): - if str(n) in stats_dict: - num_cells = stats_dict[str(n)] - else: - num_cells = 0 + num_cells = stats_dict.get(str(n), 0) cum_cells += num_cells diff --git a/scripts/i.spectral/i.spectral.py b/scripts/i.spectral/i.spectral.py index 171eac7359c..07efd8af31f 100755 --- a/scripts/i.spectral/i.spectral.py +++ b/scripts/i.spectral/i.spectral.py @@ -136,10 +136,7 @@ def draw_gnuplot(what, xlabels, output, img_format, coord_legend): cmd = [] for i, row in enumerate(what): - if not coord_legend: - title = "Pick " + str(i + 1) - else: - title = str(tuple(row[0:2])) + title = "Pick " + str(i + 1) if not coord_legend else str(tuple(row[0:2])) x_datafile = os.path.join(tmp_dir, "data_%d" % i) cmd.append(" '%s' title '%s'" % (x_datafile, title)) diff --git a/scripts/m.proj/m.proj.py b/scripts/m.proj/m.proj.py index 4540b3dcd0a..a018606fff9 100755 --- a/scripts/m.proj/m.proj.py +++ b/scripts/m.proj/m.proj.py @@ -236,14 +236,8 @@ def main(): gcore.debug("output file=[%s]" % outfile) # set up output style - if not decimal: - outfmt = ["-w5"] - else: - outfmt = ["-f", "%.8f"] - if not copy_input: - copyinp = [] - else: - copyinp = ["-E"] + outfmt = ["-w5"] if not decimal else ["-f", "%.8f"] + copyinp = [] if not copy_input else ["-E"] # do the conversion # Convert cs2cs DMS format to GRASS DMS format: diff --git a/scripts/r.buffer.lowmem/r.buffer.lowmem.py b/scripts/r.buffer.lowmem/r.buffer.lowmem.py index 2916ad95e7d..8866c21faeb 100755 --- a/scripts/r.buffer.lowmem/r.buffer.lowmem.py +++ b/scripts/r.buffer.lowmem/r.buffer.lowmem.py @@ -92,10 +92,7 @@ def main(): s = gs.read_command("g.proj", flags="j") kv = gs.parse_key_val(s) - if kv["+proj"] == "longlat": - metric = "geodesic" - else: - metric = "squared" + metric = "geodesic" if kv["+proj"] == "longlat" else "squared" gs.run_command( "r.grow.distance", input=input, metric=metric, distance=temp_dist, flags="m" diff --git a/scripts/r.import/testsuite/test_r_import.py b/scripts/r.import/testsuite/test_r_import.py index 0e4ec3839e5..075b3a363cf 100755 --- a/scripts/r.import/testsuite/test_r_import.py +++ b/scripts/r.import/testsuite/test_r_import.py @@ -13,9 +13,9 @@ class TestRImportRegion(TestCase): def setUpClass(cls): cls.runModule("g.region", raster="elevation") - def tearDown(cls): + def tearDown(self): """Remove imported map after each test method""" - cls.runModule("g.remove", flags="f", type="raster", name=cls.imported) + self.runModule("g.remove", flags="f", type="raster", name=self.imported) def test_import_estimate(self): """Test e flag""" diff --git a/scripts/r.in.aster/r.in.aster.py b/scripts/r.in.aster/r.in.aster.py index 24722293dde..626d27e7976 100755 --- a/scripts/r.in.aster/r.in.aster.py +++ b/scripts/r.in.aster/r.in.aster.py @@ -152,10 +152,7 @@ def main(): # Band 3b is not included ASTER L1T if proctype == "L1T": allbands.remove("3b") - if band == "all": - bandlist = allbands - else: - bandlist = band.split(",") + bandlist = allbands if band == "all" else band.split(",") # initialize datasets for L1A, L1B, L1T if proctype in {"L1A", "L1B", "L1T"}: diff --git a/scripts/r.in.srtm/r.in.srtm.py b/scripts/r.in.srtm/r.in.srtm.py index 1e4c585ac02..b9f51415ca0 100755 --- a/scripts/r.in.srtm/r.in.srtm.py +++ b/scripts/r.in.srtm/r.in.srtm.py @@ -177,10 +177,7 @@ def main(): infile = infile[:-4] (fdir, tile) = os.path.split(infile) - if not output: - tileout = tile - else: - tileout = output + tileout = output or tile if ".hgt" in input: suff = ".hgt" diff --git a/scripts/r.in.wms/wms_cap_parsers.py b/scripts/r.in.wms/wms_cap_parsers.py index 56b1d0ce48c..723a63f0b23 100644 --- a/scripts/r.in.wms/wms_cap_parsers.py +++ b/scripts/r.in.wms/wms_cap_parsers.py @@ -501,10 +501,7 @@ def _find(self, etreeElement, tag, ns=None): """!Find child element. If the element is not found it raises xml.etree.ElementTree.ParseError. """ - if not ns: - res = etreeElement.find(tag) - else: - res = etreeElement.find(ns(tag)) + res = etreeElement.find(tag) if not ns else etreeElement.find(ns(tag)) if res is None: raise ParseError( @@ -521,10 +518,7 @@ def _findall(self, etreeElement, tag, ns=None): """!Find all children element. If no element is found it raises xml.etree.ElementTree.ParseError. """ - if not ns: - res = etreeElement.findall(tag) - else: - res = etreeElement.findall(ns(tag)) + res = etreeElement.findall(tag) if not ns else etreeElement.findall(ns(tag)) if not res: raise ParseError( diff --git a/scripts/r.in.wms/wms_drv.py b/scripts/r.in.wms/wms_drv.py index 531112a1a6c..e1131918c4a 100644 --- a/scripts/r.in.wms/wms_drv.py +++ b/scripts/r.in.wms/wms_drv.py @@ -995,10 +995,7 @@ def _parseTilePattern(self, group_t_patts, bbox, region): res["y"] = (bbox["maxy"] - bbox["miny"]) / region["rows"] res["x"] = (bbox["maxx"] - bbox["minx"]) / region["cols"] - if res["x"] < res["y"]: - comp_res = "x" - else: - comp_res = "y" + comp_res = "x" if res["x"] < res["y"] else "y" t_res = {} best_patt = None diff --git a/scripts/r.mapcalc.simple/testsuite/test_rmapcalcsimple.py b/scripts/r.mapcalc.simple/testsuite/test_rmapcalcsimple.py index d400ccc7980..f133da22f7d 100644 --- a/scripts/r.mapcalc.simple/testsuite/test_rmapcalcsimple.py +++ b/scripts/r.mapcalc.simple/testsuite/test_rmapcalcsimple.py @@ -24,8 +24,9 @@ def setUpClass(cls): def tearDownClass(cls): map_output1 = "test1" map_output2 = "test2" - cls.runModule("g.remove", flags="f", type="raster", name=map_output1) - cls.runModule("g.remove", flags="f", type="raster", name=map_output2) + cls.runModule( + "g.remove", flags="f", type="raster", name=(map_output1, map_output2) + ) cls.del_temp_region() def test_rmapcalcsimple(self): diff --git a/scripts/r.out.xyz/r.out.xyz.html b/scripts/r.out.xyz/r.out.xyz.html index c9b2b6510be..e603c592e32 100644 --- a/scripts/r.out.xyz/r.out.xyz.html +++ b/scripts/r.out.xyz/r.out.xyz.html @@ -6,7 +6,7 @@

DESCRIPTION

NOTES

This module will by default not export x,y coordinates for raster cells -containing a NULL value. This includes cells masked by a raster MASK. +containing a NULL value. This includes cells masked by a raster mask. Using the flag -i also these raster cells will be included in the exported data.

diff --git a/scripts/r.out.xyz/r.out.xyz.py b/scripts/r.out.xyz/r.out.xyz.py index ae8e04de4d6..6df97f3c202 100755 --- a/scripts/r.out.xyz/r.out.xyz.py +++ b/scripts/r.out.xyz/r.out.xyz.py @@ -48,10 +48,7 @@ def main(): output = options["output"] donodata = flags["i"] - if donodata: - statsflags = "1g" - else: - statsflags = "1gn" + statsflags = "1g" if donodata else "1gn" parameters = { "flags": statsflags, "input": options["input"], diff --git a/scripts/r.reclass.area/r.reclass.area.py b/scripts/r.reclass.area/r.reclass.area.py index 5d6fc1292bc..01e474659bc 100755 --- a/scripts/r.reclass.area/r.reclass.area.py +++ b/scripts/r.reclass.area/r.reclass.area.py @@ -152,10 +152,7 @@ def reclass(inf, outf, lim, clump, diag, les): if len(f) < 5: continue hectares = float(f[4]) * 0.0001 - if lesser: - test = hectares <= limit - else: - test = hectares >= limit + test = hectares <= limit if lesser else hectares >= limit if test: rules += "%s = %s %s\n" % (f[0], f[2], f[3]) if rules: diff --git a/scripts/r.reclass.area/testsuite/test_r_reclass_area.py b/scripts/r.reclass.area/testsuite/test_r_reclass_area.py index c8009ce5a1d..96c922bda05 100644 --- a/scripts/r.reclass.area/testsuite/test_r_reclass_area.py +++ b/scripts/r.reclass.area/testsuite/test_r_reclass_area.py @@ -26,8 +26,12 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): cls.del_temp_region() - cls.runModule("g.remove", type="raster", flags="f", name=cls.output + "Greater") - cls.runModule("g.remove", type="raster", flags="f", name=cls.output + "Lesser") + cls.runModule( + "g.remove", + type="raster", + flags="f", + name=(cls.output + "Greater", cls.output + "Lesser"), + ) def test_reclassaeaGreater(self): """Testing r.reclass.area with greater""" diff --git a/scripts/r.reclass.area/testsuite/testrra.py b/scripts/r.reclass.area/testsuite/testrra.py index e6e2300875b..5e11bf6aecc 100644 --- a/scripts/r.reclass.area/testsuite/testrra.py +++ b/scripts/r.reclass.area/testsuite/testrra.py @@ -5,8 +5,8 @@ Author: Sunveer Singh, Google Code-in 2018 Copyright: (C) 2018 by Sunveer Singh and the GRASS Development Team Licence: This program is free software under the GNU General Public - License (>=v2). Read the file COPYING that comes with GRASS - for details. + License (>=v2). Read the file COPYING that comes with GRASS + for details. """ from grass.gunittest.case import TestCase @@ -26,8 +26,8 @@ def setUpClass(cls): def tearDownClass(cls): cls.del_temp_region() - def tearDown(cls): - cls.runModule("g.remove", type="raster", flags="f", name=cls.output) + def tearDown(self): + self.runModule("g.remove", type="raster", flags="f", name=self.output) def test_flag_c(self): """Testing flag c""" diff --git a/scripts/r.unpack/r.unpack.py b/scripts/r.unpack/r.unpack.py index 575d465ebb5..dd595b4722b 100644 --- a/scripts/r.unpack/r.unpack.py +++ b/scripts/r.unpack/r.unpack.py @@ -92,10 +92,7 @@ def main(): return 0 - if options["output"]: - map_name = options["output"] - else: - map_name = data_names[0].split("@")[0] + map_name = options["output"] or data_names[0].split("@")[0] gfile = grass.find_file(name=map_name, element="cell", mapset=".") if gfile["file"]: diff --git a/scripts/r3.in.xyz/r3.in.xyz.py b/scripts/r3.in.xyz/r3.in.xyz.py index b17d59506b7..5b2c8be4a65 100755 --- a/scripts/r3.in.xyz/r3.in.xyz.py +++ b/scripts/r3.in.xyz/r3.in.xyz.py @@ -226,10 +226,7 @@ def main(): addl_opts["flags"] = "i" if scan_only or shell_style: - if shell_style: - doShell = "g" - else: - doShell = "" + doShell = "g" if shell_style else "" grass.run_command( "r.in.xyz", flags="s" + doShell, @@ -243,10 +240,7 @@ def main(): ) sys.exit() - if dtype == "float": - data_type = "FCELL" - else: - data_type = "DCELL" + data_type = "FCELL" if dtype == "float" else "DCELL" region = grass.region(region3d=True) diff --git a/scripts/v.build.all/v.build.all.py b/scripts/v.build.all/v.build.all.py index 69b0837209f..8691ecddc32 100755 --- a/scripts/v.build.all/v.build.all.py +++ b/scripts/v.build.all/v.build.all.py @@ -31,10 +31,7 @@ def main(): vectors = grass.list_grouped("vect")[mapset] num_vectors = len(vectors) - if grass.verbosity() < 2: - quiet = True - else: - quiet = False + quiet = grass.verbosity() < 2 i = 1 for vect in vectors: diff --git a/scripts/v.centroids/v.centroids.html b/scripts/v.centroids/v.centroids.html index 2bebed24e97..b814b702bab 100644 --- a/scripts/v.centroids/v.centroids.html +++ b/scripts/v.centroids/v.centroids.html @@ -1,11 +1,13 @@

DESCRIPTION

-GRASS defines vector areas as composite entities consisting of a set of +In GRASS GIS, a centroid is a point within a closed ring of boundaries. +A vector area is defined as composite entity consisting of a set of closed boundaries and a centroid. The attribute information associated -with that area is linked to the centroid. The v.centroids module -adds centroids to closed boundaries in the input file and assigns a -category number to them. The starting value as well as the increment size -may be set using optional parameters. +with this area is linked to the centroid. + +The v.centroids module adds centroids to closed boundaries in +the input file and assigns a category number to them. The starting +value as well as the increment size may be set using optional parameters.

Multiple attributes may be linked to a single vector entity through numbered fields referred to as layers. Refer to v.category for more details, as v.centroids is simply a frontend to that diff --git a/scripts/v.db.reconnect.all/v.db.reconnect.all.py b/scripts/v.db.reconnect.all/v.db.reconnect.all.py index b42b8006673..9a9d46356a9 100755 --- a/scripts/v.db.reconnect.all/v.db.reconnect.all.py +++ b/scripts/v.db.reconnect.all/v.db.reconnect.all.py @@ -255,10 +255,7 @@ def main(): schema = "" table = schema_table - if new_schema: - new_schema_table = "%s.%s" % (new_schema, table) - else: - new_schema_table = table + new_schema_table = "%s.%s" % (new_schema, table) if new_schema else table gs.debug( "DATABASE = '%s' SCHEMA = '%s' TABLE = '%s' ->\n" diff --git a/scripts/v.dissolve/tests/conftest.py b/scripts/v.dissolve/tests/conftest.py index 9b069330488..e5e9a38818d 100644 --- a/scripts/v.dissolve/tests/conftest.py +++ b/scripts/v.dissolve/tests/conftest.py @@ -13,10 +13,7 @@ def updates_as_transaction(table, cat_column, column, column_quote, cats, values): """Create SQL statement for categories and values for a given column""" sql = ["BEGIN TRANSACTION"] - if column_quote: - quote = "'" - else: - quote = "" + quote = "'" if column_quote else "" for cat, value in zip(cats, values): sql.append( f"UPDATE {table} SET {column} = {quote}{value}{quote} " diff --git a/scripts/v.dissolve/tests/v_dissolve_aggregate_test.py b/scripts/v.dissolve/tests/v_dissolve_aggregate_test.py index 68ca178b864..90dc3de075e 100644 --- a/scripts/v.dissolve/tests/v_dissolve_aggregate_test.py +++ b/scripts/v.dissolve/tests/v_dissolve_aggregate_test.py @@ -101,10 +101,7 @@ def test_aggregate_column_result(dataset, backend): for stats_column in stats_columns: assert stats_column in columns column_info = columns[stats_column] - if stats_column.endswith("_n"): - correct_type = "integer" - else: - correct_type = "double precision" + correct_type = "integer" if stats_column.endswith("_n") else "double precision" assert ( columns[stats_column]["type"].lower() == correct_type ), f"{stats_column} has a wrong type" @@ -221,10 +218,7 @@ def test_sqlite_agg_accepted(dataset): for method, stats_column in zip(stats, expected_stats_columns): assert stats_column in columns column_info = columns[stats_column] - if method == "count": - correct_type = "integer" - else: - correct_type = "double precision" + correct_type = "integer" if method == "count" else "double precision" assert ( columns[stats_column]["type"].lower() == correct_type ), f"{stats_column} has a wrong type" diff --git a/scripts/v.dissolve/tests/v_dissolve_layers_test.py b/scripts/v.dissolve/tests/v_dissolve_layers_test.py index 702aa8f8496..f6986d35917 100644 --- a/scripts/v.dissolve/tests/v_dissolve_layers_test.py +++ b/scripts/v.dissolve/tests/v_dissolve_layers_test.py @@ -50,10 +50,7 @@ def test_layer_2(dataset_layer_2): for method, stats_column in zip(stats, expected_stats_columns): assert stats_column in columns column_info = columns[stats_column] - if method == "count": - correct_type = "integer" - else: - correct_type = "double precision" + correct_type = "integer" if method == "count" else "double precision" assert ( columns[stats_column]["type"].lower() == correct_type ), f"{stats_column} has a wrong type" diff --git a/scripts/v.import/testsuite/test_v_import.py b/scripts/v.import/testsuite/test_v_import.py index d8dd6b85887..90934bbb3e6 100755 --- a/scripts/v.import/testsuite/test_v_import.py +++ b/scripts/v.import/testsuite/test_v_import.py @@ -18,9 +18,9 @@ class TestVImport(TestCase): imported = "test_v_import_imported" - def tearDown(cls): + def tearDown(self): """Remove imported map after each test method""" - cls.runModule("g.remove", flags="f", type="vector", name=cls.imported) + self.runModule("g.remove", flags="f", type="vector", name=self.imported) def test_import_same_proj_gpkg(self): """Import GPKG in same proj, default params""" diff --git a/scripts/v.in.e00/v.in.e00.py b/scripts/v.in.e00/v.in.e00.py index 4d63636199f..4258fa3181a 100755 --- a/scripts/v.in.e00/v.in.e00.py +++ b/scripts/v.in.e00/v.in.e00.py @@ -86,10 +86,7 @@ def main(): ) merging = True - if vect: - name = vect - else: - name = e00name + name = vect or e00name # do import diff --git a/scripts/v.in.lines/v.in.lines.py b/scripts/v.in.lines/v.in.lines.py index 44112b297a3..35991988f6b 100755 --- a/scripts/v.in.lines/v.in.lines.py +++ b/scripts/v.in.lines/v.in.lines.py @@ -50,12 +50,7 @@ def main(): fs = separator(options["separator"]) threeD = flags["z"] - - if threeD: - do3D = "z" - else: - do3D = "" - + do3D = "z" if threeD else "" tmp = grass.tempfile() # set up input file diff --git a/scripts/v.in.mapgen/v.in.mapgen.py b/scripts/v.in.mapgen/v.in.mapgen.py index 404fb7ec6b2..efe7af1075d 100755 --- a/scripts/v.in.mapgen/v.in.mapgen.py +++ b/scripts/v.in.mapgen/v.in.mapgen.py @@ -73,18 +73,12 @@ def main(): if not os.path.isfile(infile): grass.fatal(_("Input file <%s> not found") % infile) - if output: - name = output - else: - name = "" + name = output or "" if threeD: matlab = True - if threeD: - do3D = "z" - else: - do3D = "" + do3D = "z" if threeD else "" tmp = grass.tempfile() diff --git a/scripts/v.pack/v.pack.py b/scripts/v.pack/v.pack.py index 4468c888bcb..e39d52dbafe 100755 --- a/scripts/v.pack/v.pack.py +++ b/scripts/v.pack/v.pack.py @@ -73,10 +73,7 @@ def main(): infile = infile.split("@")[0] # output name - if options["output"]: - outfile = options["output"] - else: - outfile = infile + ".pack" + outfile = options["output"] or infile + ".pack" # check if exists the output file if os.path.exists(outfile): 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 56acbce5964..01ae6d4a7d2 100644 --- a/scripts/v.rast.stats/testsuite/test_v_rast_stats.py +++ b/scripts/v.rast.stats/testsuite/test_v_rast_stats.py @@ -24,11 +24,12 @@ def tearDownClass(cls): cls.del_temp_region() def tearDown(self): - self.runModule("g.remove", flags="f", type="raster", name="map_a") - self.runModule("g.remove", flags="f", type="raster", name="map_b") - self.runModule("g.remove", flags="f", type="raster", name="zone_map") - self.runModule("g.remove", flags="f", type="raster", name="row_map") - self.runModule("g.remove", flags="f", type="raster", name="test_line") + self.runModule( + "g.remove", + flags="f", + type="raster", + name="map_a,map_b,zone_map,row_map,test_line", + ) def setUp(self): """Create input data""" diff --git a/scripts/v.rast.stats/v.rast.stats.py b/scripts/v.rast.stats/v.rast.stats.py index 803fc8691ad..4096aa49caf 100644 --- a/scripts/v.rast.stats/v.rast.stats.py +++ b/scripts/v.rast.stats/v.rast.stats.py @@ -116,10 +116,7 @@ def main(): # Get mapset of the vector vs = vector.split("@") - if len(vs) > 1: - vect_mapset = vs[1] - else: - vect_mapset = mapset + vect_mapset = vs[1] if len(vs) > 1 else mapset # does map exist in CURRENT mapset? if vect_mapset != mapset or not gs.find_file(vector, "vector", mapset)["file"]: @@ -373,10 +370,7 @@ def set_up_columns(vector, layer, percentile, colprefix, basecols, dbfdriver, c) perc = b if perc: # namespace is limited in DBF but the % value is important - if dbfdriver: - perccol = "per" + percentile - else: - perccol = "percentile_" + percentile + perccol = "per" + percentile if dbfdriver else "percentile_" + percentile percindex = basecols.index(perc) basecols[percindex] = perccol @@ -424,10 +418,7 @@ def set_up_columns(vector, layer, percentile, colprefix, basecols, dbfdriver, c) + _("Use -c flag to update values in this column.") ) else: - if i == "n": - coltype = "INTEGER" - else: - coltype = "DOUBLE PRECISION" + coltype = "INTEGER" if i == "n" else "DOUBLE PRECISION" addcols.append(currcolumn + " " + coltype) if addcols: diff --git a/scripts/v.report/v.report.py b/scripts/v.report/v.report.py index cec84582155..29d9d3c914e 100755 --- a/scripts/v.report/v.report.py +++ b/scripts/v.report/v.report.py @@ -91,10 +91,7 @@ def main(): isConnection = False colnames = ["cat"] - if option == "coor": - extracolnames = ["x", "y", "z"] - else: - extracolnames = [option] + extracolnames = ["x", "y", "z"] if option == "coor" else [option] if units == "percent": unitsp = "meters" diff --git a/scripts/v.unpack/v.unpack.py b/scripts/v.unpack/v.unpack.py index ea75d443b57..c3a7a55deb7 100644 --- a/scripts/v.unpack/v.unpack.py +++ b/scripts/v.unpack/v.unpack.py @@ -93,10 +93,7 @@ def main(): return 0 # set the output name - if options["output"]: - map_name = options["output"] - else: - map_name = data_name + map_name = options["output"] or data_name # grass env gisenv = grass.gisenv() @@ -233,19 +230,13 @@ def main(): # for each old connection for t in dbnlist: # it split the line of each connection, to found layer number and key - if len(t.split("|")) != 1: - values = t.split("|") - else: - values = t.split(" ") + values = t.split("|") if len(t.split("|")) != 1 else t.split(" ") from_table = values[1] layer = values[0].split("/")[0] # we need to take care about the table name in case of several layer if options["output"]: - if len(dbnlist) > 1: - to_table = "%s_%s" % (map_name, layer) - else: - to_table = map_name + to_table = "%s_%s" % (map_name, layer) if len(dbnlist) > 1 else map_name else: to_table = from_table diff --git a/scripts/v.what.strds/v.what.strds.py b/scripts/v.what.strds/v.what.strds.py index 0e1bf893f98..8be00f3536d 100644 --- a/scripts/v.what.strds/v.what.strds.py +++ b/scripts/v.what.strds/v.what.strds.py @@ -225,7 +225,7 @@ def main(): pymap = Vector(output) try: pymap.open("r") - except: + except Exception: dbif.close() gs.fatal(_("Unable to create vector map <%s>") % output) diff --git a/temporal/t.info/t.info.py b/temporal/t.info/t.info.py index 575446030a1..c04fe0c0a5f 100755 --- a/temporal/t.info/t.info.py +++ b/temporal/t.info/t.info.py @@ -103,11 +103,7 @@ def main(): if not system and not name: gs.fatal(_("Please specify %s=") % ("name")) - if name.find("@") >= 0: - id_ = name - else: - id_ = name + "@" + gs.gisenv()["MAPSET"] - + id_ = name if name.find("@") >= 0 else name + "@" + gs.gisenv()["MAPSET"] dataset = tgis.dataset_factory(type_, id_) if not dataset.is_in_db(dbif): diff --git a/temporal/t.list/t.list.py b/temporal/t.list/t.list.py index 52285d04b83..c41bcae4e22 100755 --- a/temporal/t.list/t.list.py +++ b/temporal/t.list/t.list.py @@ -123,10 +123,7 @@ def main(): outfile = open(outpath, "w") for ttype in temporal_type.split(","): - if ttype == "absolute": - time = "absolute time" - else: - time = "relative time" + time = "absolute time" if ttype == "absolute" else "relative time" stds_list = tgis.get_dataset_list(type, ttype, columns, where, order, dbif=dbif) diff --git a/temporal/t.rast.accdetect/t.rast.accdetect.py b/temporal/t.rast.accdetect/t.rast.accdetect.py index 1588b44d4b9..17433e06f3f 100644 --- a/temporal/t.rast.accdetect/t.rast.accdetect.py +++ b/temporal/t.rast.accdetect/t.rast.accdetect.py @@ -173,11 +173,7 @@ def main(): mapset = tgis.get_current_mapset() - if input.find("@") >= 0: - id = input - else: - id = input + "@" + mapset - + id = input if input.find("@") >= 0 else input + "@" + mapset input_strds = tgis.SpaceTimeRasterDataset(id) if not input_strds.is_in_db(): @@ -261,10 +257,7 @@ def main(): # The minimum threshold space time raster dataset minimum_strds = None if minimum: - if minimum.find("@") >= 0: - minimum_id = minimum - else: - minimum_id = minimum + "@" + mapset + minimum_id = minimum if minimum.find("@") >= 0 else minimum + "@" + mapset minimum_strds = tgis.SpaceTimeRasterDataset(minimum_id) if not minimum_strds.is_in_db(): @@ -282,10 +275,7 @@ def main(): # The maximum threshold space time raster dataset maximum_strds = None if maximum: - if maximum.find("@") >= 0: - maximum_id = maximum - else: - maximum_id = maximum + "@" + mapset + maximum_id = maximum if maximum.find("@") >= 0 else maximum + "@" + mapset maximum_strds = tgis.SpaceTimeRasterDataset(maximum_id) if not maximum_strds.is_in_db(): @@ -304,16 +294,10 @@ def main(): if input_strds.is_time_absolute(): start = tgis.string_to_datetime(start) - if stop: - stop = tgis.string_to_datetime(stop) - else: - stop = input_strds_end + stop = tgis.string_to_datetime(stop) if stop else input_strds_end else: start = int(start) - if stop: - stop = int(stop) - else: - stop = input_strds_end + stop = int(stop) if stop else input_strds_end if input_strds.is_time_absolute(): end = tgis.increment_datetime_by_string(start, cycle) @@ -365,10 +349,7 @@ def main(): if indicator: num_maps = len(input_maps) for i in range(num_maps): - if reverse: - map = input_maps[num_maps - i - 1] - else: - map = input_maps[i] + map = input_maps[num_maps - i - 1] if reverse else input_maps[i] if ( input_strds.get_temporal_type() == "absolute" @@ -637,19 +618,13 @@ def compute_occurrence( # Aggregate num_maps = len(input_maps) for i in range(num_maps): - if reverse: - map = input_maps[num_maps - i - 1] - else: - map = input_maps[i] + map = input_maps[num_maps - i - 1] if reverse else input_maps[i] # Compute the days since start input_start, input_end = map.get_temporal_extent_as_tuple() td = input_start - start - if map.is_time_absolute(): - days = tgis.time_delta_to_relative_time(td) - else: - days = td + days = tgis.time_delta_to_relative_time(td) if map.is_time_absolute() else td if input_strds.get_temporal_type() == "absolute" and tsuffix == "gran": suffix = tgis.create_suffix_from_datetime( diff --git a/temporal/t.rast.accdetect/testsuite/test_simple.py b/temporal/t.rast.accdetect/testsuite/test_simple.py index 522c13f93e4..d67537487d5 100644 --- a/temporal/t.rast.accdetect/testsuite/test_simple.py +++ b/temporal/t.rast.accdetect/testsuite/test_simple.py @@ -62,8 +62,7 @@ def tearDownClass(cls): def tearDown(self): """Remove generated data""" - self.runModule("t.remove", flags="df", type="strds", inputs="B") - self.runModule("t.remove", flags="df", type="strds", inputs="C") + self.runModule("t.remove", flags="df", type="strds", inputs="B,C") def test_simple(self): self.assertModule( diff --git a/temporal/t.rast.accumulate/t.rast.accumulate.py b/temporal/t.rast.accumulate/t.rast.accumulate.py index 6fe067e02bc..d254d21c110 100644 --- a/temporal/t.rast.accumulate/t.rast.accumulate.py +++ b/temporal/t.rast.accumulate/t.rast.accumulate.py @@ -192,11 +192,7 @@ def main(): mapset = tgis.get_current_mapset() - if input.find("@") >= 0: - id = input - else: - id = input + "@" + mapset - + id = input if input.find("@") >= 0 else input + "@" + mapset input_strds = tgis.SpaceTimeRasterDataset(id) if not input_strds.is_in_db(): @@ -205,10 +201,7 @@ def main(): input_strds.select(dbif) - if output.find("@") >= 0: - out_id = output - else: - out_id = output + "@" + mapset + out_id = output if output.find("@") >= 0 else output + "@" + mapset # The output space time raster dataset output_strds = tgis.SpaceTimeRasterDataset(out_id) @@ -244,11 +237,7 @@ def main(): # The lower threshold space time raster dataset if lower: - - if lower.find("@") >= 0: - lower_id = lower - else: - lower_id = lower + "@" + mapset + lower_id = lower if lower.find("@") >= 0 else lower + "@" + mapset lower_strds = tgis.SpaceTimeRasterDataset(lower_id) if not lower_strds.is_in_db(): @@ -271,11 +260,7 @@ def main(): _("The upper option works only in conjunction with the lower option") ) - if upper.find("@") >= 0: - upper_id = upper - else: - upper_id = upper + "@" + mapset - + upper_id = upper if upper.find("@") >= 0 else upper + "@" + mapset upper_strds = tgis.SpaceTimeRasterDataset(upper_id) if not upper_strds.is_in_db(): dbif.close() @@ -293,17 +278,11 @@ def main(): if input_strds.is_time_absolute(): start = tgis.string_to_datetime(start) - if stop: - stop = tgis.string_to_datetime(stop) - else: - stop = input_strds_end + stop = tgis.string_to_datetime(stop) if stop else input_strds_end start = tgis.adjust_datetime_to_granularity(start, granularity) else: start = int(start) - if stop: - stop = int(stop) - else: - stop = input_strds_end + stop = int(stop) if stop else input_strds_end if input_strds.is_time_absolute(): end = tgis.increment_datetime_by_string(start, cycle) @@ -371,10 +350,7 @@ def main(): num_maps = len(gran_list) for i in range(num_maps): - if reverse: - map = gran_list[num_maps - i - 1] - else: - map = gran_list[i] + map = gran_list[num_maps - i - 1] if reverse else gran_list[i] # Select input maps based on temporal topology relations input_maps = [] if map.get_equal(): diff --git a/temporal/t.rast.accumulate/testsuite/test_accumulation.py b/temporal/t.rast.accumulate/testsuite/test_accumulation.py index f5bf6dcf5d7..566c1971f99 100644 --- a/temporal/t.rast.accumulate/testsuite/test_accumulation.py +++ b/temporal/t.rast.accumulate/testsuite/test_accumulation.py @@ -95,9 +95,7 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): """Remove the temporary region""" - cls.runModule("t.remove", flags="df", type="strds", inputs="A") - cls.runModule("t.remove", flags="df", type="strds", inputs="Lower") - cls.runModule("t.remove", flags="df", type="strds", inputs="Upper") + cls.runModule("t.remove", flags="df", type="strds", inputs="A,Lower,Upper") cls.del_temp_region() def tearDown(self): diff --git a/temporal/t.rast.aggregate/t.rast.aggregate.py b/temporal/t.rast.aggregate/t.rast.aggregate.py index 609d6d19d38..0675407794a 100755 --- a/temporal/t.rast.aggregate/t.rast.aggregate.py +++ b/temporal/t.rast.aggregate/t.rast.aggregate.py @@ -218,10 +218,7 @@ def main(): dbif, gs.overwrite(), ) - if register_null: - register_null = False - else: - register_null = True + register_null = not register_null tgis.register_map_object_list( "rast", diff --git a/temporal/t.rast.series/testsuite/test_series.py b/temporal/t.rast.series/testsuite/test_series.py index 99fe69d4352..e936e10e0e7 100644 --- a/temporal/t.rast.series/testsuite/test_series.py +++ b/temporal/t.rast.series/testsuite/test_series.py @@ -61,11 +61,15 @@ def tearDownClass(cls): type="raster", maps="series_average,series_maximum,series_minimum,series_minimum_2", ) - cls.runModule("g.remove", flags="f", type="raster", name="series_average") - cls.runModule("g.remove", flags="f", type="raster", name="series_maximum") - cls.runModule("g.remove", flags="f", type="raster", name="series_minimum") - cls.runModule("g.remove", flags="f", type="raster", name="series_minimum_2") - cls.runModule("g.remove", flags="f", type="raster", name="series_quantile") + cls.runModule( + "g.remove", + flags="f", + type="raster", + name=( + "series_average,series_maximum" + + ",series_minimum,series_minimum_2,series_quantile" + ), + ) def test_time_stamp(self): self.assertModule( @@ -206,10 +210,12 @@ def tearDownClass(cls): type="raster", maps="series_average,series_maximum,series_minimum,series_minimum_2", ) - cls.runModule("g.remove", flags="f", type="raster", name="series_average") - cls.runModule("g.remove", flags="f", type="raster", name="series_maximum") - cls.runModule("g.remove", flags="f", type="raster", name="series_minimum") - cls.runModule("g.remove", flags="f", type="raster", name="series_minimum_2") + cls.runModule( + "g.remove", + flags="f", + type="raster", + name="series_average,series_maximum,series_minimum,series_minimum_2", + ) def test_average(self): self.assertModule( diff --git a/temporal/t.rast.to.rast3/t.rast.to.rast3.py b/temporal/t.rast.to.rast3/t.rast.to.rast3.py index 7776d7426ee..118e19549c6 100755 --- a/temporal/t.rast.to.rast3/t.rast.to.rast3.py +++ b/temporal/t.rast.to.rast3/t.rast.to.rast3.py @@ -183,10 +183,7 @@ def main(): gs.warning(_("%s failed to set units.") % "r3.support") # Register the space time voxel cube in the temporal GIS - if output.find("@") >= 0: - id = output - else: - id = output + "@" + mapset + id = output if output.find("@") >= 0 else output + "@" + mapset start, end = sp.get_temporal_extent_as_tuple() r3ds = tgis.Raster3DDataset(id) 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 dca870d6a98..17831b9b5b9 100644 --- a/temporal/t.rast.univar/testsuite/test_t_rast_univar.py +++ b/temporal/t.rast.univar/testsuite/test_t_rast_univar.py @@ -147,8 +147,7 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): """Remove the temporary region""" - cls.runModule("t.remove", flags="df", type="strds", inputs="A") - cls.runModule("t.remove", flags="df", type="strds", inputs="B") + cls.runModule("t.remove", flags="df", type="strds", inputs="A,B") cls.runModule("g.remove", flags="f", type="raster", name="zones") cls.del_temp_region() diff --git a/temporal/t.rast.what/t.rast.what.py b/temporal/t.rast.what/t.rast.what.py index b3cec73a538..24190a6a716 100755 --- a/temporal/t.rast.what/t.rast.what.py +++ b/temporal/t.rast.what/t.rast.what.py @@ -98,22 +98,6 @@ # % description: Use stdin as input and ignore coordinates and point option # %end -## Temporary disabled the r.what flags due to test issues -##%flag -##% key: f -##% description: Show the category labels of the grid cell(s) -##%end - -##%flag -##% key: r -##% description: Output color values as RRR:GGG:BBB -##%end - -##%flag -##% key: i -##% description: Output integer category values, not cell values -##%end - # %flag # % key: v # % description: Show the category for vector points map @@ -410,10 +394,7 @@ def one_point_per_row_output( for i in range(len(values)): start, end = map_list[i].get_temporal_extent_as_tuple() - if vcat: - cat_str = "{ca}{sep}".format(ca=cat, sep=separator) - else: - cat_str = "" + cat_str = "{ca}{sep}".format(ca=cat, sep=separator) if vcat else "" if site_input: coor_string = ( "%(x)10.10f%(sep)s%(y)10.10f%(sep)s%(site_name)s%(sep)s" @@ -480,10 +461,7 @@ def one_point_per_col_output( out_str = "start%(sep)send" % ({"sep": separator}) # Define different separator for coordinates and sites - if separator == ",": - coor_sep = ";" - else: - coor_sep = "," + coor_sep = ";" if separator == "," else "," for row in matrix: if vcat: @@ -517,10 +495,7 @@ def one_point_per_col_output( first = False - if vcat: - ncol = 4 - else: - ncol = 3 + ncol = 4 if vcat else 3 for col in range(num_cols - ncol): start, end = output_time_list[count][col].get_temporal_extent_as_tuple() time_string = "%(start)s%(sep)s%(end)s" % ( @@ -567,10 +542,7 @@ def one_point_per_timerow_output( if write_header: if first is True: - if vcat: - header = "cat{sep}".format(sep=separator) - else: - header = "" + header = "cat{sep}".format(sep=separator) if vcat else "" if site_input: header += "x%(sep)sy%(sep)ssite" % ({"sep": separator}) else: diff --git a/temporal/t.rename/t.rename.py b/temporal/t.rename/t.rename.py index a6aee79b790..a1f1a164fee 100755 --- a/temporal/t.rename/t.rename.py +++ b/temporal/t.rename/t.rename.py @@ -59,15 +59,8 @@ def main(): # Get the current mapset to create the id of the space time dataset mapset = gs.gisenv()["MAPSET"] - if input.find("@") >= 0: - old_id = input - else: - old_id = input + "@" + mapset - - if output.find("@") >= 0: - new_id = output - else: - new_id = output + "@" + mapset + old_id = input if input.find("@") >= 0 else input + "@" + mapset + new_id = output if output.find("@") >= 0 else output + "@" + mapset # Do not overwrite yourself if new_id == old_id: diff --git a/temporal/t.topology/t.topology.py b/temporal/t.topology/t.topology.py index 4356596f319..e9da9b369a7 100755 --- a/temporal/t.topology/t.topology.py +++ b/temporal/t.topology/t.topology.py @@ -74,10 +74,7 @@ def main(): spatial = None if spatio_temporal_relations: - if sp.get_type() == "strds": - spatial = "2D" - else: - spatial = "3D" + spatial = "2D" if sp.get_type() == "strds" else "3D" if temporal_relations or spatio_temporal_relations: sp.print_spatio_temporal_relationships(maps=maps, spatial=spatial) diff --git a/temporal/t.topology/test.t.topology.reltime.sh b/temporal/t.topology/test.t.topology.reltime.sh index 847289c4b2f..60e49355e9a 100755 --- a/temporal/t.topology/test.t.topology.reltime.sh +++ b/temporal/t.topology/test.t.topology.reltime.sh @@ -96,6 +96,5 @@ cat "${n5}" t.topology input=precip_rel_y t.topology -m input=precip_rel_y -t.remove type=strds input=precip_rel_d -t.remove type=strds input=precip_rel_y +t.remove type=strds input=precip_rel_d,precip_rel_y t.unregister type=raster file="${n1}" diff --git a/temporal/t.unregister/t.unregister.py b/temporal/t.unregister/t.unregister.py index b39b7b9819f..cacfe579a7b 100755 --- a/temporal/t.unregister/t.unregister.py +++ b/temporal/t.unregister/t.unregister.py @@ -93,12 +93,7 @@ def main(): # Map names as comma separated string if maps is not None and maps != "": - if maps.find(",") == -1: - maplist = [ - maps, - ] - else: - maplist = maps.split(",") + maplist = [maps] if maps.find(",") == -1 else maps.split(",") # Build the maplist for count in range(len(maplist)): diff --git a/temporal/t.vect.extract/test.t.vect.extract.sh b/temporal/t.vect.extract/test.t.vect.extract.sh index b5cd6882e94..8b176dcc8ed 100755 --- a/temporal/t.vect.extract/test.t.vect.extract.sh +++ b/temporal/t.vect.extract/test.t.vect.extract.sh @@ -44,5 +44,4 @@ t.vect.list input=soil_abs3 columns=name,start_time,end_time,primitives # @postprocess t.unregister type=vector maps=soil_1,soil_2,soil_3,soil_4,soil_5,soil_6,soil_7,soil_8 t.remove type=stvds input=soil_abs1,soil_abs2,soil_abs3 -g.remove -f type=vector name=soil_1,soil_2,soil_3,soil_4,soil_5,soil_6,soil_7,soil_8 -g.remove -f type=vector name=new_vect_1,new_vect_2,new_vect_3,new_vect_4,new_vect_5,new_vect_6 +g.remove -f type=vector name=soil_1,soil_2,soil_3,soil_4,soil_5,soil_6,soil_7,soil_8,new_vect_1,new_vect_2,new_vect_3,new_vect_4,new_vect_5,new_vect_6 diff --git a/temporal/t.vect.observe.strds/t.vect.observe.strds.py b/temporal/t.vect.observe.strds/t.vect.observe.strds.py index 9c60a3d026c..547f60a3250 100755 --- a/temporal/t.vect.observe.strds/t.vect.observe.strds.py +++ b/temporal/t.vect.observe.strds/t.vect.observe.strds.py @@ -202,11 +202,8 @@ def main(): vector_db = gs.vector.vector_db(input) # We copy the vector table and create the new layers - if vector_db: - # Use the first layer to copy the categories from - layers = "1," - else: - layers = "" + # If vector_db, use the first layer to copy the categories from + layers = "1," if vector_db else "" first = True for layer in range(num_samples): layer += 1 diff --git a/temporal/t.vect.what.strds/t.vect.what.strds.py b/temporal/t.vect.what.strds/t.vect.what.strds.py index 6ea36e79579..b1f2c17a9c6 100755 --- a/temporal/t.vect.what.strds/t.vect.what.strds.py +++ b/temporal/t.vect.what.strds/t.vect.what.strds.py @@ -158,12 +158,9 @@ def main(): raster_maps = (new_map.get_id(),) for rastermap in raster_maps: - if column: - col_name = column - else: - # Create a new column with the SQL compliant - # name of the sampled raster map - col_name = rastermap.split("@")[0].replace(".", "_") + # Create a new column with the SQL compliant + # name of the sampled raster map if not column + col_name = column or rastermap.split("@")[0].replace(".", "_") coltype = "DOUBLE PRECISION" # Get raster type diff --git a/testsuite/Makefile b/testsuite/Makefile deleted file mode 100644 index 7ed92b67442..00000000000 --- a/testsuite/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -MODULE_TOPDIR = .. - -SUBDIRS = \ - raster - -include $(MODULE_TOPDIR)/include/Make/Dir.make - -default: parsubdirs diff --git a/testsuite/README.md b/testsuite/README.md index 491fa4597af..36dda7d3da2 100644 --- a/testsuite/README.md +++ b/testsuite/README.md @@ -1,29 +1,39 @@ # Test suite -This directory contains scripts to check some functionality of GRASS GIS. +Tests are in directories `tests` and `testsuite` under each directory which has tests. +This directory contains additional scripts and information to test functionality +without a focus on a specific part of the code. -GRASS GIS testsuite documentation: +There are two testing mechanism in place, _pytest_ which is the modern way of testing +GRASS GIS. Tests using _pytest_ are written just as any other Python tests. -## Simple test data +In parallel, there is also custom unittest-based framework centered around +_grass.gunittest_ package. These tests run in the NC sample datasets and can be +executed using _pytest_ or directly. The unittest-based adds a number of custom +assert methods to accommodate different data and outputs typical in GRASS GIS. +_grass.gunittest_ documentation: + -Some tests may be launched in the location `../demolocation/`: +## Running tests + +Tests can be executed using _pytest_: ```bash -# create new mapset for test -grass ../demolocation/user1 -c -# run the test -make +# Setup the Python environment (if not set up already). +# Replace grass by path to the executable if not installed on path. +export PYTHONPATH=\$(grass --config python_path):\$PYTHONPATH +export LD_LIBRARY_PATH=\$(grass --config path)/lib:\$LD_LIBRARY_PATH +# Run the test. +pytest ``` -## Extended test data - -Most tests require the North Carolina Sample dataset, available from - +## Test data -## Notes +To test manually or to write tests, you may need to use the North Carolina +Sample dataset, available from +. -Since 2020: For a more advanced test suite, see - +## CI -Until 2019: For a more advanced test suite, see - +Most tests run in the CI. See the `.github` directory for details and +use it as a reference. diff --git a/testsuite/examples/test_framework_GRASS_GIS_with_NC.conf b/testsuite/examples/test_framework_GRASS_GIS_with_NC.conf deleted file mode 100644 index 6256108f6a8..00000000000 --- a/testsuite/examples/test_framework_GRASS_GIS_with_NC.conf +++ /dev/null @@ -1,29 +0,0 @@ -### CONFIGURATION -# -# name of binary: -GRASSBIN=grass -# source code directory as full path: -GRASSSRC="$(realpath ../../)" -# temporary grassdata directory -GRASSDATA="$HOME/grassdata" - -# leave 1 or more CPU free for other usage than testing -FREECPU=1 - -# Python binary to be used (python|python3) -PYTHON=python - -# here we suppose default compilation settings of GRASS GIS and no 'make install' -# may be no|yes -COMPILE="no" -# configure metascript with compiler flags: -CONFIGURE="${GRASSSRC}/conf_grass8.sh" - -# directory to store reports, e.g. in a subdirectory -REPORTS="testreports" - -# publish report on WWW Server (not needed for local tests) -# may be no|yes -PUBLISH="no" -# upload WWW dir on server for report publication (not used for local tests) -SERVERDIR="/var/www/html/grassgistestreports" diff --git a/testsuite/examples/test_framework_GRASS_GIS_with_NC.sh b/testsuite/examples/test_framework_GRASS_GIS_with_NC.sh deleted file mode 100755 index 808b4b9c716..00000000000 --- a/testsuite/examples/test_framework_GRASS_GIS_with_NC.sh +++ /dev/null @@ -1,144 +0,0 @@ -#!/bin/bash -############################################################################ -# -# MODULE: Example script to run testsuite -# AUTHOR(S): Markus Neteler, Sören Gebbert, Vaclav Petras -# PURPOSE: Test GRASS GIS using the test framework -# Documentation: -# https://trac.osgeo.org/grass/wiki/GSoC/2014/TestingFrameworkForGRASS -# https://grass.osgeo.org/grass-devel/manuals/libpython/gunittest_running_tests.html#example-bash-script-to-run-be-used-as-a-cron-job -# -# Data: -# We use the full NC dataset (nc_spm_full_v2_alpha.tar.gz) -# -# COPYRIGHT: (C) 2019-2021 by Markus Neteler, and the GRASS Development Team -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -############################################################################ - -### Fetch CONFIGURATION - -CONF="test_framework_GRASS_GIS_with_NC.conf" - -usage_msg(){ -echo "Usage: - $0 [conf_file] - -Example: - $0 ./${CONF} -" -} - -if [ ! -z "$1" ] ; then - case "$1" in - -h | --h | -help | --help) - usage_msg - exit 0 - ;; - *) - if [ -f ${1} ] ; then - CONF="$1" - else - echo "ERROR: $1 is not a file" - exit 1 - fi - ;; - esac -else - usage_msg - exit 0 -fi - -source ${CONF} - -######### nothing to change below - -set -e # fail fast - -# computer architecture: -ARCH=`${GRASSBIN} --config arch` - -# here we suppose default compilation settings of GRASS GIS and no make install -GRASSBIN="$GRASSSRC/bin.${ARCH}/${GRASSBIN}" -GRASSDIST="$GRASSSRC/dist.${ARCH}" - -# necessary hardcoded GRASS paths -GRASSDIST_PYTHON="$GRASSDIST/etc/python" -GRASS_MULTI_RUNNER="$GRASSSRC/python/grass/gunittest/multirunner.py" -GRASS_MULTI_REPORTER="$GRASSSRC/python/grass/gunittest/multireport.py" - -DATE_FLAGS="--utc +%Y-%m-%d-%H-%M" -NOW=$(date $DATE_FLAGS) - -# get number of processors of current machine -MYNPROC=`getconf _NPROCESSORS_ONLN` -# leave some free for other tasks -GCCTHREADS=`expr $MYNPROC - $FREECPU` -if [ $GCCTHREADS -lt 1 ] ; then - GCCTHREADS=1 -fi - -# contains last executed command stdout and stderr -# here were rely on reports being absolute -OUTPUT_LOGFILE="$REPORTS/output-$NOW.txt" - -# these are relative to REPORTS -CURRENT_REPORT_BASENAME="reports_for_date-" -FINAL_REPORT_DIR="summary_report" -CURRENT_REPORTS_DIR="$CURRENT_REPORT_BASENAME$NOW" -LOGFILE="$REPORTS/runs.log" - -mkdir -p $REPORTS/$CURRENT_REPORTS_DIR -mkdir -p $GRASSDATA - -# fetch sample data -SAMPLEDATA=nc_spm_full_v2alpha -(cd $GRASSDATA ; wget -c https://grass.osgeo.org/sampledata/north_carolina/$SAMPLEDATA.tar.gz ; tar xfz $SAMPLEDATA.tar.gz --strip-components 2) - -set -x - -echo "Testing of GRASS GIS started: $NOW" >> ${LOGFILE} - -if [ "$COMPILE" = "yes" ] ; then - ## compile current source code from scratch - cd $GRASSSRC - make distclean -j$GCCTHREADS - git pull - ./$CONFIGURE ... # configure meta script containing all the compiler flags - make -j$GCCTHREADS -fi - -# run tests for the current source code -cd $REPORTS/$CURRENT_REPORTS_DIR -$PYTHON $GRASS_MULTI_RUNNER \ - --grassbin $GRASSBIN \ - --grasssrc $GRASSSRC \ - --grassdata $GRASSDATA \ - --location $SAMPLEDATA --location-type nc # \ -# --location other_location --location-type other_type - -# create overall report of all so far executed tests -# the script depends on GRASS but just Python part is enough -export PYTHONPATH="$GRASSDIST_PYTHON:$PYTHONPATH" -$PYTHON $GRASS_MULTI_REPORTER --output $FINAL_REPORT_DIR \ - $CURRENT_REPORT_BASENAME*/* - -# publish on Web site -if [ "$PUBLISH" = "yes" ] ; then - ## although we cannot be sure the tests were executed was successfully - ## so publish or archive results - rsync -rtvu --delete $REPORTS/ $SERVERDIR -fi - -echo "Nightly ($NOW) GRASS GIS test finished: $(date $DATE_FLAGS)" >> ${LOGFILE} - -exit 0 diff --git a/testsuite/raster/Makefile b/testsuite/raster/Makefile deleted file mode 100644 index 21c1f8eb6c0..00000000000 --- a/testsuite/raster/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -all: - ./raster_md5test.sh - ./rmapcalc_test.sh diff --git a/testsuite/raster/README b/testsuite/raster/README deleted file mode 100644 index bc5b0940789..00000000000 --- a/testsuite/raster/README +++ /dev/null @@ -1 +0,0 @@ -Raster map tests go here diff --git a/testsuite/raster/rhemisphere.sh b/testsuite/raster/rhemisphere.sh deleted file mode 100755 index e312f7b36d1..00000000000 --- a/testsuite/raster/rhemisphere.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/sh - -# Markus Neteler, 2006 -# This program is free software under the GNU General Public -# License (>=v2). Read the file COPYING that comes with GRASS -# for details. -# Test cases for 2D raster data -# generate a hemisphere to test slope, aspect, curvatures - -# some definitions: -BOXLENGTH=1000 # side length of test area -RADIUS=500 # half BOXLENGTH - -############ - -if [ -z "$GISBASE" ] ; then - echo "You must be in GRASS GIS to run this program." >&2 - exit 1 -fi - -# some functions - keep order here -TMP="disk.$$" - -cleanup() -{ - echo "Removing temporary map" - g.remove --q -f type=raster name=$TMP > /dev/null -} - -######################## - -g.region n=$BOXLENGTH s=0 w=0 e=$BOXLENGTH -p res=1 - -X="(col() - $RADIUS)" -Y="($RADIUS - row())" -r="sqrt($X^2 + $Y^2)" - -#Mask out unwanted parts (check for <= ??): -r.mapcalc "$TMP = if($r<$RADIUS,$r,null())" - -ALPHA="acos ($TMP/$RADIUS)" -HEIGHT="$RADIUS * sin($ALPHA)" - - -r.mapcalc "hemisphere = $HEIGHT" -cleanup -g.message "Generated raster map " -#echo "Now generate aspect + slope on " diff --git a/testsuite/raster/raster_md5test.sh b/testsuite/raster_md5test.sh similarity index 67% rename from testsuite/raster/raster_md5test.sh rename to testsuite/raster_md5test.sh index 0bda0c3428c..b3281d4c505 100755 --- a/testsuite/raster/raster_md5test.sh +++ b/testsuite/raster_md5test.sh @@ -14,19 +14,23 @@ if [ -z "$GISBASE" ] ; then fi #### check if we have sed -if [ ! -x "`which sed`" ] ; then +if [ ! -x "$(which sed)" ] ; then echo "$PROG: sed required, please install first" 1>&2 exit 1 fi -#### check if we have md5sum -if [ ! -x "`which md5sum`" ] ; then - echo "$PROG: md5sum required, please install first" 1>&2 - exit 1 +#### check if we have md5sum or md5 +if [ -x "$(which md5sum)" ] ; then + MD5="md5sum | cut -d' ' -f1" +elif [ -x "$(which md5)" ] ; then + MD5="md5 -q" +else + echo "$PROG: md5sum or md5 required, please install first" 1>&2 + exit 1 fi #### check if we have cut -if [ ! -x "`which cut`" ] ; then +if [ ! -x "$(which cut)" ] ; then echo "$PROG: cut required, please install first" 1>&2 exit 1 fi @@ -38,47 +42,47 @@ export LC_NUMERIC=C # enforce ZLIB export GRASS_COMPRESSOR=ZLIB -eval `g.gisenv` -: ${GISBASE?} ${GISDBASE?} ${LOCATION_NAME?} ${MAPSET?} +eval "$(g.gisenv)" +: "${GISBASE?}" "${GISDBASE?}" "${LOCATION_NAME?}" "${MAPSET?}" MAPSET_PATH=$GISDBASE/$LOCATION_NAME/$MAPSET # some definitions PIXEL=3 PID=$$ -TMPNAME="`echo ${PID}_tmp_testmap | sed 's+\.+_+g'`" +TMPNAME=$(echo ${PID}_tmp_testmap | sed 's+\.+_+g') # some functions - keep order here cleanup() { echo "Removing temporary map" - g.remove -f type=raster name=$TMPNAME > /dev/null + g.remove -f type=raster name="$TMPNAME" > /dev/null } # check if a MASK is already present: -MASKTMP=mask.$TMPNAME -USERMASK=usermask_${MASKTMP} -if test -f $MAPSET_PATH/cell/MASK +MASKTMP="mask.${TMPNAME}" +USERMASK="usermask_${MASKTMP}" +if test -f "${MAPSET_PATH}/cell/MASK" then echo "A user raster mask (MASK) is present. Saving it..." - g.rename raster=MASK,$USERMASK > /dev/null + g.rename raster=MASK,"$USERMASK" > /dev/null fi finalcleanup() { echo "Restoring user region" - g.region region=$TMPNAME - g.remove -f type=region name=$TMPNAME > /dev/null + g.region region="$TMPNAME" + g.remove -f type=region name="$TMPNAME" > /dev/null #restore user mask if present: - if test -f $MAPSET_PATH/cell/$USERMASK ; then + if test -f "${MAPSET_PATH}/cell/${USERMASK}" ; then echo "Restoring user MASK" g.remove -f type=raster name=MASK > /dev/null - g.rename raster=$USERMASK,MASK > /dev/null + g.rename raster="$USERMASK",MASK > /dev/null fi } check_exit_status() { - if [ $1 -ne 0 ] ; then + if [ "$1" -ne 0 ] ; then echo "An error occurred." cleanup ; finalcleanup exit 1 @@ -106,7 +110,7 @@ check_md5sum() } echo "Saving current & setting test region." -g.region save=$TMPNAME +g.region save="$TMPNAME" check_exit_status $? g.region s=0 n=$PIXEL w=0 e=$PIXEL res=1 tbres=1 check_exit_status $? @@ -118,8 +122,8 @@ r.mapcalc "$TMPNAME = 1" check_exit_status $? echo "MD5 checksum on output of INT/CELL test." -MD5="`r.out.ascii $TMPNAME precision=15 | md5sum | cut -d' ' -f1`" -check_md5sum "549e7dabe70df893803690571d2e1503" "$MD5" +SUM=$(r.out.ascii "$TMPNAME" precision=15 | eval "$MD5") +check_md5sum "549e7dabe70df893803690571d2e1503" "$SUM" cleanup echo "INT/CELL md5sum test successful" @@ -132,8 +136,8 @@ r.mapcalc "$TMPNAME = $VALUE" check_exit_status $? echo "MD5 checksum on output of FLOAT/FCELL test." -MD5="`r.out.ascii $TMPNAME precision=15 | md5sum | cut -d' ' -f1`" -check_md5sum "379f3d880b6d509051af6b4ccf470762" "$MD5" +SUM=$(r.out.ascii "$TMPNAME" precision=15 | eval "$MD5") +check_md5sum "379f3d880b6d509051af6b4ccf470762" "$SUM" cleanup echo "FLOAT/FCELL md5sum test successful" diff --git a/testsuite/vector/v.in.gps_test.sh b/testsuite/vector/v.in.gps_test.sh deleted file mode 100755 index 62b2f5b651c..00000000000 --- a/testsuite/vector/v.in.gps_test.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -# v.in.garmin and v.in.gpsbabel test script -# by Hamish Bowman 18 May 2007 ; public domain -# assumes downloading from a garmin on port /dev/gps - -for PGM in garmin gpsbabel ; do - for DO_PTS in pts lines ; do - for TASK in w t r ; do - if [ $TASK = w ] && [ $DO_PTS = "lines" ] ; then - continue - fi - if [ $DO_PTS = "pts" ] ; then - PTFLAG="-p" - else - PTFLAG="" - fi - MAPNAME="test_${PGM}_${TASK}_${DO_PTS}_$$" - echo "-- Running [v.in.$PGM] for [$TASK] download as [$DO_PTS] into <$MAPNAME> --" - v.in.$PGM -$TASK $PTFLAG out="$MAPNAME" - if [ $? -ne 0 ] ; then - exit 1 - fi - awk 'BEGIN {printf("\n\n\n\n")}' - done - done -done diff --git a/utils/coverage_mapper.py b/utils/coverage_mapper.py index 9a2f1389781..88fa30f9b54 100644 --- a/utils/coverage_mapper.py +++ b/utils/coverage_mapper.py @@ -22,12 +22,14 @@ def map_scripts_paths(old_path): if INITIAL_GISBASE is None or INITIAL_PWD is None: return old_path p = Path(old_path) + extension = ".py" + p_name = p.stem if p.suffix == extension else p.name temporal_base = Path(INITIAL_GISBASE) / "scripts" / "t.*" base = Path(INITIAL_GISBASE) / "scripts" / "*" if p.match(str(temporal_base)): - return str(Path(INITIAL_PWD) / "temporal" / (p.name) / (p.name)) + ".py" + return str(Path(INITIAL_PWD) / "temporal" / (p_name) / (p_name)) + extension if p.match(str(base)): - return str(Path(INITIAL_PWD) / "scripts" / (p.name) / (p.name)) + ".py" + return str(Path(INITIAL_PWD) / "scripts" / (p_name) / (p_name)) + extension return old_path diff --git a/utils/g.html2man/ggroff.py b/utils/g.html2man/ggroff.py index e62c5ec2b1c..a0458dd0334 100644 --- a/utils/g.html2man/ggroff.py +++ b/utils/g.html2man/ggroff.py @@ -119,10 +119,7 @@ def fmt(self, format, content, var=None): self.show(pre) if sep != "": if var: - if var == "index": - val = self.get("index") + [0] - else: - val = True + val = self.get("index") + [0] if var == "index" else True self.pp_with(content, var, val) else: self.pp(content) diff --git a/utils/mkrest.py b/utils/mkrest.py index 24481bc3374..8d156e0bcc7 100755 --- a/utils/mkrest.py +++ b/utils/mkrest.py @@ -21,10 +21,7 @@ from datetime import datetime pgm = sys.argv[1] -if len(sys.argv) > 1: - year = sys.argv[2] -else: - year = str(datetime.now().year) +year = sys.argv[2] if len(sys.argv) > 1 else str(datetime.now().year) src_file = "%s.html" % pgm tmp_file = "%s.tmp.txt" % pgm diff --git a/utils/ppmrotate.py b/utils/ppmrotate.py index ec041e0f253..cef32eba0f4 100755 --- a/utils/ppmrotate.py +++ b/utils/ppmrotate.py @@ -110,11 +110,8 @@ def convert_and_rotate(src, dst, flip=False): to_png = False if dst.lower().endswith(".png"): to_png = True - if to_png: - tmp_img = gs.tempfile() + ".ppm" - # TODO: clean up the file - else: - tmp_img = dst + # TODO: clean up the file + tmp_img = gs.tempfile() + ".ppm" if to_png else dst write_ppm(tmp_img, ppm) if to_png: ppmtopng(dst, tmp_img) diff --git a/utils/thumbnails.py b/utils/thumbnails.py index ee9bd5e17ab..181a16aa666 100755 --- a/utils/thumbnails.py +++ b/utils/thumbnails.py @@ -23,13 +23,14 @@ def cleanup(): + names = [] if tmp_grad_rel: - gs.run_command( - "g.remove", flags="f", type="raster", name=tmp_grad_rel, quiet=True - ) + names.append(tmp_grad_rel) if tmp_grad_abs: + names.append(tmp_grad_abs) + if len(names) > 0: gs.run_command( - "g.remove", flags="f", type="raster", name=tmp_grad_abs, quiet=True + "g.remove", flags="f", type="raster", name=",".join(names), quiet=True ) diff --git a/utils/update_version.py b/utils/update_version.py index c66a34a62cd..947e68a1648 100755 --- a/utils/update_version.py +++ b/utils/update_version.py @@ -226,10 +226,7 @@ def status(args): version_info = read_version_file() today = datetime.date.today().isoformat() version = construct_version(version_info) - if not version_info.micro.endswith("dev"): - tag = version - else: - tag = None + tag = version if not version_info.micro.endswith("dev") else None if args.bash: status_as_bash(version_info=version_info, today=today, version=version, tag=tag) else: diff --git a/vector/v.colors/read_rgb.c b/vector/v.colors/read_rgb.c index 99aa054138d..c6e762ca0af 100644 --- a/vector/v.colors/read_rgb.c +++ b/vector/v.colors/read_rgb.c @@ -64,4 +64,5 @@ void rgb2colr(struct Map_info *Map, int layer, const char *rgb_column, G_warning(_("%d invalid RGB color values skipped"), nskipped); db_close_database_shutdown_driver(driver); + Vect_destroy_field_info(fi); } diff --git a/vector/v.extract/testsuite/test_v_extract.py b/vector/v.extract/testsuite/test_v_extract.py index d266e8ad649..958f153d1a3 100644 --- a/vector/v.extract/testsuite/test_v_extract.py +++ b/vector/v.extract/testsuite/test_v_extract.py @@ -5,8 +5,8 @@ Author: Sunveer Singh, Google Code-in 2017 Copyright: (C) 2017 by Sunveer Singh and the GRASS Development Team Licence: This program is free software under the GNU General Public - License (>=v2). Read the file COPYING that comes with GRASS - for details. + License (>=v2). Read the file COPYING that comes with GRASS + for details. """ import os @@ -54,8 +54,8 @@ def setUpClass(cls): def tearDownClass(cls): cls.del_temp_region() - def tearDown(cls): - cls.runModule("g.remove", flags="f", type="vector", name=cls.output) + def tearDown(self): + self.runModule("g.remove", flags="f", type="vector", name=self.output) def test_flagd(self): """Testing flag d""" diff --git a/vector/v.hull/chull.c b/vector/v.hull/chull.c index 93dfd15742e..cd8dcf52153 100644 --- a/vector/v.hull/chull.c +++ b/vector/v.hull/chull.c @@ -687,6 +687,8 @@ void CleanEdges(void) e = edges; DELETE(edges, e); } + if (!edges) + return; e = edges->next; do { if (e->delete) { @@ -711,6 +713,8 @@ void CleanFaces(void) f = faces; DELETE(faces, f); } + if (!faces) + return; f = faces->next; do { if (f->visible) { @@ -746,6 +750,8 @@ void CleanVertices(void) v = vertices; DELETE(vertices, v); } + if (!vertices) + return; v = vertices->next; do { if (v->mark && !v->onhull) { diff --git a/vector/v.kernel/main.c b/vector/v.kernel/main.c index 96198a4d8f7..c1e7f77a36a 100644 --- a/vector/v.kernel/main.c +++ b/vector/v.kernel/main.c @@ -769,7 +769,9 @@ double compute_all_net_distances(struct Map_info *In, struct Map_info *Net, G_debug(3, " kk = %d", kk); } } - + Vect_destroy_line_struct(APoints); + Vect_destroy_line_struct(BPoints); + Vect_destroy_boxlist(List); return (kk); } diff --git a/vector/v.net.timetable/main.c b/vector/v.net.timetable/main.c index 5fb382009bb..424d8a851bd 100644 --- a/vector/v.net.timetable/main.c +++ b/vector/v.net.timetable/main.c @@ -176,9 +176,7 @@ void write_subroute(struct segment *seg, struct line_pnts *line, int line_id) struct line_cats *Cats; struct ilist *list; - Points = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); - list = Vect_new_list(); r = seg->route; Vect_cat_set(Cats, 2, line_id); @@ -188,6 +186,9 @@ void write_subroute(struct segment *seg, struct line_pnts *line, int line_id) return; } + Points = Vect_new_line_struct(); + list = Vect_new_list(); + for (i = 0; i < nnodes; i++) edges[i] = 0; for (i = 0; i < lines[r]->n_values; i++) diff --git a/vector/v.net/testsuite/test_v_net.py b/vector/v.net/testsuite/test_v_net.py index 469ac127f47..50bb047628f 100644 --- a/vector/v.net/testsuite/test_v_net.py +++ b/vector/v.net/testsuite/test_v_net.py @@ -6,10 +6,10 @@ class TestVNet(TestCase): network = "test_vnet" - def tearDown(cls): + def tearDown(self): """Remove viewshed map after each test method""" # TODO: eventually, removing maps should be handled through testing framework functions - cls.runModule("g.remove", flags="f", type="vector", name=cls.network) + self.runModule("g.remove", flags="f", type="vector", name=self.network) def test_nodes(self): """Test""" diff --git a/vector/v.out.svg/main.c b/vector/v.out.svg/main.c index 4c99d148d04..4d29f845b29 100644 --- a/vector/v.out.svg/main.c +++ b/vector/v.out.svg/main.c @@ -25,7 +25,7 @@ #define SVG_NS "http://www.w3.org/2000/svg" #define XLINK_NS "http://www.w3.org/1999/xlink" -#define GRASS_NS "http:/grass.itc.it/2006/gg" +#define GRASS_NS "http://grass.itc.it/2006/gg" #define RADIUS_SCALE .003 #define WIDTH_SCALE .001 #define G_Areas "G_Areas" diff --git a/vector/v.overlay/v.overlay.html b/vector/v.overlay/v.overlay.html index 2b2a35be8c8..0077f2915f5 100644 --- a/vector/v.overlay/v.overlay.html +++ b/vector/v.overlay/v.overlay.html @@ -97,7 +97,7 @@

AND operator

v.overlay with AND operator
-Figure: v.overlay with AND operator (selected polygons in grey color) +Figure: v.overlay with AND operator (selected polygons in yellow color)

OR operator

@@ -113,7 +113,7 @@

OR operator

v.overlay with OR operator
-Figure: v.overlay with OR operator (selected polygons in grey color) +Figure: v.overlay with OR operator (selected polygons in yellow color)

XOR operator

@@ -129,7 +129,7 @@

XOR operator

v.overlay with XOR operator
-Figure: v.overlay with XOR operator (selected polygons in grey color) +Figure: v.overlay with XOR operator (selected polygons in yellow color)

NOT operator

@@ -145,7 +145,7 @@

NOT operator

v.overlay with NOT operator
-Figure: v.overlay with NOT operator (selected polygon in grey color) +Figure: v.overlay with NOT operator (selected polygon in yellow color)

Overlay operations: AND, OR, NOT, XOR

diff --git a/vector/v.overlay/v_overlay_op_and.png b/vector/v.overlay/v_overlay_op_and.png index 3d4bb3a07ff..c545f2140ad 100644 Binary files a/vector/v.overlay/v_overlay_op_and.png and b/vector/v.overlay/v_overlay_op_and.png differ diff --git a/vector/v.overlay/v_overlay_op_not.png b/vector/v.overlay/v_overlay_op_not.png index ac7eb441bbc..2c903f22d60 100644 Binary files a/vector/v.overlay/v_overlay_op_not.png and b/vector/v.overlay/v_overlay_op_not.png differ diff --git a/vector/v.overlay/v_overlay_op_or.png b/vector/v.overlay/v_overlay_op_or.png index c19bd32ce9f..bbde7b06eb1 100644 Binary files a/vector/v.overlay/v_overlay_op_or.png and b/vector/v.overlay/v_overlay_op_or.png differ diff --git a/vector/v.overlay/v_overlay_op_xor.png b/vector/v.overlay/v_overlay_op_xor.png index 446624f36ec..bf5bd5b8e45 100644 Binary files a/vector/v.overlay/v_overlay_op_xor.png and b/vector/v.overlay/v_overlay_op_xor.png differ diff --git a/vector/v.random/testsuite/test_v_random.py b/vector/v.random/testsuite/test_v_random.py index e5cc95a93db..7b68434f46b 100644 --- a/vector/v.random/testsuite/test_v_random.py +++ b/vector/v.random/testsuite/test_v_random.py @@ -30,9 +30,10 @@ def setUpClass(cls): def tearDownClass(cls): cls.del_temp_region() - def tearDown(cls): - cls.runModule("g.remove", type="vector", flags="f", name=cls.output) - cls.runModule("g.remove", type="vector", flags="f", name=cls.output2) + def tearDown(self): + self.runModule( + "g.remove", type="vector", flags="f", name=(self.output, self.output2) + ) def test_num_points(self): """Checking if number of points equals 100""" diff --git a/vector/v.select/testsuite/test_v_select.py b/vector/v.select/testsuite/test_v_select.py index b8564505a6a..c72aac8c4cc 100644 --- a/vector/v.select/testsuite/test_v_select.py +++ b/vector/v.select/testsuite/test_v_select.py @@ -17,8 +17,8 @@ class TestRasterReport(TestCase): ainput = "geology" output = "testvselect" - def tearDown(cls): - cls.runModule("g.remove", type="vector", flags="f", name=cls.output) + def tearDown(self): + self.runModule("g.remove", type="vector", flags="f", name=self.output) def test_opo(self): """Testing operator overlap""" diff --git a/vector/v.surf.rst/benchmark/benchmark_v_surf_rst.py b/vector/v.surf.rst/benchmark/benchmark_v_surf_rst.py index 91fd784e0b3..ac69b441010 100644 --- a/vector/v.surf.rst/benchmark/benchmark_v_surf_rst.py +++ b/vector/v.surf.rst/benchmark/benchmark_v_surf_rst.py @@ -36,8 +36,7 @@ def benchmark(size, label, results): ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=8, repeat=3)) - Module("g.remove", quiet=True, flags="f", type="raster", name=reference) - Module("g.remove", quiet=True, flags="f", type="raster", name=output) + Module("g.remove", quiet=True, flags="f", type="raster", name=(reference, output)) def generate_map(npoints, rows, cols, fname): diff --git a/vector/v.surf.rst/benchmark/benchmark_v_surf_rst_cv.py b/vector/v.surf.rst/benchmark/benchmark_v_surf_rst_cv.py index 21a3c7459eb..f140ee67b8a 100644 --- a/vector/v.surf.rst/benchmark/benchmark_v_surf_rst_cv.py +++ b/vector/v.surf.rst/benchmark/benchmark_v_surf_rst_cv.py @@ -37,8 +37,7 @@ def benchmark(size, label, results): ) results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=8, repeat=3)) - Module("g.remove", quiet=True, flags="f", type="raster", name=reference) - Module("g.remove", quiet=True, flags="f", type="raster", name=output) + Module("g.remove", quiet=True, flags="f", type="raster", name=(reference, output)) def generate_map(npoints, rows, cols, fname): diff --git a/vector/v.to.3d/testsuite/test_vto3d.py b/vector/v.to.3d/testsuite/test_vto3d.py index bfa982ddd87..2297ec3a174 100644 --- a/vector/v.to.3d/testsuite/test_vto3d.py +++ b/vector/v.to.3d/testsuite/test_vto3d.py @@ -15,10 +15,13 @@ def setUpClass(cls): def tearDownClass(cls): cls.del_temp_region() - def tearDown(cls): + def tearDown(self): """Remove contours map after each test method""" - cls.runModule( - "g.remove", flags="f", type="vector", name=[cls.contours2d, cls.contours3d] + self.runModule( + "g.remove", + flags="f", + type="vector", + name=[self.contours2d, self.contours3d], ) def test_contours(self): diff --git a/vector/v.to.db/areas.c b/vector/v.to.db/areas.c index 0977890276e..232cc4894d6 100644 --- a/vector/v.to.db/areas.c +++ b/vector/v.to.db/areas.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "global.h" @@ -111,5 +112,6 @@ int read_areas(struct Map_info *Map) G_percent(area_num, nareas, 2); } + Vect_destroy_cats_struct(Cats); return 0; } diff --git a/vector/v.type/v.type.html b/vector/v.type/v.type.html index 8e6d5806db8..124f8f39fe0 100644 --- a/vector/v.type/v.type.html +++ b/vector/v.type/v.type.html @@ -2,6 +2,27 @@

DESCRIPTION

v.type changes the type of geometry primitives. +

+The following vector object types are defined in GRASS GIS: + +

    +
  • point: a point;
  • +
  • line: a directed sequence of connected vertices with two endpoints called nodes;
  • +
  • boundary: the border line describing an area;
  • +
  • centroid: a point within a closed ring of boundaries;
  • +
  • area: the topological composition of a closed ring of boundaries and a centroid;
  • +
  • face: a 3D area;
  • +
  • kernel: a 3D centroid in a volume (not yet implemented);
  • +
  • volume: a 3D corpus, the topological composition of faces and kernel (not yet implemented).
  • +
+

+Lines and boundaries can be composed of multiple vertices. +

+Area topology also holds information about isles. These isles are located +within that area, not touching the boundaries of the outer area. Isles +are holes inside the area, and can consist of one or more areas. They are +used internally to maintain correct topology for areas. +

EXAMPLES

Convert lines to area boundaries
diff --git a/vector/v.vect.stats/testsuite/test_vect_stats.py b/vector/v.vect.stats/testsuite/test_vect_stats.py index fe982ceff33..e8a1c30c966 100644 --- a/vector/v.vect.stats/testsuite/test_vect_stats.py +++ b/vector/v.vect.stats/testsuite/test_vect_stats.py @@ -5,8 +5,8 @@ Author: Sunveer Singh, Google Code-in 2017 Copyright: (C) 2017 by Sunveer Singh and the GRASS Development Team Licence: This program is free software under the GNU General Public - License (>=v2). Read the file COPYING that comes with GRASS - for details. + License (>=v2). Read the file COPYING that comes with GRASS + for details. """ from grass.gunittest.case import TestCase